REST(3)Restlet实现REST
REST(三)Restlet实现REST
Restlet项目为“建立REST概念与Java类之间的映射”提供了一个轻量级而全面的框架。它可用于实现任何种类的REST式系统,而不仅仅是REST式Web服务。
Restlet参考REST标准的术语,如:资源(resource)、表示(representation)、连接器(connector)、组件(component)、媒体类型(media type)、语言(language),等等。Restlet增加了一些专门的类(如Application、Filter、Finder、Router和Route),用以简化restlets的彼此结合,以及简化把收到的请求(incoming requests)映射为处理它们的资源。
其他Restlet介绍见:Restlet 入门。本节简单介绍restlet2+spring4的集成及客户端访问。
1.spring web基础环境
见上一节Jersey创建的web工程testRest。
2.REST地址
与上一节类似,设计restlet模块rest接口地址:
/restlet/teachers GET 获取所有资源
/restlet/teachers POST 创建新资源,content中包含资源内容
/restlet/teacher/{id} GET 获取编号为id的资源
/restlet/teacher/{id} PUT 更新编号为id的资源,content中包含资源内容
/restlet/teacher/{id} DELETE 删除编号为id的资源
3.restlet库
在项目pom.xml中加入Restlet依赖:
Restlet使用版本2.3.5,并加入servlet、spring、json、xml等包。因Restlet库不在中央仓库中,所以要加入https://maven.restlet.com仓库。
4.Servlet
配置Restlet访问入口。
Restlet Servlet将拦截/restlet/*下的所有访问。
Restlet与web、spring集成后,不需要再自己实现Router、Application、Component。但需要在spring中进行配置。
在配置文件目录src\main\resources\下创建restlet配置目录config\restlet,并创建配置文件restlet.xml:
此处定义的component bean将被web.xml配置的Restlet servlet引用。
5.实现api
在com.sunbin.test.restlet新增TeachersResource类,以实现/teachers路径的接口:
用到了以下Restlet自带注解:
@Get:GET请求的响应方法。
@Post("*:json"):POST请求的响应方法,指定*可接受各种Content-Type的请求数据,json可返回Content-Type:application/json数据。
需要注意的是,Restlet自带的json返回new JsonRepresentation(map)会对Map中的键值简单调用toString,再返回json串例如:{"teachers":["Teacher [id=1, age=1, name=a]","Teacher [id=2, age=2, name=b]"]},这就不是标准json了。所以干脆使用阿里的fastjson库先将结果转成JSONString,再返回:{"teachers":[{"age":1,"id":1,"name":"a"},{"age":2,"id":2,"name":"b"}]}。
新增TeacherResource类,以实现/teacher/{id}路径的接口:
同样使用的不是JAX-RS注解,而是Restlet自带注解(大小写不同)。
restlet的路径绑定需要配置在spring文件中。在restlet.xml中增加配置:
6.js测试
使用上一节的测试页面src\main\webapp\rest\index.jsp进行测试:
重新部署后,使用浏览器访问http://localhost:8080/testRest/rest,测试内容和jersey测试相同。
7.RestletClient测试
Restlet提供Client库用于测试REST接口。
新建TestRestlet类,代码如下:
测试结果与Jersey类似。
8.部署至jboss
将Restlet Web工程部署至jboss,会因为jboss类加载配置导致无法启动。
需要修改jboss目录/modules/system/layers/base/sun/jdk/main下的module.xml文件:
加入httpserver配置行。
9.Restlet访问session
Restlet不推荐在Resource中访问会话信息,因为这是违背REST的无状态设计的。
但如果需要在Resource中访问Session,可以按以下代码获取:
Restlet项目为“建立REST概念与Java类之间的映射”提供了一个轻量级而全面的框架。它可用于实现任何种类的REST式系统,而不仅仅是REST式Web服务。
Restlet参考REST标准的术语,如:资源(resource)、表示(representation)、连接器(connector)、组件(component)、媒体类型(media type)、语言(language),等等。Restlet增加了一些专门的类(如Application、Filter、Finder、Router和Route),用以简化restlets的彼此结合,以及简化把收到的请求(incoming requests)映射为处理它们的资源。
其他Restlet介绍见:Restlet 入门。本节简单介绍restlet2+spring4的集成及客户端访问。
1.spring web基础环境
见上一节Jersey创建的web工程testRest。
2.REST地址
与上一节类似,设计restlet模块rest接口地址:
/restlet/teachers GET 获取所有资源
/restlet/teachers POST 创建新资源,content中包含资源内容
/restlet/teacher/{id} GET 获取编号为id的资源
/restlet/teacher/{id} PUT 更新编号为id的资源,content中包含资源内容
/restlet/teacher/{id} DELETE 删除编号为id的资源
3.restlet库
在项目pom.xml中加入Restlet依赖:
<repositories> <repository> <id>maven-restlet</id> <name>Restlet repository</name> <url>https://maven.restlet.com</url> </repository> </repositories> <properties> <restlet.version>2.3.5</restlet.version> <fastjson.version>1.2.7</fastjson.version> </properties> <dependencies> ... <!-- restlet begin --> <dependency> <groupId>org.restlet.jee</groupId> <artifactId>org.restlet</artifactId> <version>${restlet.version}</version> </dependency> <dependency> <groupId>org.restlet.jee</groupId> <artifactId>org.restlet.ext.json</artifactId> <version>${restlet.version}</version> </dependency> <dependency> <groupId>org.restlet.jee</groupId> <artifactId>org.restlet.ext.spring</artifactId> <version>${restlet.version}</version> </dependency> <dependency> <groupId>org.restlet.jee</groupId> <artifactId>org.restlet.ext.servlet</artifactId> <version>${restlet.version}</version> </dependency> <dependency> <groupId>org.restlet.jee</groupId> <artifactId>org.restlet.ext.xml</artifactId> <version>${restlet.version}</version> </dependency> <dependency> <groupId>org.restlet.jee</groupId> <artifactId>org.restlet.ext.oauth</artifactId> <version>${restlet.version}</version> </dependency> <!-- json 相关jar包 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>${guava.version}</version> </dependency> <!-- restlet end --> ...
Restlet使用版本2.3.5,并加入servlet、spring、json、xml等包。因Restlet库不在中央仓库中,所以要加入https://maven.restlet.com仓库。
4.Servlet
配置Restlet访问入口。
<!-- restlet --> <servlet> <servlet-name>restlet</servlet-name> <servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class> <init-param> <param-name>org.restlet.component</param-name> <param-value>component</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>restlet</servlet-name> <url-pattern>/restlet/*</url-pattern> </servlet-mapping>
Restlet Servlet将拦截/restlet/*下的所有访问。
Restlet与web、spring集成后,不需要再自己实现Router、Application、Component。但需要在spring中进行配置。
在配置文件目录src\main\resources\下创建restlet配置目录config\restlet,并创建配置文件restlet.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName" default-lazy-init="true"> <bean id="component" class="org.restlet.ext.spring.SpringComponent"> <property name="defaultTarget" ref="router" /> </bean> <bean id="router" class="org.restlet.ext.spring.SpringBeanRouter"></bean> </beans>
此处定义的component bean将被web.xml配置的Restlet servlet引用。
5.实现api
在com.sunbin.test.restlet新增TeachersResource类,以实现/teachers路径的接口:
package com.sunbin.test.restlet; import java.util.HashMap; import java.util.Map; import org.restlet.data.Form; import org.restlet.ext.json.JsonRepresentation; import org.restlet.representation.Representation; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.ServerResource; import org.springframework.beans.factory.annotation.Autowired; import com.alibaba.fastjson.JSON; import com.sunbin.test.teacher.pojo.Teacher; import com.sunbin.test.teacher.service.TeacherService; public class TeachersResource extends ServerResource { @Autowired private TeacherService teacherService; @Get public Representation get() { System.out.println("Restlet TeachersResource.get"); Map map = new HashMap(); map.put("teachers", teacherService.list()); return new JsonRepresentation(JSON.toJSONString(map)); } @Post("*:json") public Representation post(Representation entity){ Map map = new HashMap(); try { Form form = new Form(entity); System.out.println("Restlet TeachersResource.post:"+form); Teacher teacher = new Teacher(); teacher.setName(form.getFirstValue("name")); teacher.setAge(Integer.parseInt(form.getFirstValue("age"))); teacherService.save(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new JsonRepresentation(JSON.toJSONString(map)); } }
用到了以下Restlet自带注解:
@Get:GET请求的响应方法。
@Post("*:json"):POST请求的响应方法,指定*可接受各种Content-Type的请求数据,json可返回Content-Type:application/json数据。
需要注意的是,Restlet自带的json返回new JsonRepresentation(map)会对Map中的键值简单调用toString,再返回json串例如:{"teachers":["Teacher [id=1, age=1, name=a]","Teacher [id=2, age=2, name=b]"]},这就不是标准json了。所以干脆使用阿里的fastjson库先将结果转成JSONString,再返回:{"teachers":[{"age":1,"id":1,"name":"a"},{"age":2,"id":2,"name":"b"}]}。
新增TeacherResource类,以实现/teacher/{id}路径的接口:
package com.sunbin.test.restlet; import java.util.HashMap; import java.util.Map; import org.restlet.data.Form; import org.restlet.ext.json.JsonRepresentation; import org.restlet.representation.Representation; import org.restlet.resource.Delete; import org.restlet.resource.Get; import org.restlet.resource.Put; import org.restlet.resource.ServerResource; import org.springframework.beans.factory.annotation.Autowired; import com.alibaba.fastjson.JSON; import com.sunbin.test.teacher.pojo.Teacher; import com.sunbin.test.teacher.service.TeacherService; public class TeacherResource extends ServerResource{ @Autowired private TeacherService teacherService; @Get public Representation get(){ String id = (String)getRequest().getAttributes().get("id"); System.out.println("Restlet TeacherResource.get:"+id); Teacher teacher = new Teacher(); teacher.setId(Integer.parseInt(id)); Map map = new HashMap(); map.put("teacher", teacherService.get(teacher)); return new JsonRepresentation(JSON.toJSONString(map)); } @Put public Representation put(Representation entity){ Map map = new HashMap(); try { String id = (String)getRequest().getAttributes().get("id"); Form form = new Form(entity); System.out.println("Restlet TeacherResource.put:"+id+":"+form); Teacher teacher = new Teacher(); teacher.setId(Integer.parseInt(id)); teacher.setName(form.getFirstValue("name")); teacher.setAge(Integer.parseInt(form.getFirstValue("age"))); teacherService.update(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new JsonRepresentation(JSON.toJSONString(map)); } @Delete public Representation delete(){ Map map = new HashMap(); try { String id = (String)getRequest().getAttributes().get("id"); System.out.println("Restlet TeacherResource.delete:"+id); Teacher teacher = new Teacher(); teacher.setId(Integer.parseInt(id)); teacherService.remove(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new JsonRepresentation(JSON.toJSONString(map)); } }
同样使用的不是JAX-RS注解,而是Restlet自带注解(大小写不同)。
restlet的路径绑定需要配置在spring文件中。在restlet.xml中增加配置:
<bean name="/teacher/{id}" id="teacherResource" class="com.sunbin.test.restlet.TeacherResource" scope="prototype"> </bean> <bean name="/teachers" id="teachersResource" class="com.sunbin.test.restlet.TeachersResource" scope="prototype"> </bean>
6.js测试
使用上一节的测试页面src\main\webapp\rest\index.jsp进行测试:
... <script type="text/javascript"> // 测试框架 //var restType = "jersey"; var restType = "restlet"; //var restType = "resteasy"; //var restType = "restCxf"; //var restType = "restMvc"; ...
重新部署后,使用浏览器访问http://localhost:8080/testRest/rest,测试内容和jersey测试相同。
7.RestletClient测试
Restlet提供Client库用于测试REST接口。
新建TestRestlet类,代码如下:
package com.sunbin.test.restlet; import java.io.IOException; import org.restlet.resource.ClientResource; import org.restlet.resource.ResourceException; public class TestRestlet { public static final String URL_BASE = "http://localhost:8080/testRest/restlet/"; /** * @param args * @throws IOException * @throws ResourceException */ public static void main(String[] args) throws Exception { // TODO Auto-generated method stub String module = "teacher"; String url = ""; ClientResource client = null; String entity = ""; String result = ""; url = URL_BASE + module + "s"; System.out.println("get\t " + url); client = new ClientResource(url); result = client.get().getText(); System.out.println(result); url = URL_BASE + module + "s"; System.out.println("post\t " + url); client = new ClientResource(url); entity = "name=a&age=1"; result = client.post(entity).getText(); System.out.println(result); url = URL_BASE + module + "s"; System.out.println("get\t " + url); client = new ClientResource(url); result = client.get().getText(); System.out.println(result); url = URL_BASE + module + "/1"; System.out.println("get\t " + url); client = new ClientResource(url); result = client.get().getText(); System.out.println(result); url = URL_BASE + module + "/1"; System.out.println("put\t " + url); client = new ClientResource(url); entity = "name=aa&age=11"; result = client.put(entity).getText(); System.out.println(result); url = URL_BASE + module + "s"; System.out.println("get\t " + url); client = new ClientResource(url); result = client.get().getText(); System.out.println(result); url = URL_BASE + module + "/1"; System.out.println("delete\t " + url); client = new ClientResource(url); result = client.delete().getText(); System.out.println(result); url = URL_BASE + module + "s"; System.out.println("get\t " + url); client = new ClientResource(url); result = client.get().getText(); System.out.println(result); } }
测试结果与Jersey类似。
8.部署至jboss
将Restlet Web工程部署至jboss,会因为jboss类加载配置导致无法启动。
需要修改jboss目录/modules/system/layers/base/sun/jdk/main下的module.xml文件:
<dependencies> <system export="true"> <paths> ... <path name="com/sun/net/httpserver"/> ...
加入httpserver配置行。
9.Restlet访问session
Restlet不推荐在Resource中访问会话信息,因为这是违背REST的无状态设计的。
但如果需要在Resource中访问Session,可以按以下代码获取:
import org.restlet.Request; import org.restlet.engine.adapter.HttpRequest; import org.restlet.engine.adapter.ServerCall; import org.restlet.ext.servlet.internal.ServletCall; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; ... Request request = getRequest(); if (request instanceof HttpRequest) { final ServerCall httpCall = ((HttpRequest) request) .getHttpCall(); if (httpCall instanceof ServletCall) { HttpServletRequest httpServletRequest = ((ServletCall) httpCall) .getRequest(); HttpSession session = httpServletRequest.getSession(); ...