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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章