1 前提
由于近期工作的需要,要把RESTLET应用到项目中,于是在网上参考了一些资料的基础上,实践了一个关于RESTLET接口的小例子。
Restlet的思想是:HTTP客户端与HTTP服务器之间的差别,对架构来说无所谓。一个软件应可以既充当Web客户端又充当Web服务器,而无须采用两套完全不同的APIs。
Restlet提供了多个版本:Java SE、Java EE、android、Google AppEngine、Google Web Toolkit、Android。这里我们使用的是jee版本。
RESTLET的实现可以采用JAX-RS方式,也可以采用其他方式
本例子是采用JAX-RS的API开发的,这种方式提供了一种基于注解的模型来描述分布式资源,可以利用注解的功能提供资源的位置、传递等。可以在一个Resource类中同时对外提供多个rest接口服务。具体的实现步骤见如下章节。
2 实例开发
2.1 工程说明
此处可以建立一个web工程,也可以建立一个JAVASE工程,如何应用就UP TO YOU了,此处的例子只是为了梳理下RESTLET的开发流程,不涉及到web界面的应用,所以就建立了一个普通的JAVASE的工程。工程的结构图如下:
我们的工程分为如下几个部分:Server启动模块、Application模块、modle模块、REST接口实现的resource模块和客户端模块。
工程所依赖的jar包,是最基本的这个6个包,因为数据的传输使用的JSON,所以json包是必须的,如果想扩展功能,请自行添加其余的包。
2.2 Server端启动部分
RestJaxRsServer类的代码如下:
1 package com.scott.restlet;
2
3 import org.restlet.Component;
4 import org.restlet.data.Protocol;
5
6 import com.scott.restlet.application.RestJaxRsApplication;
7
8 public class RestJaxRsServer {
9
10 public static void main(String[] args) throws Exception {
11 Component component = new Component();
12 component.getServers().add(Protocol.HTTP, 8082);
13 component.getDefaultHost().attach(new RestJaxRsApplication(null));
14 component.start();
15
16 System.out.println("The restlet server started ...");
17 }
18 }
此代码中指定了我们的HTTP的绑定端口,地址就是本地默认的ip。另外,代码中有一个RestJaxRsApplication,这是每个Application能够管理一组restlet接口。
2.3 Application部分
在application包中有两个类,一个是RestJaxRsApplication类,继承了org.restlet.ext.jaxrs.JaxRsApplication,作为运行类,用于初始化REST的运行环境,其代码如下:
1 package com.scott.restlet.application;
2
3 import org.restlet.Context;
4 import org.restlet.ext.jaxrs.JaxRsApplication;
5
6
7 public class RestJaxRsApplication extends JaxRsApplication {
8
9 public RestJaxRsApplication(Context context) {
10 super(context);
11 this.add(new MyApplication());
12 }
13
14 }
另外一个是MyApplication,作为应用类,继承了javax.ws.rs.core.Application,这里面绑定了我们的RESTLET的接口RESOURCE类,可以将多个资源绑定在HashSet中,代码如下:
1 package com.scott.restlet.application;
2
3 import java.util.HashSet;
4 import java.util.Set;
5
6 import javax.ws.rs.core.Application;
7
8 import com.scott.restlet.resource.StudentResource;
9 import com.scott.restlet.resource.TeacherResource;
10
11 public class MyApplication extends Application {
12
13 @Override
14 public Set<Class<?>> getClasses() {
15 Set<Class<?>> resources = new HashSet<Class<?>>();
16
17 resources.add(StudentResource.class);
18 resources.add(TeacherResource.class);
19
20 return resources;
21 }
22
23 }
2.4 模型部分
此实例的操作模型比较简单,就是一个Student类和一个Teacher类,代码如下:
Student:
1 package com.scott.restlet.modle;
2
3 public class Student {
4
5 private int id;
6 private String name;
7 private int sex;
8 private int age;
9 private int grade;
10
11 public int getGrade() {
12 return grade;
13 }
14
15 public void setGrade(int grade) {
16 this.grade = grade;
17 }
18
19 public int getId() {
20 return id;
21 }
22
23 public void setId(int id) {
24 this.id = id;
25 }
26
27 public String getName() {
28 return name;
29 }
30
31 public void setName(String name) {
32 this.name = name;
33 }
34
35 public int getSex() {
36 return sex;
37 }
38
39 public void setSex(int sex) {
40 this.sex = sex;
41 }
42
43 public int getAge() {
44 return age;
45 }
46
47 public void setAge(int age) {
48 this.age = age;
49 }
50 }
Teacher:
1 package com.scott.restlet.modle;
2
3 public class Teacher {
4
5 private int id;
6 private String name;
7 private int sex;
8 private int age;
9 private String subject;
10
11 public int getId() {
12 return id;
13 }
14 public void setId(int id) {
15 this.id = id;
16 }
17 public String getName() {
18 return name;
19 }
20 public void setName(String name) {
21 this.name = name;
22 }
23 public int getSex() {
24 return sex;
25 }
26 public void setSex(int sex) {
27 this.sex = sex;
28 }
29 public int getAge() {
30 return age;
31 }
32 public void setAge(int age) {
33 this.age = age;
34 }
35 public String getSubject() {
36 return subject;
37 }
38 public void setSubject(String subject) {
39 this.subject = subject;
40 }
41 }
2.5 RESOURCE部分
这部分是我们的RESTLET接口实现部分,此处我们有两个接口实现类,一个是StudentResource,一个是TeacherResource,分别对外提供不同的REST接口服务。至于StorageOperator类,把它当做一个内存数据库好了,其实就是一个HashMap,StudentResource和TeacherResource的操作会在StorageOperator类中具体实现。代码如下:
StudentResource:
1 package com.scott.restlet.resource;
2
3 import javax.ws.rs.DELETE;
4 import javax.ws.rs.GET;
5 import javax.ws.rs.POST;
6 import javax.ws.rs.PUT;
7 import javax.ws.rs.Path;
8 import javax.ws.rs.PathParam;
9 import javax.ws.rs.Produces;
10
11 import org.restlet.data.Form;
12 import org.restlet.representation.Representation;
13
14 import com.scott.restlet.modle.Student;
15
16 @Path("/TestRestlet/student/")
17 public class StudentResource {
18
19 @GET
20 @Path("{id}/json")
21 @Produces("application/json")
22 public Student getStudentJson(@PathParam("id") int id) {
23 return StorageOperator.findStudent(id);
24 }
25
26 @POST
27 @Path("add")
28 public String addStudent(Representation entity) {
29
30 //get parameters from client
31 Form form = new Form(entity);
32 String name = form.getFirstValue("name");
33 int grade = Integer.parseInt(form.getFirstValue("grade"));
34 int sex = Integer.parseInt(form.getFirstValue("sex"));
35 int age = Integer.parseInt(form.getFirstValue("age"));
36
37 Student student = new Student();
38 student.setGrade(grade);
39 student.setName(name);
40 student.setSex(sex);
41 student.setAge(age);
42
43 int id = StorageOperator.studentID + 1;
44 student.setId(id);
45 return String.valueOf(StorageOperator.addStudent(student));
46 }
47
48 @PUT
49 @Path("update")
50 public String updateStudent(Representation entity) {
51 Form form = new Form(entity);
52
53 int id = Integer.parseInt(form.getFirstValue("id"));
54 Student student = StorageOperator.findStudent(id);
55
56 if (student == null) {
57 return "null";
58 }else{
59 String name = form.getFirstValue("name");
60 int grade = Integer.parseInt(form.getFirstValue("grade"));
61 int sex = Integer.parseInt(form.getFirstValue("sex"));
62 int age = Integer.parseInt(form.getFirstValue("age"));
63
64 student.setGrade(grade);
65 student.setName(name);
66 student.setSex(sex);
67 student.setAge(age);
68
69 return String.valueOf(StorageOperator.updateStudent(student));
70 }
71 }
72
73 @DELETE
74 @Path("delete/{id}")
75 public String deleteStudent(@PathParam("id") int id) {
76 int status = StorageOperator.deleteStudent(id);
77 return String.valueOf(status);
78 }
79
80 }
TeacherResource:
1 package com.scott.restlet.resource;
2
3 import javax.ws.rs.DELETE;
4 import javax.ws.rs.GET;
5 import javax.ws.rs.POST;
6 import javax.ws.rs.PUT;
7 import javax.ws.rs.Path;
8 import javax.ws.rs.PathParam;
9 import javax.ws.rs.Produces;
10
11 import org.restlet.data.Form;
12 import org.restlet.representation.Representation;
13
14 import com.scott.restlet.modle.Teacher;
15
16 @Path("/TestRestlet/teacher/")
17 public class TeacherResource {
18 @GET
19 @Path("{id}/json")
20 @Produces("application/json")
21 public Teacher getTeacherJson(@PathParam("id") int id) {
22 return StorageOperator.findTeacher(id);
23 }
24
25 @POST
26 @Path("add")
27 public String addTeacher(Representation entity) {
28
29 //get parameters from client
30 Form form = new Form(entity);
31 String name = form.getFirstValue("name");
32 String subject = form.getFirstValue("subject");
33 int sex = Integer.parseInt(form.getFirstValue("sex"));
34 int age = Integer.parseInt(form.getFirstValue("age"));
35
36 Teacher teacher = new Teacher();
37 teacher.setSubject(subject);
38 teacher.setName(name);
39 teacher.setSex(sex);
40 teacher.setAge(age);
41
42 int id = StorageOperator.teacherID + 1;
43 teacher.setId(id);
44 return String.valueOf(StorageOperator.addTeacher(teacher));
45 }
46
47 @PUT
48 @Path("update")
49 public String updateTeacher(Representation entity) {
50 Form form = new Form(entity);
51
52 int id = Integer.parseInt(form.getFirstValue("id"));
53 Teacher teacher = StorageOperator.findTeacher(id);
54
55 if (teacher == null) {
56 return "null";
57 }else{
58 String name = form.getFirstValue("name");
59 String subject = form.getFirstValue("subject");
60 int sex = Integer.parseInt(form.getFirstValue("sex"));
61 int age = Integer.parseInt(form.getFirstValue("age"));
62
63 teacher.setSubject(subject);
64 teacher.setName(name);
65 teacher.setSex(sex);
66 teacher.setAge(age);
67
68 return String.valueOf(StorageOperator.updateTeacher(teacher));
69 }
70 }
71
72 @DELETE
73 @Path("delete/{id}")
74 public String deleteTeacher(@PathParam("id") int id) {
75 int status = StorageOperator.deleteTeacher(id);
76 return String.valueOf(status);
77 }
78 }
StorageOperator:
1 package com.scott.restlet.resource;
2
3 import java.util.HashMap;
4
5 import com.scott.restlet.modle.Student;
6 import com.scott.restlet.modle.Teacher;
7
8 public class StorageOperator {
9 public static int studentID = 1;
10 public static int teacherID = 1;
11
12 public static HashMap<Integer, Student> students = new HashMap<Integer, Student>();
13 public static HashMap<Integer, Teacher> teachers = new HashMap<Integer, Teacher>();
14
15 static {
16 Student student = new Student();
17 student.setId(1);
18 student.setGrade(3);
19 student.setName("Scott");
20 student.setSex(0);
21 student.setAge(18);
22 students.put(student.getId(), student);
23
24 Teacher teacher = new Teacher();
25 teacher.setId(1);
26 teacher.setSubject("MATH");
27 teacher.setName("Toney");
28 teacher.setSex(1);
29 teacher.setAge(27);
30 teachers.put(teacher.getId(), teacher);
31 }
32
33 public static Student findStudent(int id) {
34 return students.get(id);
35 }
36
37 public static int addStudent(Student student) {
38 students.put(student.getId(), student);
39 return student.getId();
40 }
41
42 public static int updateStudent(Student student) {
43 return addStudent(student);
44 }
45
46 public static int deleteStudent(int id) {
47 if (students.get(id) != null) {
48 students.remove(id);
49 return 1;
50 }
51 return 0;
52 }
53
54 public static Teacher findTeacher(int id) {
55 return teachers.get(id);
56 }
57
58 public static int addTeacher(Teacher teacher) {
59 teachers.put(teacher.getId(), teacher);
60 return teacher.getId();
61 }
62
63 public static int updateTeacher(Teacher teacher) {
64 return addTeacher(teacher);
65 }
66
67 public static int deleteTeacher(int id) {
68 if (teachers.get(id) != null) {
69 teachers.remove(id);
70 return 1;
71 }
72 return 0;
73 }
74
75 }
在类的上面标注的PATH注解,作为全局的一个路径变量,方法上部的PATH注解都以类注解作为相对路径。并且,Produce表示返回的数据格式,此处是JSON类型。至于GET和POST,可以简单的理解为:
POST /uri 创建
DELETE /uri/xxx 删除
PUT /uri/xxx 更新或创建
GET /uri/xxx 查看
2.6 客户端部分
客户端的访问REST接口,可以通过浏览器来输入url访问,也可以通过代码来访问,通过代码来访问的代码此处封装在了Client类中,代码如下:
StudentOperateClient:
1 package com.scott.restlet.client;
2
3 import java.io.IOException;
4
5 import org.restlet.data.Form;
6 import org.restlet.resource.ClientResource;
7 import org.restlet.resource.ResourceException;
8
9 public class StudentOperateClient {
10 public static void testGetStudent(String url) {
11 ClientResource client = new ClientResource(url + "student/1/json");
12 try {
13 System.out.println("Student get " + client.get().getText());
14 } catch (ResourceException e) {
15 e.printStackTrace();
16 } catch (IOException e) {
17 e.printStackTrace();
18 }
19 }
20
21 public static void testAddStudent(String url) {
22 ClientResource client = new ClientResource(url + "student/add");
23 try {
24 Form form = new Form();
25 form.add("name", "Scott007");
26 form.add("grade", "3");
27 form.add("sex", "0");
28 form.add("age", "15");
29
30 String id = client.post(form.getWebRepresentation()).getText();
31
32 System.out.println("Student add " + id);
33 } catch (Exception e) {
34 e.printStackTrace();
35 }
36 }
37
38 public static void testUpdateStudent(String url) {
39 ClientResource client = new ClientResource(url + "student/update");
40 try {
41 Form form = new Form();
42 form.add("name", "Scott007");
43 form.add("grade", "4");
44 form.add("sex", "0");
45 form.add("id", "1");
46 form.add("age", "16");
47
48 String id = client.put(form.getWebRepresentation()).getText();
49
50 System.out.println("Student update " + id);
51 } catch (Exception e) {
52 e.printStackTrace();
53 }
54 }
55
56 public static void testDeleteStudent(String url) {
57 ClientResource client = new ClientResource(url + "student/delete/1");
58 try {
59 System.out.println("Student delete " + client.delete().getText());
60 } catch (ResourceException e) {
61 e.printStackTrace();
62 } catch (IOException e) {
63 e.printStackTrace();
64 }
65 }
66 }
TeacherOperateClient:
1 package com.scott.restlet.client;
2
3 import java.io.IOException;
4
5 import org.restlet.data.Form;
6 import org.restlet.resource.ClientResource;
7 import org.restlet.resource.ResourceException;
8
9 public class TeacherOperateClient {
10 public static void testGetTeacher(String url) {
11 ClientResource client = new ClientResource(url + "teacher/1/json");
12 try {
13 System.out.println("Teacher get " + client.get().getText());
14 } catch (ResourceException e) {
15 e.printStackTrace();
16 } catch (IOException e) {
17 e.printStackTrace();
18 }
19 }
20
21 public static void testAddTeacher(String url) {
22 ClientResource client = new ClientResource(url + "teacher/add");
23 try {
24 Form form = new Form();
25
26 form.add("name", "Scott008");
27 form.add("subject", "MATH");
28 form.add("sex", "0");
29 form.add("age", "27");
30
31 String id = client.post(form.getWebRepresentation()).getText();
32
33 System.out.println("Teacher add " + id);
34 } catch (Exception e) {
35 e.printStackTrace();
36 }
37 }
38
39 public static void testUpdateTeacher(String url) {
40 ClientResource client = new ClientResource(url + "teacher/update");
41 try {
42 Form form = new Form();
43
44 form.add("age", "28");
45 form.add("name", "Scott008");
46 form.add("subject", "English");
47 form.add("sex", "0");
48 form.add("id", "1");
49
50 String id = client.put(form.getWebRepresentation()).getText();
51 System.out.println("Teacher update " + id);
52
53 } catch (Exception e) {
54 e.printStackTrace();
55 }
56 }
57
58 public static void testDeleteTeacher(String url) {
59 ClientResource client = new ClientResource(url + "teacher/delete/1");
60 try {
61 System.out.println("Teacher delete " + client.delete().getText());
62 } catch (ResourceException e) {
63 e.printStackTrace();
64 } catch (IOException e) {
65 e.printStackTrace();
66 }
67 }
68 }
Client:
1 package com.scott.restlet.client;
2
3 public class Client {
4
5 public static final String url = "http://localhost:8082/TestRestlet/";
6
7 public static void main(String args[]){
8
9 StudentOperateClient.testGetStudent(url);
10 StudentOperateClient.testUpdateStudent(url);
11
12 TeacherOperateClient.testGetTeacher(url);
13 TeacherOperateClient.testUpdateTeacher(url);
14 }
15 }
2.7 运行
运行,得到如下结果:
服务端启动后:
2013-9-3 23:22:51 org.restlet.engine.http.connector.HttpServerHelper start
信息: Starting the internal HTTP server on port 8082
The restlet server started ...
客户端启动后的客户端信息:
2013-9-3 23:24:12 org.restlet.engine.http.connector.HttpClientHelper start
信息: Starting the default HTTP client
Student get {"id":1,"sex":0,"age":18,"name":"Scott","grade":3}
2013-9-3 23:24:12 org.restlet.engine.http.connector.HttpClientHelper start
信息: Starting the default HTTP client
Student update 1
2013-9-3 23:24:12 org.restlet.engine.http.connector.HttpClientHelper start
信息: Starting the default HTTP client
Teacher get {"id":1,"sex":1,"subject":"MATH","age":27,"name":"Toney"}
2013-9-3 23:24:12 org.restlet.engine.http.connector.HttpClientHelper start
信息: Starting the default HTTP client
Teacher update 1
客户端启动的服务端信息:
2013-9-3 23:22:51 org.restlet.engine.http.connector.HttpServerHelper start
信息: Starting the internal HTTP server on port 8082
The restlet server started ...
2013-9-3 23:24:12 org.restlet.engine.log.LogFilter afterHandle
信息: 2013-09-03 23:24:12 127.0.0.1 - - 8082 GET /TestRestlet/student/1/json - 200 - 0 70 http://localhost:8082 Restlet-Framework/2.0.6 -
2013-9-3 23:24:12 org.restlet.engine.log.LogFilter afterHandle
信息: 2013-09-03 23:24:12 127.0.0.1 - - 8082 PUT /TestRestlet/student/update - 200 1 39 4 http://localhost:8082 Restlet-Framework/2.0.6 -
2013-9-3 23:24:12 org.restlet.engine.log.LogFilter afterHandle
信息: 2013-09-03 23:24:12 127.0.0.1 - - 8082 GET /TestRestlet/teacher/1/json - 200 - 0 1 http://localhost:8082 Restlet-Framework/2.0.6 -
2013-9-3 23:24:13 org.restlet.engine.log.LogFilter afterHandle
信息: 2013-09-03 23:24:13 127.0.0.1 - - 8082 PUT /TestRestlet/teacher/update - 200 1 47 2 http://localhost:8082 Restlet-Framework/2.0.6 -
当然,此处的例子规模和功能都很小,是在参考了[2]的基础上稍加改造,算是可以作为学习RESTLET的第一步吧。