本文主要是介绍使用JAX-RS创建RESTful Web Service,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本章介绍REST架构、RESTful web service和JAX-RS(Java API for RESTful Web Service,JSR 311)。JAX-RS的参考实现Jersey实现了对JSR 311中定义的注解的支持,使得使用Java编程语言开发RESTful web service变得简单。
如果是使用GalssFish服务器,可以使用Update Tool安装Jersey例子和文档。使用Update Tool的教程见第62页的“Java EE 6教程组件”。Jersey例子和文档位于Update Tool的Available Add-ons中。
什么是RESTful Web Services
RESTful web service是创建来能在web更好的运行的web service。REST是一种架构类型,指定了如统一的接口等应用于web service的约束。REST提供了如性能、可扩展性和可变性等特性,使得service能够更好的在web上工作。在REST框架中,数据和功能被认为是资源,是通过URI来访问的,通常是web链接。资源是通过使用一组简单的、定义良好的操作来生效。REST的架构方式限定了客户/服务器架构,是设计来使用无状态的通信协议的,通常是HTTP。在REST框架类型中,客户端和服务器使用标准的接口和协议交换资源的representation。
下面的原则使得RESTful的应用程序简单、轻量并快捷:
通过URI确定资源:一个RESTful的web service会公开一组资源,这组资源确定了和客户端互动的目标。资源是通过URI来确定的,URI为service和资源提供了全球的地址空间。更多的信息见第239页的“@Path注解和URI路径模版”。
统一的接口:资源是通过固定的操作PUT,GET,POST和DELETE来创建、读取、修改和删除的。PUT会创建一个新的资源,DELETE会删除一个资源。GET会获取资源的当前状态。POST将资源的状态转变成新的值。更多信息见底241页的“对HTTP资源的响应”。
自描述的消息:资源和它们的表现是解耦的,因此可以以不同的格式来访问,如HTML,XML,纯文本,PDF,JPEG,JSON等。关于资源的元数据是有效的并且用来完成控制缓存、检测传输错误、商讨合适的表现格式、执行身份验证和访问控制等。更多信息见第241页的“对HTTP资源的响应”和第243页的“使用实体提供者来映射HTTP response和request的实体段”。
使用超链接的无状态的互动:和资源的互动都是无状态的,也就是说,request消息是自包含的。无状态的互动是基于显示状态转换的概念的。如URI重写、cookies和隐藏的表单字段等技术是为了交换状态而存在的。状态可以被嵌套在response消息中来指向互动的将来有效的状态。更多信息见“使用实体提供者来映射HTTP response和request的实体段”和JAX-RS Overview文档中的“Building URIs”。
创建RESTful的Web根资源类
根资源类(Root resource classes)是由@Path注解标记的POJO或者至少有一个方法有@Path注解或请求方法指示符注解(@GET,@PUT,@POST或@DELETE)的POJO。
资源方法(resource method)是资源类中含有请求方法指示符注解的方法。
本节中将会解释怎样使用JAX-RS来注解Java类来创建RESTful的web service。
使用JAX-RS来开发RESTful web service
JAX-RS是一个Java编程语言接口,被设计用来简化使用REST架构的应用程序的开发。
JAX-RS API使用Java编程语言的注解来简化RESTful web service的开发。开发人员使用JAX-RS的注解修饰Java编程语言的类文件来定义资源和能够应用在资源上的行为。JAX-RS的注解是运行时的注解,因此运行时的映射会为资源生成辅助类和其他的辅助文件。包含JAX-RS资源类的Java EE应用程序中资源是被配置好的,辅助类和辅助文件是生成的,资源通过被发布到Java EE服务器上来公开给客户端。
下表列出了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注解是请求方法指示符,这个指示符注解的Java方法会处理HTTPPOST请求。资源的行为由资源回应的HTTP方法决定。 |
@PUT | @PUT注解是请求方法指示符,这个指示符注解的Java方法会处理HTTPPUT请求。资源的行为由资源回应的HTTP方法决定。 |
@DELETE | @DELETE注解是请求方法指示符,这个指示符注解的Java方法会处理HTTPDELETE请求。资源的行为由资源回应的HTTP方法决定。 |
@HEAD | @HEAD注解是请求方法指示符,这个指示符注解的Java方法会处理HTTPHEAD请求。资源的行为由资源回应的HTTP方法决定。 |
@PathParam | @PathParam注解是可以抽取并用在资源类中的一类参数。URIpath参数是从请求的URI中抽取的,而且参数的名称和@Path注解中定义的变量名对应。 |
@QueryParam | @QueryParam注解是可以抽取并在资源类中使用的一类参数。Query参数是从请求URI的查询参数中抽取的。 |
@Consumes | @Consumes注解是用来指定资源能够接受的客户发送的MIME媒体类型。 |
@Produces | @Produces注解用来指定资源能够生成并发送给客户端的MIME媒体类型,例如“text/plain”. |
@Provider | @Provider注解用在任何对JAX-RS运行时(如MessageBodyReader和MessageBodyWriter)有意义的事物上。对HTTP请求,MessageBodyReader用来将HTTP请求实体段映射为方法参数。在响应的时候,返回的值使用MessageBodyWriter来映射成HTTP响应实体段。如果应用程序需要提供其他的元数据,如HTTP头或不同的状态代码,方法可以返回一个打包了实体的Response,该Response可以使用Response.ResponseBuilder创建。 |
JAX-RS应用程序概况
下面的代码例子是一个非常简单的根资源类,使用了JAX-RS注解:
- package com.sun.jersey.samples.helloworld.resources;
- import javax.ws.rs.GET;
- import javax.ws.rs.Produces;
- import javax.ws.rs.Path;
- // The Java class will be hosted at the URI path "/helloworld"
- @Path("/helloworld")
- public class HelloWorldResource {
- // The Java method will process HTTP GET requests
- @GET
- // The Java method will produce content identified by the MIME Media
- // type "text/plain"
- @Produces("text/plain")
- public String getClichedMessage() {
- // Return some cliched textual content
- return "Hello World";
- }
- }
@Path注解的值是一个相对的URI路径。在上面的例子中,这个Java类会放在/helloworld这个路径下。上面的例子中使用的是静态的URI路径,是最简单的例子。在URI中,我们可以包含变量,对于这样的包含变量的URI我们称为URI路径模板。
@GET注解是一个请求方法指示符。在上面的例子中,被注解的Java方法会处理HTTP GET请求。
@Produces注解用来指定资源能够生产和发送回客户端的MIME媒体类型。在上面的例子中,Java方法会生成MIME媒体类型“text/plain”。
@Consumes注解用来指定资源能够消费的客户端发送的MIME媒体类型。我们可以将上面的例子中的代码修改为
- @POST
- @Consumes("text/plain")
- public void postClichedMessage(String message) {
- // Store the message
- }
@Path注解指定了URI资源会响应的路径模板,是指定在资源的类级别或方法级别上。@Path注解的值是一个相对URI路径模板,这个路径是相对于资源发布的服务器的基础URI路径、应用程序的上下文根路径和JAX-RS运行时响应的URL。URI路径模板是包含变量的URI。这些变量在运行时会被取代,这样资源就能够根据被取代的URI来响应请求。变量是用花括号{}表示的,例如下面的例子
- @Path annotation:
- @Path("/users/{username}")
要获取用户的名字,可以在请求方法的参数中使用@PathParam注解,如下面的代码所示:
- @Path("/users/{username}")
- public class UserResource {
- @GET
- @Produces("text/xml")
- public String getUser(@PathParam("username") String userName) {
- ...
- }
- }
- @Path("users/{username: [a-zA-Z][a-zA-Z_0-9]}")
@Path的值并不要求必须以/开始。无论是否以/开头或者开头是否有空格,JAX-RS运行时会一样的解析。
一个URI路径模板可以包含一个或多个变量名,每个变量名都以前花括号{开始,以后花括号}结束。在上面的例子中,变量名是username。在运行时,被配置为响应上面的URI路径模板的资源会处理URI数据,将URI中对应{username}的部分作为username的变量数据。
例如,如果你想要发布一个资源来响应URI路径模板 http://example.com/myContextRoot/resources/ {name1}/{name2}/,你需要将应用程序发布在一个Java EE服务器上,该服务器要响应对URI http://example.com/myContextRoot 的请求,并使用如下的@Path注解来修饰你的资源:
- @Path annotation:
- @Path("/{name1}/{name2}/")
- public class SomeResource {
- ...
- }
- <servlet-mapping>
- <servlet-name>My JAX-RS Resource</servlet-name>
- <url-pattern>/resources/*</url-pattern>
- </servlet-mapping>
如果变量的值中的字符和URI的保留字符冲突,那么该冲突的字符应该使用百分号编码代替。例如,变量值中的空格应该是一%20代替。
下表中列出了一些URI路径模板的例子以及这些URI中的变量被替换后的结果,例子中使用保额变量名和值如下:
name1: james
name2: gatz
name3:
location: Main%20Street
question: why
注意:names的值是空字符串“”。
URI路径模板 | 替换后的URI |
http://example.com/{name1}/{name2}/ | http://example.com/james/gatz/ |
http://example.com/{question}/{question}/{question}/ | http://example.com/why/why/why/ |
http://example.com/maps/{location} | http://example.com/maps/Main%20Street |
http://example.com/{name3}/home/ | http://example.com//home |
对HTTP资源的响应
资源的行为是由资源响应的HTTP方法(GET,POST,PUT,DELETE)来决定的。
请求方法指示符注解
请求方法指示符注解是由JAX-RS定义的运行时注解,对应同名的HTTP方法。在一个资源类文件中,HTTP方法通过使用请求方法指示符注解映射到Java编程语言的方法。一个资源的行为是由其要响应的HTTP方法决定的。JAX-RS为普通的HTTP方法定义了@GET,@POST,@PUT,@DELETE和@HEAD注解。开发人员也肯创建自己的客户化的请求方法指示符。创建客户化的请求方法指示符不在本教程的范围内。
下面的例子是从storage service例子程序中抽取的,显示了怎样使用PUT方法来创建或更新storage容器:
- @PUT
- public Response putContainer() {
- System.out.println("PUT CONTAINER " + container);
- URI uri = uriInfo.getAbsolutePath();
- Container c = new Container(container, uri.toString());
- Response r;
- if (!MemoryStore.MS.hasContainer(c)) {
- r = Response.created(uri).build();
- } else {
- r = Response.noContent().build();
- }
- MemoryStore.MS.createContainer(c);
- return r;
- }
由请求方法指示符修饰的方法必须返回void、Java编程语言的类型或javax.ws.rs.core.Response对象。可以使用PathParam或QueryParam注解来从URI中抽取多个参数,详细描述见第246页的“抽取request参数”。Java类型和实体段之间的转换是实体提供者(如MessageBodyReader和MessageBodyWriter)的责任。
想要在response中提供额外的元数据方法应该返回一个Response类的实例。ResponseBuilder类使用builder模式提供了一个创建Response实例的简便的方法。HTTP PUT和POST方法期望有HTTP request体,因此应该对响应PUT和POST请求的方法使用MessageBodyReader。
@PUT和@POST都能够被使用来创建和更新资源。POST可以表示任何事情,因此在使用POST时,由应用程序来定义其语义。PUT有定义好的语义。当使用PUT来创建的时候,客户声明了新创建的资源的URI。
PUT有很明确的语义来创建和更新一个资源。客户发送的表述必须是使用GET收到的同样的表述,只是给出不同的媒体类型。PUT不允许一个资源被部分更新,这是使用PUT方法时的一个很常见的错误。一个通用的应用程序模式是使用POST类创建资源并返回一个201响应,将新创建的资源的URI的值设置为location头。这种模式下,web service声明了新创建的资源的URI。
使用实体提供者来映射HTTP Response和Request实体段
实体提供者提供了在表述和相关的Java类型之间的映射服务。两种类型的实体提供者是MessageBodyReader和MessageBodyWriter。对于HTTP request,MessageBodyReader被用来将HTTP request实体段映射为方法参数。在response方面,使用MessageBodyWriter来将返回值映射为HTTP response实体段。如果应用程序需要提供附加的元数据,例如HTTP头或不同的状态码,方法可以返回一个包装了这些实体的Response实例,该实例可以用Response.ResponseBuilder创建。
下表显示了自动支持的标准的类型,只有当不使用这些标准类型的时候才需要编写实体提供者。
Java类型 | 支持的媒体类型 |
byte[] | 所有的媒体类型(*/*) |
java.lang.String | 所有的文本媒体类型(text/*) |
java.io.InputStream | 所有的媒体类型(*/*) |
java.io.Reader | 所有的媒体类型(*/*) |
java.io.File | 所有的媒体类型(*/*) |
javax.activation.DataSource | 所有的媒体类型(*/*) |
javax.xml.transform.Source | XML媒体类型(text/xml,application/xml和application/*+xml) |
javax.xml.bind.JAXBElement和应用程序提供的JAXB类 | XML媒体类型(text/xml,application/xml和application/*+xml) |
MultivaluedMap<String,String> | 表单内容(application/x-www-form-urlencoder) |
StreamingOutPut | 所有的媒体类型(*/*),只对MessageBodyWriter有效 |
- @Consumes("application/x-www-form-urlencoded")
- @Provider
- public class FormReader implements MessageBodyReader<NameValuePair> {
- @Produces("text/html")
- @Provider
- public class FormWriter implements
- MessageBodyWriter<Hashtable<String, String>> {
- @GET
- public Response getItem() {
- System.out.println("GET ITEM " + container + " " + item);
- Item i = MemoryStore.MS.getItem(container, item);
- if (i == null)
- throw new NotFoundException("Item not found");
- Date lastModified = i.getLastModified().getTime();
- EntityTag et = new EntityTag(i.getDigest());
- ResponseBuilder rb = request.evaluatePreconditions(lastModified, et);
- if (rb != null)
- return rb.build();
- byte[] b = MemoryStore.MS.getItemData(container, item);
- return Response.ok(b, i.getMimeType()).
- lastModified(lastModified).tag(et).build();
- }
发送给资源的信息和传回客户端的信息都被指定了MIME媒体类型,是在HTTP request或response的头中指定的。可以使用下面的注解来指定资源能够响应或生产的MIME媒体类型:
- javax.ws.rs.Consumes
- javax.ws.rs.Produces
@Produces注解
注解@Produces是用来指定一个资源能够产生并发送回客户端的MIME媒体类型或表现。如果@Produces是应用在类级别上,那么默认的资源的所有的方法都能够生产指定的MIME类型。如果是应用在方法级别上,那么这个注解会覆盖应用在类级别的@Produces注解。如果资源中没有方法能够生产客户端请求的MIME类型,那么JAX-RS运行时会发送回一个HTTP的“406 Not Acceptable”错误。
注解@Produces的值是一个MIME类型的字符串数组,例如
- @Produces({"image/jpeg,image/png"})
- @Path("/myResource")
- @Produces("text/plain")
- public class SomeResource {
- @GET
- public String doGetAsPlainText() {
- ...
- }
- @GET
- @Produces("text/html")
- public String doGetAsHtml() {
- ...
- }
- }
如果一个资源类能够生产超过一种MIME媒体类型,那么资源方法的选择就对应最能够接受的客户端声明的媒体类型。具体来说,HTTP请求的Accept头属性中声明了最可接受的MIME类型。例如,如果Accept头的值是Accept:text/plain,那么方法doGetAsPlainText会被调用。如果Accept头的值是Accept:text/plain;q=0.9,text/html,这声明了客户端能够接受媒体类型text/plain和test/html,但是更喜欢后者,因此doGetAsHtml方法会被调用。
一个@Produces注解中可以声明多个媒体类型。下面的代码例子显示了怎样做到这点:
- @Produces({"application/xml", "application/json"})
- public String 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 Media Type”)错误。
@Consumes注解的值是一个可接受的MIME类型的字符串数组。例如:
- @Consumes({"text/plain,text/html"})
- @Path("/myResource")
- @Consumes("multipart/related")
- public class SomeResource {
- @POST
- public String doPost(MimeMultipart mimeMultipartData) {
- ...
- }
- @POST
- @Consumes("application/x-www-form-urlencoded")
- public String doPost2(FormURLEncodedProperties formData) {
- ...
- }
- }
如果没有任何的资源方法能够响应请求中的MIME类型,那么一个HTTP 415错误会被返回给客户端。
本节中前面讨论的HelloWorld例子中,我们可以使用@Consume注解,如同我们下面的代码所做的
- @POST
- @Consumes("text/plain")
- public void postClichedMessage(String message) {
- // Store the message
- }
提取请求参数
资源方法的参数可能被添加了基于参数的注解来从请求中提取信息。在以前的例子中我们展现了时候@PathParam参数来从匹配@Path中声明的路径的request的URL中提取路径参数。
可以在资源类中提取下面类型的参数:
- Query
- URI Path
- Form
- Cookie
- Header
- Matrix
- @Path("smooth")
- @GET
- public Response smooth(
- @DefaultValue("2") @QueryParam("step") int step,
- @DefaultValue("true") @QueryParam("min-m") boolean hasMin,
- @DefaultValue("true") @QueryParam("max-m") boolean hasMax,
- @DefaultValue("true") @QueryParam("last-m") boolean hasLast,
- @DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
- @DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
- @DefaultValue("red") @QueryParam("last-color") ColorParam lastColor
- ) { ... }
用户自定义的Java编程语言类型也可以被用来作为query参数。下面的代码例子是上面的例子中使用的ColorParam类。
- public class ColorParam extends Color {
- public ColorParam(String s) {
- super(getRGB(s));
- }
- private static int getRGB(String s) {
- if (s.charAt(0) == ’#’) {
- try {
- Color c = Color.decode("0x" + s.substring(1));
- return c.getRGB();
- } catch (NumberFormatException e) {
- throw new WebApplicationException(400);
- }
- } else {
- try {
- Field f = Color.class.getField(s);
- return ((Color)f.get(null)).getRGB();
- } catch (Exception e) {
- throw new WebApplicationException(400);
- }
- }
- }
- }
@QueryParam和@PathParam都只能被应用到以下的Java类型:
- 除char之外的原始类型
- 除Character之外的原始类型封装类
- 有接受单独的字符串做参数的构造函数的类
- 包含名为valueOf(String)的静态方法的类
- List<T>,Set<T>,SortedSet<T>,这里T是上面所列的类型之一。有时候,同一个名字的参数可能包含多个值,在这种情况下,使用这些类型可以获取所有的值
URI路径参数是从request的URI中提取的,参数的名字对应类级别的@Path注解指定的URI路径模板中的变量名。在方法参数中使用javax.ws.rs.PathParam注解来指定URI路径参数。下面的例子显示了怎样使用@Path注解和在方法中使用@Path注解:
- @Path("/{username}")
- public class MyResourceBean {
- ...
- @GET
- public String printUsername(@PathParam("username") String userId) {
- ...
- }
- }
如果路径模板的变量无法被转换成指定的类型,JAX-RS运行时会返回一个HTTP 400(“Bad Request”)错误给客户端。如果@PathParam注解不能转换成指定的类型,JAX-RS运行时会返回一个HTTP 404(“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参数:
- @POST
- @Consumes("application/x-www-form-urlencoded")
- public void post(@FormParam("name") String name) {
- // Store the message
- }
- @GET
- public String get(@Context UriInfo ui) {
- MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
- MultivaluedMap<String, String> pathParams = ui.getPathParameters();
- }
- @GET
- public String get(@Context HttpHeaders hh) {
- MultivaluedMap<String, String> headerParams = ui.getRequestHeaders();
- Map<String, Cookie> pathParams = ui.getCookies();
- }
对form参数,我们可以进行以下的操作:
- @POST
- @Consumes("application/x-www-form-urlencoded")
- public void post(MultivaluedMap<String, String> formParams) {
- // Store the message
- }
这篇关于使用JAX-RS创建RESTful Web Service的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!