RESTEasy
RESTEasy 是 JBoss 的一个开源项目,提供各种框架帮助你构建 RESTful Web Services 和 RESTful Java 应用程序。它是 JAX-RS 规范的一个完整实现并通过 JCP 认证。作为一个 JBOSS 的项目,它当然能和 JBOSS 应用服务器很好地集成在一起。但是,它也能在任何运行 JDK5 或以上版本的 Servlet 容器中运行。RESTEasy 还提供一个 RESTEasy JAX-RS 客户端调用框架。能够很方便与 EJB、Seam、Guice、Spring 和 Spring MVC 集成使用。支持在客户端与服务器端自动实现 GZIP 解压缩。
RESTEasy 项目是 JAX-RS 的一个实现,集成的一些亮点:
不需要配置文件,只要把 JARs 文件放到类路径里面,添加 @Path 标注就可以了。
完全的把 RESTEeasy 配置作为 Seam 组件来看待。
HTTP 请求由 Seam 来提供,不需要一个额外的 Servlet。
Resources 和 providers 可以作为 Seam components (JavaBean or EJB),具有全面的 Seam injection,lifecycle, interception, 等功能支持。
支持在客户端与服务器端自动实现 GZIP 解压缩。
JAX-RS: Java API for RESTful Web Services 是一个 Java 编程语言的应用程序接口, 支持按照 表象化状态转变 (REST) 架构风格创建 Web 服务 Web 服务 [1]. JAX-RS 使用了 Java SE 5 引入的 Java 标注来简化 Web 服务客户端和服务端的开发和部署。
JAX-RS 提供了一些标注将一个资源类,一个 POJOJava 类,封装为 Web 资源。标注包括:
@Path,标注资源类或方法的相对路径
@GET,@PUT,@POST,@DELETE,标注方法是用的 HTTP 请求的类型
@Produces,标注返回的 MIME 媒体类型
@Consumes,标注可接受请求的 MIME 媒体类型
@PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam, 分别标注方法的参数来自于 HTTP 请求的不同位置,例如 @PathParam 来自于 URL 的路径,@QueryParam 来自于 URL 的查询参数,@HeaderParam 来自于 HTTP 请求的头信息,@CookieParam 来自于 HTTP 请求的 Cookie。
配置 web.xml 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <web-app > <display-name > Archetype Created Web Application</display-name > <servlet > <servlet-name > Resteasy</servlet-name > <servlet-class > org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher </servlet-class > <init-param > <param-name > javax.ws.rs.Application</param-name > <param-value > com.restfully.shop.services.ShoppingApplication</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > Resteasy</servlet-name > <url-pattern > /*</url-pattern > </servlet-mapping > </web-app >
使用 ServletContextListener 来配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <web-app > <listener > <listener-class > org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap </listener-class > </listener > <servlet > <servlet-name > Resteasy</servlet-name > <servlet-class > org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher </servlet-class > </servlet > <servlet-mapping > <servlet-name > Resteasy</servlet-name > <url-pattern > /resteasy/*</url-pattern > </servlet-mapping > </web-app >
使用 ServletFilter 来配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <web-app > <filter > <filter-name > Resteasy</filter-name > <filter-class > org.jboss.resteasy.plugins.server.servlet.FilterDispatcher </filter-class > <init-param > <param-name > javax.ws.rs.Application</param-name > <param-value > com.restfully.shop.services.ShoppingApplication</param-value > </init-param > </filter > <filter-mapping > <filter-name > Resteasy</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > </web-app >
与 Spring 集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <web-app > <display-name > Archetype Created Web Application</display-name > <listener > <listener-class > org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class > </listener > <listener > <listener-class > org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class > </listener > <servlet > <servlet-name > Resteasy</servlet-name > <servlet-class > org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class > </servlet > <servlet-mapping > <servlet-name > Resteasy</servlet-name > <url-pattern > /*</url-pattern > </servlet-mapping > </web-app >
使用 @Path,@Get,@Post 等标注 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Path("/library") public class Library { @GET @Path("/books") public String getBooks () {...} @GET @Path("/book/{isbn}") public String getBook (@PathParam("isbn") String id) { } @PUT @Path("/book/{isbn}") public void addBook (@PathParam("isbn") String id, @QueryParam("name") String name) {...} @DELETE @Path("/book/{id}") public void removeBook (@PathParam("id") String id {...} }
以下操作都是针对 library 这个资源的
GET http://myhost.com/services/library/books 意思为获得所有的 books 调用的方法为 getBooks
GET http://myhost.com/services/library/book/333 意思为获得 ID 为 333 的 book 调用的方法为 getBook
PUT http://myhost.com/services/library/book/333 新增一个 ID 为 333 的 book 调用的方法为 addBook
DELETE http://myhost.com/services/library/book/333 删除一个 ID 为 333 的 book 调用的方法为 removeBook
@Path ** 这个标注可以在类上也可以在方法上, 如果 类和方法上都有的话, 那么方法上的路径是级联的**如上面例子的 / library/book/ @Path 和使用正则表达式匹配路径 @Path 不仅仅接收简单的路径表达式, 也可以使用正则表达式:
1 2 3 4 5 6 @Path("/resources) public class MyResource { @GET @Path("{var:.*}/stuff") public String get () {...} }
如下操作就能获得该资源: GET /resources/stuff GET /resources/foo/stuff GET /resources/on/and/on/stuff
表达式的格式为:
"{" variable-name [ ":" regular-expression] "}"
正则表达式部分是可选的, 当未提供时, 则会匹配一个默认的表达式 “([]*
)”
@Path(“/resources/{var}/stuff”)
会匹配如下路径: GET /resources/foo/stuff GET /resources/bar/stuff
下面的则不会匹配 GET /resources/a/bunch/of/stuff
@QueryParam @QueryParam 这个标注是给通过? 的方式传参获得参数值的, 如:
GET /books?num=5&index=1
1 2 3 4 @GET public String getBooks (@QueryParam("num") int num,@QueryParam("index") int index) { ... }
这里同上面的 @PathParam, 参数类型可以是任意类型
@HeaderParam 这个标注时用来获得保存在 HttpRequest 头里面的参数信息的, 如:
1 2 @PUT public void put (@HeaderParam("Content-Type") MediaType contentType, ...)
这里同上面的 @PathParam, 参数类型可以是任意类型
@CookieParam 用来获取保存在 Cookie 里面的参数, 如:
1 2 3 4 @GET public String getBooks (@CookieParam("sessionid") int id) { ... }
@FormParam 用来获取 Form 中的参数值, 如: 页面代码:
1 2 3 4 5 6 7 8 9 <form method ="POST" action ="/resources/service" > First name: <input type ="text" name ="firstname" /> <br /> Middle name: <input type ="text" name ="middlename" /> Last name: <input type ="text" name ="lastname" /> </form >
后台代码
1 2 3 4 5 6 @Path("/") public class NameRegistry { @Path("/resources/service") @POST public void addName (@FormParam("firstname") String first, @FormParam("lastname") String last) {...} }
标注了 @FormParam, 会把表达里面的值自动映射到方法的参数上去. 如果要取得 Form 里面的所有属性, 可以通过在方法上增加一个 MultivaluedMap<String, String> form 这样的对象来获得, 如下:
1 2 3 @Path("/resources/service") @POST public void addName (@FormParam("firstname") String first, @FormParam("lastname") String last,MultivaluedMap<String, String> form) {...}
@Form 上面已经说的几种标注都是一个属性对应一个参数的, 那么如果属性多了, 定义的方法就会变得不好阅读, 此时最好有个东西能够把上面的几种自动标注自动封装成一个对象,@Form 这个标注就是用来实现这个功能的, 如:
1 2 3 4 5 6 7 8 public class MyForm { @FormParam("stuff") private int stuff; @HeaderParam("myHeader") private String header; @PathParam("foo") public void setFoo (String foo) {...} }
@POST
1 2 @Path("/myservice") public void post (@Form MyForm form) {...}
@DefaultValue 在以上标注使用的时候, 有些参数值在没有值的情况下如果需要有默认值, 则使用这个标注, 如:
1 2 @GET public String getBooks (@QueryParam("num") @DefaultValue("10") int num) {...}
满足 JAX-RS 规范的 Resource Locators 和子资源 资源处理类定义的某个方法可以处理某个请求的一部分, 剩余部分由子资源处理类来处理, 如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Path("/") public class ShoppingStore { @Path("/customers/{id}") public Customer getCustomer (@PathParam("id") int id) { Customer cust = ...; return cust; } } public class Customer { @GET public String get () {...} @Path("/address") public String getAddress () {...} }
当我们发起 GET /customer/123 这样的请求的时候, 程序会先调用 ShoppingStore 的 getCustomer 这个方法, 然后接着调用 Customer 里面的 get 方法
当我们发起 GET /customer/123/address 这样的请求的时候, 程序会先调用 ShoppingStore 的 getCustomer 这个方法, 然后接着调用 Customer 里面的 getAddress 方法
JAX-RS Content Negotiation @Consumes 我们从页面提交数据到后台的时候, 数据的类型可以是 text 的, xml 的, json 的, 但是我们在请求资源的时候想要请求到同一个资源路径上面去, 此时怎么来区分处理呢? 使用 @Consumes 标注, 下面的例子将说明:
1 2 3 4 5 6 7 8 9 @Consumes("text/*") @Path("/library") public class Library { @POST public String stringBook (String book) {...} @Consumes("text/xml") @POST public String jaxbBook (Book book) {...} }
当客户端发起请求的时候, 系统会先找到所有匹配路径的方法, 然后根据 content-type 找到具体的处理方法, 比如:
1 2 POST /library content-type: text/plain
就会执行上面的 stringBook 这个方法, 因为这个方法上面没有标注 @ Consumes, 程序找了所有的方法没有找到标注 @ Consumes(“text/plain”) 这个类型的, 所以就执行这个方法了. 如果请求的 content-type=xml, 比如:
1 2 POST /library content-type: text/xml
此时就会执行 jaxbBook 这个方法
@Produces 当服务器端实行完成相关的逻辑需要返回对象的时候, 程序会根据 @Produces 返回相应的对象类型
1 2 3 4 5 6 7 8 9 @Produces("text/*") @Path("/library") public class Library { @GET @Produces("application/json") public String getJSON () {...} @GET public String get () {...} }
如果客户端发起如下请求
那么则会调用到 get 方法并且返回的格式是 json 类型的 这些标注能不能写多个呢? 答案是可以的, 但是系统只认第一个