JAX-RS入门 八: HTTP响应

首先先看一下HTTP中定义的响应码及其意义:

响应码
含义
100
继续
101
分组交换协议
200
OK
201
被创建
202
被采纳
203
非授权信息
204
无内容
205
重置内容
206
部分内容
300
多选项
301
永久地传递
302
找到
303
参见其他
304
未改动
305
使用代理
307
暂时重定向
400
错误请求
401
未授权
402
要求付费
403
禁止
404
未找到
405
不允许的方法
406
不被采纳
407
要求代理授权
408
请求超时
409
冲突
410
过期的
411
要求的长度
412
前提不成立
413
请求实例太大
414
请求URL太大
415
不支持的媒体类型
416
无法满足的请求范围
417
失败的预期
500
内部错误
501
未被使用
502
网关错误
503
不可用的服务
504
网关超时
505
HTTP版本未被支持

一、成功

从 200 到 399 为成功码,表示请求处理成功。

如果方法返回值不为null,则返回码是 200;如果返回值为 null 或者为 void,则返回码为 204,表示无内容。

二、错误

从 400 到 599 表示处理错误。

例如 404表示网页未找着;如果请求的期望的返回交换类型不对,则返回 406,表示不可接爱;如果请求的方法未找着,则返回 405,表示方法不允许,这个返回结果对于HEAD和OPTIONS请求方法例外,对于HEAD会试图去查找能处理相同URI的GET方法;对于OPTION,会返回一些自动生成的信息。

三、复杂的响应

对于不能简单处理的返回信息,则可以返回javax.ws.rs.core.Response对象:

Java代码 复制代码  收藏代码
  1. public abstract class Response {   
  2.   
  3.     public abstract Object getEntity();   
  4.     public abstract int getStatus();   
  5.     public abstract MultivaluedMap<String, Object> getMetadata();   
  6.     ...   
  7.   
  8. }   

Response对象不能直接创建,需要通过javax.ws.rs.core.Response.ResponseBuilder来创建:

Java代码 复制代码  收藏代码
  1. public abstract class Response {   
  2.   
  3.     ...   
  4.     public static ResponseBuilder status(Status status) {...}   
  5.     public static ResponseBuilder status(int status) {...}   
  6.     public static ResponseBuilder ok() {...}   
  7.     public static ResponseBuilder ok(Object entity) {...}   
  8.     public static ResponseBuilder ok(Object entity, MediaType type) {...}   
  9.     public static ResponseBuilder ok(Object entity, String type) {...}   
  10.     public static ResponseBuilder ok(Object entity, Variant var) {...}   
  11.     public static ResponseBuilder serverError() {...}   
  12.     public static ResponseBuilder created(URI location) {...}   
  13.     public static ResponseBuilder noContent() {...}   
  14.     public static ResponseBuilder notModified() {...}   
  15.     public static ResponseBuilder notModified(EntityTag tag) {...}   
  16.     public static ResponseBuilder notModified(String tag) {...}   
  17.     public static ResponseBuilder seeOther(URI location) {...}   
  18.     public static ResponseBuilder temporaryRedirect(URI location) {...}   
  19.     public static ResponseBuilder notAcceptable(List<Variant> variants) {...}   
  20.     public static ResponseBuilder fromResponse(Response response) {...}   
  21.     ...   
  22.   
  23. }   

ResponseBuilder是一个用来创建单个Response实例的工厂类, 首先将要创建的response对象的状态存起来,最后当状态设置完成了,就使用builder去初始化Response:

Java代码 复制代码  收藏代码
  1. public static abstract class ResponseBuilder {   
  2.   
  3.     public abstract Response build();   
  4.   
  5.     public abstract ResponseBuilder clone();   
  6.     public abstract ResponseBuilder status(int status);   
  7.   
  8.     public ResponseBuilder status(Status status) {...}   
  9.   
  10.     public abstract ResponseBuilder entity(Object entity);   
  11.     public abstract ResponseBuilder type(MediaType type);   
  12.     public abstract ResponseBuilder type(String type);   
  13.     public abstract ResponseBuilder variant(Variant variant);   
  14.     public abstract ResponseBuilder variants(List<Variant> variants);   
  15.     public abstract ResponseBuilder language(String language);   
  16.     public abstract ResponseBuilder language(Locale language);   
  17.     public abstract ResponseBuilder location(URI location);   
  18.     public abstract ResponseBuilder contentLocation(URI location);   
  19.     public abstract ResponseBuilder tag(EntityTag tag);   
  20.     public abstract ResponseBuilder tag(String tag);   
  21.     public abstract ResponseBuilder lastModified(Date lastModified);   
  22.     public abstract ResponseBuilder cacheControl(CacheControl cacheControl);   
  23.     public abstract ResponseBuilder expires(Date expires);   
  24.     public abstract ResponseBuilder header(String name, Object value);   
  25.     public abstract ResponseBuilder cookie(NewCookie... cookies);   
  26. }   

例如:

Java代码 复制代码  收藏代码
  1. @Path("/textbook")   
  2. public class TextBookService {   
  3.   
  4.     @GET  
  5.     @Path("/restfuljava")   
  6.     @Produces("text/plain")   
  7.     public Response getBook() {   
  8.         String book = ...;   
  9.         ResponseBuilder builder = Response.ok(book);   
  10.         builder.language("fr").header("Some-Header""some value");   
  11.         return builder.build();   
  12.     }   
  13.   
  14. }   

四、Cookie

JAX-RS使用了一个简单的类去表示一个cookie值,它就是javax.ws.rs.core.NewCookie:

Java代码 复制代码  收藏代码
  1. public class NewCookie extends Cookie {   
  2.   
  3.     public static final int DEFAULT_MAX_AGE = −1;   
  4.     public NewCookie(String name, String value) {}   
  5.     public NewCookie(String name, String value, String path,   
  6.     String domain, String comment,   
  7.     int maxAge, boolean secure) {}   
  8.     public NewCookie(String name, String value, String path,   
  9.             String domain, int version, String comment,   
  10.     int maxAge, boolean secure) {}   
  11.     public NewCookie(Cookie cookie) {}   
  12.     public NewCookie(Cookie cookie, String comment,   
  13.     int maxAge, boolean secure) {}   
  14.     public static NewCookie valueOf(String value)   
  15.             throws IllegalArgumentException {}   
  16.     public String getComment() {}   
  17.     public int getMaxAge() {}   
  18.     public boolean isSecure() {}   
  19.     public Cookie toCookie() {}   
  20.   
  21. }  

要返回Cookie,只需要传入它到Response中:

Java代码 复制代码  收藏代码
  1. @GET  
  2. public Response get() {   
  3.     NewCookie cookie = new NewCookie("key", "value);   
  4.     ResponseBuilder builder = Response.ok("hello""text/plain");   
  5. return builder.cookies(cookie).build();   
  6. }   

五、状态类别

除了直接写数据外,JAX-RS定义了一个状态值的枚举类别:

Java代码 复制代码  收藏代码
  1. public enum Status {   
  2.     OK(200"OK"),   
  3.     CREATED(201"Created"),   
  4.     ACCEPTED(202"Accepted"),   
  5.     NO_CONTENT(204"No Content"),   
  6.     MOVED_PERMANENTLY(301"Moved Permanently"),   
  7.     SEE_OTHER(303"See Other"),   
  8.     NOT_MODIFIED(304"Not Modified"),   
  9.     TEMPORARY_REDIRECT(307"Temporary Redirect"),   
  10.     BAD_REQUEST(400"Bad Request"),   
  11.     UNAUTHORIZED(401"Unauthorized"),   
  12.     FORBIDDEN(403"Forbidden"),   
  13.     NOT_FOUND(404"Not Found"),   
  14.     NOT_ACCEPTABLE(406"Not Acceptable"),   
  15.     CONFLICT(409"Conflict"),   
  16.     GONE(410"Gone"),   
  17.     PRECONDITION_FAILED(412"Precondition Failed"),   
  18.     UNSUPPORTED_MEDIA_TYPE(415"Unsupported Media Type"),   
  19.     INTERNAL_SERVER_ERROR(500"Internal Server Error"),   
  20.     SERVICE_UNAVAILABLE(503"Service Unavailable");   
  21.   
  22.     public enum Family {   
  23.         INFORMATIONAL, SUCCESSFUL, REDIRECTION,   
  24.         CLIENT_ERROR, SERVER_ERROR, OTHER   
  25.     }   
  26.   
  27.     public Family getFamily()   
  28.     public int getStatusCode()   
  29.     public static Status fromStatusCode(final int statusCode)   
  30. }   

每个Status的值都关联的到一个特定HTTP的返回值族,这个族由Status.Family标识。例如 100范围的值被认识是信息性的;200范围的是成功;300范围的也是成功,但是被重定向的;400是client错误;500是服务器错误。

Response.status()和ResponseBuilder.status()都接受一个Status值,例如:

    

Java代码 复制代码  收藏代码
  1. @DELETE  
  2. Response delete() {   
  3.   
  4.     ...   
  5.     return Response.status(Status.GONE).build();   
  6.   
  7. }   

六、javax.ws.rs.core.GenericEntity

当处理Response的返回对象(entity)时,如果是一个类似于MessageBodyWriter这样的支持泛型的对象,则问题来了: isWriteable()方法需要有泛弄的信息。然后不幸的是java的泛型信息只存在编译时,不存在运行时,因此没有一个简单的方法可以得到在运行时得到泛型的信息,因此MessageBodyWriter不知道如何输出对象。

为了解决这个问题,JAX-RS提供了一个帮助类 javax.ws.rs.core.GenericEntity 。例如:

Java代码 复制代码  收藏代码
  1. @GET  
  2. @Produces("application/xml")   
  3. public Response getCustomerList() {   
  4.     List<Customer> list = new ArrayList<Customer>();   
  5.     list.add(new Customer(...));   
  6.     GenericEntity entity = new GenericEntity<List<Customer>>(list){};   
  7.     return Response.ok(entity);   
  8. }  

GenericEntity也是一个泛型模板,只需要将输出entity的泛型信息加到它上,并且把对象做为一个参数传入即可。

七、javax.ws.rs.WebApplicationException

WebApplicationException是一个内置、非检测异常,支持传入Response对象或者状态码:

Java代码 复制代码  收藏代码
  1. public class WebApplicationException extends RuntimeException {   
  2.   
  3.     public WebApplicationException() {...}   
  4.     public WebApplicationException(Response response) {...}   
  5.     public WebApplicationException(int status) {...}   
  6.     public WebApplicationException(Response.Status status) {...}   
  7.     public WebApplicationException(Throwable cause) {...}   
  8.     public WebApplicationException(Throwable cause,   
  9.                             Response response) {...}   
  10.     public WebApplicationException(Throwable cause, int status) {...}   
  11.     public WebApplicationException(Throwable cause,   
  12.     Response.Status status) {...}   
  13.     public Response getResponse() {...]   
  14. }   

当JAX-RS碰到一个WebApplicationException抛出时,它就捕获这个异常,调用它的getResponse()方法去获取Response,发回给client端。 如果应用以一个状态码或者Response初始化了WebApplicationException,则这个状态码或者Response将被用来创建真正的HTTP响应;或者,会直接返回 500, “Internal Server Error“给客户端,例如:

Java代码 复制代码  收藏代码
  1. @Path("/customers")   
  2. public class CustomerResource {   
  3.   
  4.     @GET  
  5.     @Path("{id}")   
  6.     @Produces("application/xml")   
  7.     public Customer getCustomer(@PathParam("id"int id) {   
  8.         Customer cust = findCustomer(id);   
  9.         if (cust == null) {   
  10.             throw new WebApplicationException(Response.Status.NOT_FOUND);   
  11.         }   
  12.         return cust;   
  13.     }   
  14. }  

这里如果没找着客户,会返回404错误

八、错误匹配

应用中可能有各种各样的,来自应用或者第三方包的异常,如果仅依赖于容器提供的错误处理方式,则可能灵活度不够: 捕获这些异常,然后包装到WebApplicationException中会让人觉得相当乏味。

这里的另外一种选择就是使用javax.ws.rs.ext.ExceptionMapper,这个对象知道怎么匹配一个抛出的异常到一个Repsonse对象上:

Java代码 复制代码  收藏代码
  1. public interface ExceptionMapper<E extends Throwable> {   
  2.     Response toResponse(E exception);   
  3. }  

例如对于JPA有EntityNotFoundException:

Java代码 复制代码  收藏代码
  1. @Provider  
  2. public class EntityNotFoundMapper   
  3. implements ExceptionMapper<EntityNotFoundException> {   
  4.        
  5.       public Response toResponse(EntityNotFoundException e) {   
  6.            return Response.status(Response.Status.NOT_FOUND).build();   
  7.       }   
  8. }  

注: ExceptionMapper的实现,必须加上@Provider注释

  

ExceptionMapper也支持异常层级关系,例如A 继承 B,如果找不到A的mapper,则会向上查找B的mapper。

最后ExceptionMapper使用JAX-RS的deployment API进行注册,可以用Application.

猜你喜欢

转载自zzc1684.iteye.com/blog/1674381