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方式,也可以采用其他方式,见:
http://www.iteye.com/topic/85928
本例子是采用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 @Override14 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 @GET20 @Path("{id}/json")21 @Produces("application/json")22 public Student getStudentJson(@PathParam("id") int id) {23 return StorageOperator.findStudent(id);24 }25 26 @POST27 @Path("add")28 public String addStudent(Representation entity) {29 30 //get parameters from client31 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 @PUT49 @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 @DELETE74 @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 @GET19 @Path("{id}/json")20 @Produces("application/json")21 public Teacher getTeacherJson(@PathParam("id") int id) {22 return StorageOperator.findTeacher(id);23 }24 25 @POST26 @Path("add")27 public String addTeacher(Representation entity) {28 29 //get parameters from client30 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 @PUT48 @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 @DELETE73 @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 8082The restlet server started ...
客户端启动后的客户端信息:
2013-9-3 23:24:12 org.restlet.engine.http.connector.HttpClientHelper start信息: Starting the default HTTP clientStudent 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 clientStudent update 12013-9-3 23:24:12 org.restlet.engine.http.connector.HttpClientHelper start信息: Starting the default HTTP clientTeacher 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 clientTeacher update 1
客户端启动的服务端信息:
2013-9-3 23:22:51 org.restlet.engine.http.connector.HttpServerHelper start信息: Starting the internal HTTP server on port 8082The 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的第一步吧。