2.5 处理响应
REST的响应处理结果应包括响应头中HTTP状态码,响应实体中媒体参数类型和返回值类型,以及异常情况处理。JAX-RS2支持4种返回值类型的响应,分别是无返回值、返回Response类实例、返回GenericEntity类实例和返回自定义类实例。如下,逐一讲述这4种返回值类型。
阅读指南
本节示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-II/tree/master/2.simple-service-3。
相关包:com.example.response。
2.5.1 返回类型
1. void
在返回值类型是void的响应中,其响应实体为空,HTTP状态码为204。在前面的DELETE方法讲述中已经介绍过,再来看一下这种类型的资源方法。
@DELETE
@Path("{s}")
//关注点1:无返回值的DELETE方法
public void
delete(@PathParam("s") final String s) {
LOGGER.debug(s);
}
因为delete操作无须返回更多的关于资源表述的信息,因此该方法没有返回值,即返回值类型为void,见关注点1。
2. Response
在返回值类型为Response的响应中,响应实体为Response类的entity()方法定义的实体类实例。如果该内容为空,则HTTP状态码为204,否则HTTP状态码为200 OK,示例代码如下。
@POST
@Path("c")
public Response get(final String s) {
LOGGER.debug(s);
//Response.noContent().build();
//关注点1:构建无返回值的响应实例
return Response.ok().entity("char[]:" + s).build();
}
在这段代码中,Response首先定义了HTTP的状态码为ok,然后填充实体信息,最后调用build()方法构建Response实例,见关注点1。
3. GenericEntity
通用实体类型作为返回值的情况并不常用。其形式是构造一个统一的实体实例并将其返回,实体实例作为第一个参数、该实体类型作为第二个参数,示例代码如下。
@POST
@Path("b")
public String get(final byte[] bs) {
for (final byte b : bs) {
LOGGER.debug(b);
}
return "byte[]:" + new String(bs);
}
/*
public GenericEntity<String>
get(final byte[] bs) {
for (final byte b : bs) {
LOGGER.debug(b);
}
//关注点1:构建GenericEntity实例
return new GenericEntity<String>("byte[]:" + new
String(bs), String.class);
}
*/
在这段代码中,GenericEntity的第一个是由byte数组实例作为参数构建的字符串实例,第二个参数是字符串类,见关注点1。
4. 自定义类型
JDK中的类(比如File、String等)都可以作为返回值类型,更常用的是返回自定义的POJO类型,前述多个例子就是这样做的,再来看一个示例。
@POST
@Path("f")
//关注点1:GET方法的返回类型为File
public File get(final File f) throws
FileNotFoundException, IOException {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
String s;
do {
s = br.readLine();
LOGGER.debug(s);
} while (s != null);
return f;
}
}
@POST
@Consumes({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@Produces(MediaType.APPLICATION_XML)
//关注点2:POST方法的返回值是自定义类Book
public Book getEntity(Book book) {
LOGGER.debug(book.getBookName());
return book;
}
@POST
@Consumes({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
@Produces(MediaType.APPLICATION_XML)
//关注点3:POST方法的返回值是自定义类Book
public Book
getEntity(JAXBElement<Book> bookElement) {
Book book = bookElement.getValue();
LOGGER.debug(book.getBookName());
return book;
}
在这段代码中,返回值类型有来自JDK的File类型,见关注点1,也有自定义的POJO类型,见关注点2和关注点3。
2.5.2 处理异常
实现REST的资源方法时应使其具有良好的异常处理能力,这包括异常的定义和错误状态码的正确返回。
1. 处理状态码
首先通过表2-7了解下REST中常用的HTTP状态码,应当在处理异常的同时,为REST请求的客户端提供对应的错误码。
表2-7 HTTP常用状态码列表
状态码 含 义
200 OK 服务器正常响应
201 Created 创建新实体,响应头Location指定访问该实体的URL
202 Accepted 服务器接受请求,处理尚未完成。可用于异步处理机制
204 No Content 服务器正常响应,但响应实体为空
301 Moved Permanently 请求资源的地址发生永久变动,响应头Location指定新的URL
302 Found 请求资源的地址发生临时变动
304 Not Modified 客户端缓存资源依然有效
400 Bad Request 请求信息出现语法错误
401 Unauthorized 请求资源无法授权给未验证用户
403 Forbidden 请求资源未授权当前用户
404 Not Found 请求资源不存在
405 Method Not Allowed 请求方法不匹配
406 Not Acceptable 请求资源的媒体类型不匹配
500 Internal Server Error 服务器内部错误,意外终止响应
501 Not Implemented 服务器不支持当前请求
JAX-RS2规定的REST式的Web服务的基本异常类型为运行时异常WebApplicationException类。该类包含3个主要的子类分别对应如下内容。
HTTP状态码为3xx的重定向类RedirectionException;
HTTP状态码为4xx的请求错误类ClientErrorException;
HTTP状态码为5xx的服务器错误类ServerErrorException。
它们各自的子类对照HTTP状态码再细分,比如常见的HTTP状态码404错误,对应的错误类为NotFoundException,如图2-4所示。
除了Jersey提供的标准异常类型,我们也可以根据业务需要自定义相关的业务异常类,示例如下。
//关注点1:定义WebApplicationException接口实现类
public class Jaxrs2GuideNotFoundException
extends WebApplicationException {
public Jaxrs2GuideNotFoundException() {
//关注点2:定义HTTP状态
super(javax.ws.rs.core.Response.Status.NOT_FOUND);
}
public Jaxrs2GuideNotFoundException(String message) {
super(message);
}
}
在这段代码中,Jaxrs2GuideNotFoundException类继承自JAX-RS2的WebApplication-Exception类,见关注点1。其默认构造子提供了HTTP状态码,其值为Response.Status.NOT_FOUND,见关注点2。
图2-4 Jersey定义的异常类型
2. ExceptionMapper
Jersey框架为我们提供了更为通用的异常处理方式。通过实现ExceptionMapper接口并使用@Provider注解将其定义为一个Provider,可以实现通用的异常的面向切面处理,而非针对某一个资源方法的异常处理,示例如下。
@Provider
public class EntityNotFoundMapper
implements
ExceptionMapper<Jaxrs2GuideNotFoundException>{
//关注点1:定义ExceptionMapper接口实现类
@Override
public Response toResponse(final Jaxrs2GuideNotFoundException ex) {
//关注点2:拦截并返回新的响应实例
return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}
}
在这段代码中,EntityNotFoundMapper实现了ExceptionMapper接口,并提供了泛型类型为前述刚定义的Jaxrs2GuideNotFoundException类,见关注点1;当响应中发生了Jaxrs2GuideNotFoundException类型的异常,响应流程就会被拦截并补充HTTP状态码和异常消息,以文本作为媒体格式返回给客户端,见关注点2。