REST 在 Java 中的使用

UWAIS 5年前
   <p>REST是一种混合的架构风格。本篇主要描述一下J2EE对REST的支持。</p>    <p>Java是在J2EE6中引入了对REST的支持,即JSR-311(JAX-RS 1.1: The JavaTM API for RESTful Web Services),现在JAX-RS到2.0版本了,对应的是JSR-339,JSR是Java技术规范提案,由JCP组织进行管理。该规范使得使用Java进行开发的人员使用一套固定的接口来开发 REST 应用,避免了依赖于第三方框架。JAX-RS是一套接口,具体实现由第三方提供,例如 Sun 的参考实现 Jersey、Apache 的CXF 以及 JBoss 的 RESTEasy,下文会有对它们如何发布rest服务的简单介绍。</p>    <h2>1.常用API介绍</h2>    <h2>1.1 javax.ws.rs包(用于创建RESTful服务资源的高级接口和注解)</h2>    <h3>1.1.1 统一接口</h3>    <p>JAX-RS使用@javax.ws.rs.POST 、@javax.ws.rs.GET 、@javax.ws.rs.PUT、@javax.ws.rs.DELETE四个注解代表了对资源的CRUD(Create, Retrieve, Update, Delete)操作。</p>    <h3>1.1.2 定位资源</h3>    <p>注:下文中url中的root代表你的web应用的名称加上你在web.xml中servlet-mapping元素设置的路径。</p>    <p>@javax.ws.rs.ApplicationPath 标识应用路径,用于由@Path提供的所有资源uri的基本uri。当发布在一个servlet容器中,它的值可以使用web.xml中的servlet-mapping进行重写。</p>    <p>@javax.ws.rs.Path 标识要请求的资源类或类方法的uri路径。</p>    <p>@javax.ws.rs.PathParam 将uri中指定的路径参数绑定到资源方法参数,资源类的字段,或资源类的bean属性。</p>    <p>比如:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserResource {   @GET   @Path("{userName"})   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@PathParam("userName") String userName) {   ...   }  }  </code></pre>    <p>当浏览器请求http://localhost:8080/root/user/boglond时,方法中userName值为boglond。</p>    <p>@javax.ws.rs.QueryParam 将http请求的Query参数绑定到资源方法参数,资源类的字段,或资源类的bean属性。</p>    <p>eg:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getUser")   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@QueryParam("userName") String userName,@QueryParam("age") int age) {   ...   }  }  </code></pre>    <p>当浏览器请求http://localhost:8080/root/user/getUser?userName=boglond&age=26时,方法中userName值为boglond,age值为26。</p>    <p>@javax.ws.rs.FormParam 将http请求的Form表单中的参数绑定到资源方法参数。</p>    <p>eg:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @javax.ws.rs.POST   @Path("/insert")   @Consumes(MediaType.APPLICATION_FORM_URLENCODED)   public String insertUser(@FormParam("userName") String userName,@FormParam("age") int age){   ...   }  }  </code></pre>    <p>页面中的form表单如下</p>    <pre>  <code class="language-java"><formaction="/root/user/insert" method="post">   姓名:<inputname="userName" id= "userName" type = "text" value="boglond"/></br>   年龄:<inputname="age" id= "age" type = "text" value="26"/>   <buttontype = "submit">提交</button>  </form>  </code></pre>    <p>提交表单后方法中userName值为boglond,age值为26。</p>    <p>@javax.ws.rs.CookieParam 将http cookie的值绑定到资源方法参数,资源类的字段,或资源类的bean属性。</p>    <p>eg:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getCookieParam")   @Produces(MediaType.APPLICATION_JSON)   public String getCookieParam(@CookieParam("JSESSIONID") String jsessionId) {   ...   }  }  </code></pre>    <p>当浏览器请求http://localhost:8080/root/user/getCookieParam时,方法中jsessionId值为”FFF7BDDC46579DBDDDEB3E94776A2623″,此值不是固定的,不同的浏览器有不同的值。</p>    <p>@javax.ws.rs.HeaderParam 将http header的值绑定到资源方法参数,资源类的字段,或资源类的bean属性。</p>    <p>eg:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getHeaderParam")   @Produces(MediaType.APPLICATION_JSON)   public String getHeaderParam(@HeaderParam("Accept") String accept,@HeaderParam("Accept-Language")     String acceptLang) {   ...   }  }  </code></pre>    <p>当浏览器请求http://localhost:8080/root/user/getHeaderParam时,方法中accept值为”text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8″,acceptLang值为”zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3″,不同的浏览器,值有所不同。</p>    <p>@javax.ws.rs.MatrixParam 将uri矩阵参数的值绑定到资源方法参数,资源类的字段,或资源类的bean属性。</p>    <p>eg:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getUser")   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@MatrixParam("userName") String userName,@MatrixParam("age") int age) {   ...   }  }  </code></pre>    <p>当浏览器请求http://localhost:8080/root/user/getUser;userName=boglond;age=26时,方法中userName值为boglond,age值为26。</p>    <p>@javax.ws.rs.DefaultValue 设置 @PathParam, @QueryParam, @MatrixParam, @CookieParam, @FormParam, or @HeaderParam参数的默认值。如果它们没有接收到值,就使用默认值。</p>    <p>@javax.ws.rs.BeanParam 将请求中的bean绑定到资源方法参数,资源类的字段,或资源类的bean属性。</p>    <p>eg:</p>    <p>bean的配置:</p>    <pre>  <code class="language-java">@javax.xml.bind.annotation.XmlRootElement  public class UserBean{   @FormParam("userName")   private String userName;   @FormParam("age")   private int age;   ...  }  </code></pre>    <p>资源类方法的配置:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @javax.ws.rs.POST   @Path("/insertUserBean")   @Consumes(MediaType.APPLICATION_FORM_URLENCODED)   public String insertUserBean(@BeanParam UserBeanuserBean){   ...   }  }  </code></pre>    <p>页面中的form表单如下:</p>    <pre>  <code class="language-java"><formaction="/root/user/insertUserBean" method="post">   姓名:<inputname="userName" id= "userName" type = "text" value="boglond"/></br>   年龄:<inputname="age" id= "age" type = "text" value="26"/>   <buttontype = "submit">提交</button>  </form>  </code></pre>    <p>当提交表单后insertUserBean方法中的userBean对象的属性值会映射为表单中对应的值。</p>    <h3>1.1.3 表述资源的数据格式(通过媒体类型表述)</h3>    <p>@javax.ws.rs.Consumes 定义一个资源类的方法或MessageBodyReader能够接受的媒体类型。方法级别的@Consumes会覆盖类级别的@Consumes。(指定将要返回给client端的数据MIME类型)</p>    <p>@javax.ws.rs.Produces 定义一个资源类的方法或MessageBodyWriter能够产生的媒体类型。方法级别的@Produces会覆盖类级别的@Produces。(指定可以接受client发送过来的MIME类型)</p>    <p>eg:@Produces(“application/json”);指定多个MIME类型 @Produces({“application/json”,”application/xml”})</p>    <p>常见的媒体类型如</p>    <pre>  <code class="language-java">text/html : HTML格式  text/plain :纯文本格式  text/xml :  XML格式(它会忽略xml头所指定编码格式而默认采用us-ascii编码)  image/gif :gif图片格式  image/jpeg :jpg图片格式  image/png:png图片格式  application/xhtml+xml :XHTML格式  application/xml     : XML格式(它会根据xml头指定的编码格式来编码)  application/atom+xml  :AtomXML聚合格式  application/json    : JSON数据格式  application/pdf       :pdf格式  application/msword  : Word文档格式  application/octet-stream : 二进制流数据(如常见的文件下载)  application/x-www-form-urlencoded : <formencType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)  multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式。  </code></pre>    <p>媒体类型的常量值在javax.ws.rs.core.MediaType中。</p>    <h3>1.1.4 编解码</h3>    <p>@javax.ws.rs.Encoded 禁用由 @QueryParam, @PathParam, @FormParam or @MatrixParam绑定的参数值的自动解码</p>    <p>eg:</p>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getUser")   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@MatrixParam("userName") String userName,   @MatrixParam("age") int age) {   ...   }  }  </code></pre>    <p>此时没有使用@Encoded</p>    <p>当浏览器请求http://localhost:8080/root/user/getUser;userName=boglond%5CD;age=26时,方法中userName值为boglond\D,age值为26。</p>    <p>当给方法参数加上@Encoded时,</p>    <p>浏览器请求http://localhost:8080/root/user/getUser;userName=boglond%5CD;age=26,方法中userName值为boglond%5CD,age值为26。</p>    <p>@Encoded可加在方法的上面禁用此方法所有参数的自动解码。</p>    <h2>1.2 javax.ws.rs.client包(JAX-RS客户端API)</h2>    <p>javax.ws.rs.client.WebTarget 由资源URI标识的资源目标。</p>    <p>javax.ws.rs.client.ClientBuilder 用于加载客户端实例的主入口点。</p>    <p>eg:使用 request() 函数来初始化一个请求并用后续的 post 或者get等方法来指定请求的类型。</p>    <pre>  <code class="language-java">Clientclient = ClientBuilder.newClient();  client.target(..).request().get();  </code></pre>    <p>通过 path() 和 resolveTemplate() 方法来处理动态的 URL 路径参数。</p>    <pre>  <code class="language-java">client.target(..).path(..).resolveTemplate(..);  javax.ws.rs.client.InvocationCallback<RESPONSE> 可以实现从调用处理接收异步处理事件。  javax.ws.rs.client.ClientRequestFilter 客户端请求过滤器。  javax.ws.rs.client.ClientResponseFilter 客户端响应过滤器。  </code></pre>    <h2>1.3 javax.ws.rs.container包(特定容器JAX-RS API )</h2>    <p>@javax.ws.rs.container.ResourceContext 提供对资源类实例的访问,使用@javax.ws.rs.core.Context 注入。</p>    <p>eg:</p>    <pre>  <code class="language-java">public String getResourceContext(@javax.ws.rs.core.ContextResourceContextresourceContext) {  ...  }  </code></pre>    <p>@javax.ws.rs.container.ContainerRequestContext 容器请求过滤器上下文。为过滤器提供特定于请求的信息的可变类,例如请求URI,消息头,消息实体或请求范围属性。暴露的setter方法允许修改暴露的请求特定信息。</p>    <p>@javax.ws.rs.container.ContainerResponseContext 容器响应过滤器上下文。为过滤器提供特定于响应的信息的可变类,例如消息头,消息实体或请求范围属性。暴露的setter方法允许修改暴露的响应特定信息。</p>    <h2>1.4 javax.ws.rs.core包(用于创建RESTful服务资源的低级接口和注解)</h2>    <p>@javax.ws.rs.core.MediaType 媒体类型常量类。</p>    <p>j</p>    <p>avax.ws.rs.core.Cookie 在请求中转移的HTTP Cookie的值。</p>    <p>eg:</p>    <pre>  <code class="language-java">Cookie c = new Cookie(name, value);  javax.ws.rs.core.Form 表示使用"application / x-www-form-urlencoded"媒体类型编码的HTML表单数据请求实体。  </code></pre>    <p>eg:</p>    <pre>  <code class="language-java">Form f = new Form();  f.param(name, value)  </code></pre>    <p>javax.ws.rs.core.Response.Status  http所定义的响应状态码。</p>    <p>javax.ws.rs.core.HttpHeaders 一个可注入的接口,提供对HTTP头信息的访问。</p>    <p>eg:</p>    <pre>  <code class="language-java">@GET  @Path("/getHttpHeaders")  @Produces(MediaType.APPLICATION_JSON)  public String getHttpHeaders(@javax.ws.rs.core.ContextHttpHeadershttpHeaders) {  ...  }  </code></pre>    <p>@javax.ws.rs.core.UriInfo 一个可注入的接口,提供对应用程序和请求URI信息的访问。</p>    <p>eg:</p>    <pre>  <code class="language-java">@GET  @Path("/getUriInfo")  @Produces(MediaType.APPLICATION_JSON)  public String getUriInfo(@javax.ws.rs.core.ContextUriInfouriInfo) {  ...  }  </code></pre>    <p>@javax.ws.rs.core.Context 将信息注入类字段,bean属性或方法参数。如</p>    <h2>1.5 javax.ws.rs.ext包(为JAX-RS API支持的类型提供扩展)</h2>    <p>javax.ws.rs.ext.MessageBodyReader<T> 反序列化</p>    <p>javax.ws.rs.ext.MessageBodyWriter<T> 序列化</p>    <h2>2.Jersey发布rest服务简介</h2>    <p>Jersey项目使用apache Maven构建,它的相关模块都可从Maven中央仓库获取。本项目是基于JAX-RS 2.0的。</p>    <h2>2.1 创建一个maven工程</h2>    <p>如图</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/79a8d24d4f681ec32a714bb4cfd24a7a.png"></p>    <h2>2.2 在pom中添加依赖</h2>    <pre>  <code class="language-java"><dependency>      <groupId>javax.ws.rs</groupId>      <artifactId>javax.ws.rs-api</artifactId>      <version>2.0.1</version>  </dependency>  <dependency>      <groupId>org.glassfish.jersey.containers</groupId>      <!-- if yourcontainerimplements ServletAPIolderthan 3.0, use "jersey-container-servlet-core"  -->      <artifactId>jersey-container-servlet</artifactId>       <version>2.25</version>  </dependency>  </code></pre>    <h2>2.3 配置web.xml</h2>    <p>在web.xml中添加</p>    <pre>  <code class="language-java"><servlet>        <servlet-name>jersey-demo</servlet-name>        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>        <init-param>            <param-name>jersey.config.server.provider.packages</param-name>            <!-- 这个包路径换成你的包路径,多个包路径可以使用逗号分隔-->            <param-value>com.boglond.jersey</param-value>        </init-param>        <load-on-startup>1</load-on-startup>  </servlet>  <servlet-mapping>        <servlet-name>jersey-demo</servlet-name>        <!-- 这个设置为你想要的路径-->        <url-pattern>/resources/*</url-pattern>  </servlet-mapping>  </code></pre>    <h2>2.4 创建资源类</h2>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getUser")   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@QueryParam("userName") String userName,   @QueryParam("age") int age) {   ...   }  }  </code></pre>    <p>访问:在浏览器里输入http://localhost:8080/jersey-demo/resources/user/getUser?userName=boglond&age=30即可访问。</p>    <h2>3.CXF发布rest服务简介</h2>    <h2>3.1 创建一个maven工程</h2>    <p>如图</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/ea47f475ab4833ebd1e7a9f76e2ba20d.png"></p>    <h2>3.2 在pom中添加依赖</h2>    <p>CXF的依赖</p>    <pre>  <code class="language-java"><dependency>      <groupId>org.apache.cxf</groupId>      <artifactId>cxf-rt-frontend-jaxrs</artifactId>      <version>3.0.0</version>  </dependency>        <dependency>      <groupId>org.apache.cxf</groupId>      <artifactId>cxf-rt-transports-http-jetty</artifactId>      <version>3.0.0</version>  </dependency>  </code></pre>    <p>日志的依赖</p>    <pre>  <code class="language-java"><dependency>      <groupId>log4j</groupId>      <artifactId>log4j</artifactId>      <version>1.2.17</version>  </dependency>  <dependency>   <groupId>org.slf4j</groupId>   <artifactId>slf4j-log4j12</artifactId>   <version>1.7.6</version>  </dependency>  </code></pre>    <h2>3.3 创建资源类</h2>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getUser")   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@QueryParam("userName") String userName,   @QueryParam("age") int age) {   ...   }  }  </code></pre>    <h2>3.4 发布REST服务</h2>    <pre>  <code class="language-java">package com.boglond.cxf;  import java.util.ArrayList;  import java.util.List;  import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;  import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;  import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory;  public class CxfServer {    private final static Loggerlogger = LoggerFactory.getLogger(CxfServer.class);    public static void main(String[] args) {        List<Class<?>> resourceClassList = new ArrayList<Class<?>>();      resourceClassList.add(UserRecource.class);          List<ResourceProvider> resourceProviderList = new ArrayList<ResourceProvider>();      resourceProviderList.add(new SingletonResourceProvider(new UserRecource()));      // 发布 REST 服务      JAXRSServerFactoryBeanfactory = new JAXRSServerFactoryBean();      factory.setAddress("http://localhost:11002/cxf-demo/recources");      factory.setResourceClasses(resourceClassList);      factory.setResourceProviders(resourceProviderList);      factory.create();      logger.info("使用CXF发布REST服务成功");    }  }  </code></pre>    <p>访问:在浏览器里输入http://localhost:8080/cxf-demo/recources/user/getUser?userName=boglond&age=26即可访问。</p>    <h2>4.RESTEasy发布rest服务简介</h2>    <h2>4.1 创建一个maven工程</h2>    <p>如图</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/85c818d370b160e2c5d7bbceb85625f1.png"></p>    <h2>4.2 在pom中添加依赖</h2>    <pre>  <code class="language-java"><dependency>        <groupId>org.jboss.resteasy</groupId>        <artifactId>resteasy-jaxrs</artifactId>        <version>3.1.0.Final</version>  </dependency>  <dependency>        <groupId>org.jboss.resteasy</groupId>        <artifactId>resteasy-jaxb-provider</artifactId>        <version>3.1.0.Final</version>  </dependency>  如果servlet容器使用的是servlet3.0 需要加入如下依赖  <dependency>        <groupId>org.jboss.resteasy</groupId>        <artifactId>resteasy-servlet-initializer</artifactId>        <version>3.1.0.Final</version>  </dependency>  </code></pre>    <p>注:如果你配置的maven仓库地址下载不下来resteasy依赖的jar,就将你的maven conf文件夹下的setting.xml 中的仓库url替换为下面的url。<url>http://repository.jboss.org/nexus/content/groups/public/</url></p>    <h2>4.3 配置web.xml</h2>    <p>在web.xml中添加</p>    <pre>  <code class="language-java"><context-param>    <param-name>resteasy.resources</param-name>    <!-- 这是你的资源类 -->    <param-value>com.boglond.resteasy.UserRecource</param-value>  </context-param>  <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>/*</url-pattern>  </servlet-mapping>  </code></pre>    <h2>4.4 创建资源类</h2>    <pre>  <code class="language-java">@Path("/user")  public class UserRecource {   @GET   @Path("/getUser")   @Produces(MediaType.APPLICATION_JSON)   public UsergetUser(@QueryParam("userName") String userName,   @QueryParam("age") int age) {   ...   }  }  </code></pre>    <p> </p>    <p> </p>    <p>来自:http://blog.jobbole.com/109031/</p>    <p> </p>