微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

转载:使用JAX-RS创建RESTful WebServices

原文:http://blog.csdn.net/huxiweng/article/details/7349795

本章介绍REST架构、RESTful web service和JAX-RS(Java API for RESTful WebService,JSR 311)。
    JAX-RS的参考实现Jersey实现了对JSR311中定义的注解的支持,使得使用Java编程语言开发RESTful webservice变得简单。
    如果是使用galssFish服务器,可以使用Updatetool安装Jersey例子和文档。使用Update Tool的教程见第62页的“Java EE6教程组件”。Jersey例子和文档位于Update Tool的AvailableAdd-ons中。


什么是RESTful WebServices
    RESTful webservice是创建来能在web更好的运行的web service。REST是一种架构类型,指定了如统一的接口等应用于webservice的约束。REST提供了如性能、可扩展性和可变性等特性,使得service能够更好的在web上工作。在REST框架中,数据和功能被认为是资源,是通过URI来访问的,通常是web链接。资源是通过使用一组简单的、定义良好的操作来生效。REST的架构方式限定了客户/服务器架构,是设计来使用无状态的通信协议的,通常是HTTP。在REST框架类型中,客户端和服务器使用标准的接口和协议交换资源的representation。
   下面的原则使得RESTful的应用程序简单、轻量并快捷:
   通过URI确定资源一个RESTful的webservice会公开一组资源,这组资源确定了和客户端互动的目标。资源是通过URI来确定的,URI为service和资源提供了全球的地址空间。更多的信息见第239页的“@Path注解和URI路径模版”。
   统一的接口:资源是通过固定的操作PUT,GET,POST和DELETE来创建、读取、修改删除的。PUT会创建一个新的资源,DELETE会删除一个资源。GET会获取资源的当前状态。POST将资源的状态转变成新的值。更多信息见底241页的“对HTTP资源的响应”。
   自描述的消息:资源和它们的表现是解耦的,因此可以以不同的格式来访问,如HTML,XML,纯文本,PDF,JPEG,JSON等。关于资源的元数据是有效的并且用来完成控制缓存、检测传输错误、商讨合适的表现格式、执行身份验证和访问控制等。更多信息见第241页的“对HTTP资源的响应”和第243页的“使用实体提供者来映射HTTPresponse和request的实体段”。
   使用超链接的无状态的互动:和资源的互动都是无状态的,也就是说,request消息是自包含的。无状态的互动是基于显示状态转换的概念的。如URI重写、cookies和隐藏的表单字段等技术是为了交换状态而存在的。状态可以被嵌套在response消息中来指向互动的将来有效的状态。更多信息见“使用实体提供者来映射HTTPresponse和request的实体段”和JAX-RS Overview文档中的“BuildingURIs”。

创建RESTful的Web根资源类
    根资源类(Root resourceclasses)是由@Path注解标记的POJO或者至少有一个方法有@Path注解或请求方法指示符注解(@GET,@PUT,@POST或@DELETE)的POJO。
    资源方法(resourcemethod)是资源类中含有请求方法指示符注解的方法
   本节中将会解释怎样使用JAX-RS来注解java类来创建RESTful的web service。
使用JAX-RS来开发RESTful webservice
   JAX-RS是一个Java编程语言接口,被设计用来简化使用REST架构的应用程序的开发。
    JAX-RSAPI使用Java编程语言的注解来简化RESTful webservice的开发。开发人员使用JAX-RS的注解修饰Java编程语言的类文件来定义资源和能够应用在资源上的行为。JAX-RS的注解是运行时的注解,因此运行时的映射会为资源生成辅助类和其他的辅助文件。包含JAX-RS资源类的JavaEE应用程序中资源是被配置好的,辅助类和辅助文件生成的,资源通过被发布到JavaEE服务器上来公开给客户端。
   下表列出了JAX-RS定义的一些Java注解以及怎样使用它们的简要的描述。更进一步的JAX-RS的API见 http://download.oracle.com/javaee/6/api

注解

描述

@Path

@Path注解的值是一个相对的URI路径,这个路径指定了该Java类的位置,例如/helloworld在这URI中可以包含变量,例如可以获取用户的姓名然后作为参数传入URI中:/helloworld/{username}

@GET

@GET注解是请求方法指示符,这个指示符注解的Java方法会处理HTTPGET请求。资源的行为由资源回应的HTTP方法决定。

@POST

@POST注解是请求方法指示符,这个指示符注解的HTTPPOST请求。资源的行为由资源回应的@PUT

@PUT注解是请求方法指示符,这个指示符注解的HTTPPUT请求。资源的行为由资源回应的@DELETE

@DELETE注解是请求方法指示符,这个指示符注解的HTTPDELETE请求。资源的行为由资源回应的@HEAD

@HEAD注解是请求方法指示符,这个指示符注解的HTTPHEAD请求。资源的行为由资源回应的@PathParam

@PathParam注解是可以抽取并用在资源类中的一类参数。URIpath参数是从请求的URI中抽取的,而且参数的名称@Path注解中定义的变量名对应。

@QueryParam

@QueryParam注解是可以抽取并在资源类中使用的一类参数。Query参数是从请求URI查询参数中抽取的。

@Consumes

@Consumes注解是用来指定资源能够接受的客户发送的MIME媒体类型

@Produces

@Produces注解用来指定资源能够生成并发送给客户端的MIME媒体类型,例如“text/plain”.

@Provider

@Provider注解用在任何对JAX-RS运行时(如MessageBodyReaderMessageBodyWriter)有意义的事物上。对HTTP请求,MessageBodyReader用来将HTTP请求实体段映射为方法参数。在响应的时候,返回的值使用MessageBodyWriter来映射成HTTP响应实体段。如果应用程序需要提供其他的元数据,如HTTP头或不同的状态代码方法可以返回一个打包了实体的Response,该Response可以使用Response.ResponseBuilder创建。

JAX-RS应用程序概况
   下面的代码例子是一个非常简单的根资源类,使用了JAX-RS注解:
  1. package com.sun.jersey.samples.helloworld.resources;
  2. import javax.ws.rs.GET;
  3. import javax.ws.rs.Produces;
  4. import javax.ws.rs.Path;
  5. // The Java class will be hosted at the URI path"/helloworld"
  6. @Path("/helloworld")
  7. public class HelloWorldResource {
  8.        // The Javamethod will process HTTP GET requests
  9.        @GET
  10.        // The Javamethod will produce content identified by the MIME Media
  11.        // type"text/plain"
  12.        @Produces("text/plain")
  13.        public StringgetClichedMessage() {
  14.              // Returnsome cliched textual content
  15.              return"Hello World";
  16.        }
  17. }

   随后的几节中我们会详细描述这个例子中使用的注解。
   @Path注解的值是一个相对的URI路径。在上面的例子中,这个java类会放在/helloworld这个路径下。上面的例子中使用的是静态的URI路径,是最简单的例子。在URI中,我们可以包含变量,对于这样的包含变量的URI我们称为URI路径模板。
   @GET注解是一个请求方法指示符。在上面的例子中,被注解的Java方法会处理HTTP GET请求。
   @Produces注解用来指定资源能够生产和发送回客户端的MIME媒体类型。在上面的例子中,Java方法生成MIME媒体类型“text/plain”。
   @Consumes注解用来指定资源能够消费的客户端发送的MIME媒体类型。我们可以将上面的例子中的代码修改
  1. @POST
  2. @Consumes("text/plain")
  3. public void postClichedMessage(String message) {
  4. // Store the message
  5. }

@Path注解和URI路径模板
   @Path注解指定了URI资源会响应的路径模板,是指定在资源的类级别或方法级别上。@Path注解的值是一个相对URI路径模板,这个路径是相对于资源发布的服务器的基础URI路径、应用程序的上下文根路径和JAX-RS运行时响应的URL。URI路径模板是包含变量的URI。这些变量在运行时会被取代,这样资源就能够根据被取代的URI来响应请求。变量是用花括号{}表示的,例如下面的例子
  1. @Path annotation:
  2. @Path("/users/{username}")
    在这个例子中,用户会被提示输入自己的名字,然后一个被配置来响应这个URI路径模板的JAX-RSweb service会响应该请求。例如,如果用户输入的名字是galileo,这个webservice会响应下面的URL http://example.com/users/Galileo.
获取用户的名字,可以在请求方法的参数中使用@PathParam注解,如下面的代码所示:
  1. @Path("/users/{username}")
  2. public class UserResource {
  3.        @GET
  4.        @Produces("text/xml")
  5.        public StringgetUser(@PathParam("username") String userName) {
  6.        ...
  7.        }
  8. }

  缺省的,URI变量必须符合正则表达式“[^/]+?”。开发人员也可以通过在变量名称后指定不同的正则表达式来指定客户化的变量模式。例如,如果用户名字必须是由大写或小写字母组成,可以在变量的定义中指定:
  1.        @Path("users/{username:[a-zA-Z][a-zA-Z_0-9]}")

  在这个例子中,username这个变量只能匹配由大写或小写字母开头,后跟0个或多个字母、数字和下划线的用户名。如果用户名不匹配这个模板,那么一个404(NotFound)响应会被发送给客户端。
   @Path的值并不要求必须以/开始。无论是否以/开头或者开头是否有空格,JAX-RS运行时会一样的解析。
   一个URI路径模板可以包含一个或多个变量名,每个变量名都以前花括号{开始,以后花括号}结束。在上面的例子中,变量名是username。在运行时,被配置为响应上面的URI路径模板的资源会处理URI数据,将URI中对应{username}的部分作为username的变量数据。
   例如,如果你想要发布一个资源来响应URI路径模板 http://example.com/myContextRoot/resources/ {name1}/{name2}/,你需要将应用程序发布在一个JavaEE服务器上,该服务器要响应对URI  http://example.com/myContextRoot 的请求,并使用如下的@Path注解来修饰你的资源:
  1. @Path annotation:
  2. @Path("/{name1}/{name2}/")
  3. public class SomeResource {
  4. ...
  5. }

  在这个例子中,在web.xml文件中指定的JAX-RS辅助servlet的URLpattern是:

  1. My JAX-RS Resource
  2. /resources*)

    java.lang.String

    所有的文本媒体类型text*

    java.io.Reader

    所有的媒体类型**

    javax.activation.DataSource

    所有的媒体类型**),只对MessageBodyWriter有效

      下面的例子显示了怎样与@Consumes和@Provider注解一起使用MessageBodyReader:
    1. @Consumes("application/x-www-form-urlencoded")
    2. @Provider
    3. public class FormReader implements MessageBodyReader {

  下面的例子显示了怎样与@Produces和@Provider注解一起使用MessageBodyWriter:
  1. @Produces("text/html")
  2. @Provider
  3. public class FormWriter implements
  4. MessageBodyWriter> {

   下面的例子显示了怎样使用ResponseBuilder:
  1. @GET
  2. public Response getItem() {
  3.        System.out.println("GETITEM " + container + " " + item);
  4.        Item i =MemoryStore.MS.getItem(container,item);
  5.        if (i ==null)
  6.              throw newNotFoundException("Item not found");
  7.        DatelastModified = i.getLastModified().getTime();
  8.        EntityTag et= new EntityTag(i.getDigest());
  9.        ResponseBuilderrb = request.evaluatePreconditions(lastModified,et);
  10.        if (rb !=null)
  11.              returnrb.build();
  12.        byte[] b =MemoryStore.MS.getItemData(container,item);
  13.        returnResponse.ok(b,i.getMimeType()).
  14.        lastModified(lastModified).tag(et).build();
  15. }

使用@Consumes和@Produces来客户化Request和Response
   发送给资源的信息和传回客户端的信息都被指定了MIME媒体类型,是在HTTPrequest或response的头中指定的。可以使用下面的注解来指定资源能够响应或生产的MIME媒体类型
  1. javax.ws.rs.Consumes
  2. javax.ws.rs.Produces

   认的,一个资源能够响应和生产所有的MIMIE媒体类型
@Produces注解
   注解@Produces是用来指定一个资源能够产生并发送回客户端的MIME媒体类型或表现。如果@Produces是应用在类级别上,那么认的资源的所有的方法都能够生产指定的MIME类型。如果是应用在方法级别上,那么这个注解会覆盖应用在类级别的@Produces注解。如果资源中没有方法能够生产客户端请求的MIME类型,那么JAX-RS运行时会发送回一个HTTP的“406Not Acceptable”错误
   注解@Produces的值是一个MIME类型的字符串数组,例如
  1. @Produces({"image/jpeg,image/png"})

  下面的例子显示了怎样在类级别和方法级别应用@Produces注解:
  1. @Path("/myResource")
  2. @Produces("text/plain")
  3. public class SomeResource {
  4.        @GET
  5.        public StringdoGetAsPlainText() {
  6.             ...
  7.        }
  8.        @GET
  9.        @Produces("text/html")
  10.        public StringdoGetAsHtml() {
  11.             ...
  12.        }
  13. }

  方法doGetAsPlainText()认使用的是类级别上定义的@Produces注解指定的MIME媒体类型方法doGetAsHtml上的@Produces注解覆盖了类级别的设置,因此该方法能够生成的是HTML类型,而不是普通文本。
   如果一个资源类能够生产超过一种MIME媒体类型,那么资源方法的选择就对应最能够接受的客户端声明的媒体类型。具体来说,HTTP请求的Accept头属性中声明了最可接受的MIME类型。例如,如果Accept头的值是Accept:text/plain,那么方法doGetAsPlainText会被调用。如果Accept头的值是Accept:text/plain;q=0.9,text/html,这声明了客户端能够接受媒体类型text/plain和test/html,但是更喜欢后者,因此doGetAsHtml方法会被调用
   一个@Produces注解中可以声明多个媒体类型。下面的代码例子显示了怎样做到这点:
  1. @Produces({"application/xml","application/json"})
  2. public String doGetAsXmlOrjson() {
  3.        ...
  4. }

  当可接受的媒体类型是application/xml或application/json的时候这个doGetAsXmlOrjson方法会被调用。如果两个媒体类型是同样可接受的,那么会选择声明在前面的一个
   上面的例子中,为了更明白,我们使用的是明确的MIME媒体类型。也可以使用相应的常量值,这样可以减少拼写错误。相关的信息见MediaType类的常量字段,位于https://jsr311.dev.java.net/nonav/releases/1.0/javax/ws/rs/core/MediaType.html
@Consumes注解
   注解@Consumes被用来指定资源能够接受或消费的来自客户端的MIME媒体类型。如果@Consumes是应用在类级别上的,该类的所有的响应方法认的接受指定的MIME类型。如果是应用在方法级别上,那么方法级别上的@Consumes注解会覆盖应用在类级别上的@Consumes注解。
   如果资源不能够消费客户请求中的MIME类型,JAX-RS运行时会发送回一个HTTP 415 (“Unsupported MediaType”)错误
   @Consumes注解的值是一个可接受的MIME类型的字符串数组。例如:
  1. @Consumes({"text/plain,text/html"})

  下面的例子显示了怎样在类级别和方法级别上使用@Consumes注解。
  1. @Path("/myResource")
  2. @Consumes("multipart/related")
  3. public class SomeResource {
  4.        @POST
  5.        public StringdoPost(MimeMultipart mimeMultipartData) {
  6.             ...
  7.        }
  8.        @POST
  9.        @Consumes("application/x-www-form-urlencoded")
  10.        public StringdoPost2(FormURLEncodedProperties formData) {
  11.             ...
  12.        }
  13. }

  方法doPost认的接受类级别的@Consumes注解指定的MIME类型。方法doPost2的@Consumes注解覆盖了类级别的@Consumes注解,因此doPost2方法可接受的MIME类型是application/x-www-form-urencoded。
   如果没有任何的资源方法能够响应请求中的MIME类型,那么一个HTTP 415错误会被返回给客户端。
   本节中前面讨论的HelloWorld例子中,我们可以使用@Consume注解,如同我们下面的代码所做的
  1. @POST
  2. @Consumes("text/plain")
  3. public void postClichedMessage(String message) {
  4.        // Store themessage
  5. }

  在这个例子中,这个方法会消费由MIME媒体类型text/plain指定的表现。注意这里资源的方法返回的是void,这意味着没有任何表现被返回,因此一个状态码为HTTP204(“No Content”)的响应会被返回。
提取请求参数
   资源方法的参数可能被添加了基于参数的注解来从请求中提取信息。在以前的例子中我们展现了时候@PathParam参数来从匹配@Path中声明的路径的request的URL中提取路径参数。
   可以在资源类中提取下面类型的参数:
  Query参数是从请求URI的查询参数中抽取的,在方法参数中使用javax.ws.rs.QueryParam注解来指定。下面的代码例子来自sparklines例子程序,演示了怎样使用@QueryParam注解来从请求URL中抽取query参数:
  1. @Path("smooth")
  2. @GET
  3. public Response smooth(
  4. @DefaultValue("2") @QueryParam("step") int step,
  5. @DefaultValue("true") @QueryParam("min-m") booleanhasMin,
  6. @DefaultValue("true") @QueryParam("max-m") booleanhasMax,
  7. @DefaultValue("true") @QueryParam("last-m") booleanhasLast,
  8. @DefaultValue("blue") @QueryParam("min-color") ColorParamminColor,
  9. @DefaultValue("green") @QueryParam("max-color") ColorParammaxColor,
  10. @DefaultValue("red") @QueryParam("last-color") ColorParamlastColor
  11. ) { ... }

  如果query参数step在请求URI的查询部分中存在,那么将从URI中抽取step的值并将其解析为32位的有符号整数并赋值给方法参数step。如果在请求URI的查询部分中不存在step参数,那么@DefaultValue注解所声明的缺省值2将被赋给方法参数step。如果step参数在请求URI中存在,但是无法被解析为32位的符号整数,那么一个HTTP400(“Client Error”)响应将会被返回。
   用户自定义的Java编程语言类型也可以被用来作为query参数。下面的代码例子是上面的例子中使用的ColorParam类。
  1. public class ColorParam extends Color {
  2.        publicColorParam(String s) {
  3.             super(getRGB(s));
  4.        }
  5.        privatestatic int getRGB(String s) {
  6.              if(s.charat(0) == ’#’) {
  7.                   try{
  8.                        Color c =Color.decode("0x" + s.substring(1));
  9.                        returnc.getRGB();
  10.                   } catch(NumberFormatException e) {
  11.                        throw newWebApplicationException(400);
  12.                   }
  13.              } else{
  14.                   try{
  15.                        Field f =Color.class.getField(s);
  16.                        return((Color)f.get(null)).getRGB();
  17.                   } catch(Exception e) {
  18.                        throw newWebApplicationException(400);
  19.                   }
  20.             }
  21.        }
  22. }

   类ColorParam的构造方法接受一个String作为参数。
   @QueryParam和@PathParam都只能被应用到以下的java类型:
  • 除char之外的原始类型
  • 除Character之外的原始类型封装类
  • 有接受单独的字符串做参数的构造函数的类
  • 包含名为valueOf(String)的静态方法的类
  • List,Set,SortedSet,这里T是上面所列的类型之一。有时候,同一个名字的参数可能包含多个值,在这种情况下,使用这些类型可以获取所有的值
  •   如果@DefaultValue没有被使用,在请求中又没有找到相应的query参数,那么对List,Set和SortedSet类型的参数其值是空的collection,对其他的对象类型值为null,对原始类型则是各自的认值。
       URI路径参数是从request的URI中提取的,参数的名字对应类级别的@Path注解指定的URI路径模板中的变量名。在方法参数中使用javax.ws.rs.PathParam注解来指定URI路径参数。下面的例子显示了怎样使用@Path注解和在方法中使用@Path注解:
    1. @Path("/{username}")
    2. public class MyResourceBean {
    3.        ...
    4.        @GET
    5.        public StringprintUsername(@PathParam("username") String userId) {
    6.        ...
    7.        }
    8. }

      在这个例子中,URI路径模板的变量名为username,该变量被指定为printUserName方法的参数。@PathParam注解的值被设置为变量名username。在运行的时候,printUserName被调用之前,username的值被从URI中提取出来并转换成字符串。转换的结果对printUserName方法生效并作为userId变量。
       如果路径模板的变量无法被转换成指定的类型,JAX-RS运行时会返回一个HTTP 400(“BadRequest”)错误给客户端。如果@PathParam注解不能转换成指定的类型,JAX-RS运行时会返回一个HTTP404(“Not Found”)错误给客户端。
       @PathParam参数和其他的基于参数的注解(@MatrixParam,@HeaderParam,@CookieParam和@FormParam)遵守和@Query相同的规则。
       Cookie参数是使用javax.ws.rs.CookieParam注解的参数,这种参数会从cookie相关的HTTP头声明的cookie中提取信息。Header参数是使用javax.ws.rs.HeaderParam注解的参数,这种参数从HTTP头中提取信息。Matrix参数是使用javax.ws.rs.MatrixParam的参数,这种参数会从URL路径段中提取信息。Form参数是使用javax.ws.rs.FormParam的参数,这种参数从MIME类型是application/x-www-form-urlencoded的请求表现中提取信息并遵从HTML表单指定的编码,如同http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1描述的一样。这种类型的参数对从以POST方式发送的HTML表单中提取信息很有用。
       下面的例子中我们从POST的表单数据中提取name参数:
    1. @POST
    2. @Consumes("application/x-www-form-urlencoded")
    3. public void post(@FormParam("name") String name) {
    4.        // Store themessage
    5. }

      要获得所有的query或path参数的参数名称和值的Map,使用如下的代码
    1. @GET
    2. public String get(@Context UriInfo ui) {
    3.        MultivaluedMapqueryParams = ui.getQueryParameters();
    4.        MultivaluedMappathParams = ui.getPathParameters();
    5. }

      下面的方法获得header参数和cookie参数的名称和值的Map:
    1. @GET
    2. public String get(@Context HttpHeaders hh) {
    3.        MultivaluedMapheaderParams = ui.getRequestHeaders();
    4.        MappathParams = ui.getCookies();
    5. }

      通常,@Context被用来获取和request或response相关的上下文的java类型。
    对form参数,我们可以进行以下的操作:
    1. @POST
    2. @Consumes("application/x-www-form-urlencoded")
    3. public void post(MultivaluedMap formParams) {
    4.        // Store themessage
    5. }

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

    相关推荐