200105-SpringBoot 系列 web 篇之自定義返回 Http Code 的 n 種姿式
雖然 http 的提供了一整套完整、定義明確的狀態碼,但實際的業務支持中,後端並不總會遵照這套規則,更多的是在返回結果中,加一個 code 字段來自定義業務狀態,即使是後端 5xx 了,返回給前端的 http code 依然是 200前端
那麼若是我想遵照 http 的規範,不一樣的 case 返回不一樣的 http code 在 Spring 中能夠作呢?java
本文將介紹四種設置返回的 HTTP CODE 的方式git
@ResponseStatus
註解方式HttpServletResponse#sendError
HttpServletResponse#setStatus
ResponseEntity
<!-- more -->github
進入正文以前,先建立一個 SpringBoot 項目,本文示例全部版本爲 spring-boot.2.1.2.RELEASE
web
(須要測試的小夥伴,本機建立一個 maven 項目,在pom.xml
文件中,拷貝下面的配置便可)spring
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement> </build> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/libs-snapshot-local</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone-local</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/libs-release-local</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
下面全部的方法都放在 ErrorCodeRest 這個類中json
@RestController @RequestMapping(path = "code") public class ErrorCodeRest { }
經過註解@ResponseStatus
,來指定返回的 http code, 通常來講,使用它有兩種姿式,一個是直接加在方法上,一個是加在異常類上後端
直接在方法上添加註解,並制定對應的 code數組
/** * 註解方式,只支持標準http狀態碼 * * @return */ @GetMapping("ano") @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "請求參數異常!") public String ano() { return "{\"code\": 400, \"msg\": \"bad request!\"}"; }
實測一下,返回結果以下springboot
➜ ~ curl 'http://127.0.0.1:8080/code/ano' -i HTTP/1.1 400 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 05 Jan 2020 01:29:04 GMT Connection: close {"timestamp":"2020-01-05T01:29:04.673+0000","status":400,"error":"Bad Request","message":"請求參數異常!","path":"/code/ano"}%
當咱們發起請求時,返回的狀態碼爲 400,返回的數據爲 springboot 默認的錯誤信息格式
雖然上面這種使用姿式能夠設置 http code,可是這種使用姿式有什麼意義呢?
若是看過 web 系列教程中的:SpringBoot 系列教程 web 篇之全局異常處理 可能就會有一些映象,配合@ExceptionHandler
來根據異常返回對應的狀態碼
一個推薦的使用姿式,下面表示當你的業務邏輯中出現數組越界時,返回 500 的狀態碼以及完整的堆棧信息
@ResponseBody @ExceptionHandler(value = ArrayIndexOutOfBoundsException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String handleArrayIndexOutBounds(HttpServletRequest request, HttpServletResponse response, ArrayIndexOutOfBoundsException e) throws IOException { log.info("array index out conf!"); return "aryIndexOutOfBounds: " + getThrowableStackInfo(e); }
另一種使用姿式就是直接裝飾在異常類上,而後當你的業務代碼中,拋出特定的異常類,返回的 httpcode 就會設置爲註解中的值
/** * 異常類 + 註解方式,只支持標準http狀態碼 * * @return */ @GetMapping("exception/500") public String serverException() { throw new ServerException("內部異常哦"); } @GetMapping("exception/400") public String clientException() { throw new ClientException("客戶端異常哦"); } @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "服務器失聯了,請到月球上呼叫試試~~") public static class ServerException extends RuntimeException { public ServerException(String message) { super(message); } } @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "老哥,你的請求有問題~~") public static class ClientException extends RuntimeException { public ClientException(String message) { super(message); } }
測試結果以下,在異常類上添加註解的方式,優勢在於不須要配合@ExceptionHandler
寫額外的邏輯了;缺點則在於須要定義不少的自定義異常類型
➜ ~ curl 'http://127.0.0.1:8080/code/exception/400' -i HTTP/1.1 400 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 05 Jan 2020 01:37:07 GMT Connection: close {"timestamp":"2020-01-05T01:37:07.662+0000","status":400,"error":"Bad Request","message":"老哥,你的請求有問題~~","path":"/code/exception/400"}% ➜ ~ curl 'http://127.0.0.1:8080/code/exception/500' -i HTTP/1.1 500 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 05 Jan 2020 01:37:09 GMT Connection: close {"timestamp":"2020-01-05T01:37:09.389+0000","status":500,"error":"Internal Server Error","message":"服務器失聯了,請到月球上呼叫試試~~","path":"/code/exception/500"}%
注意
org.springframework.http.HttpStatus
)這種使用姿式就比較簡單了,方法的返回結果必須是ResponseEntity
,下面給出兩個實際的 case
@GetMapping("401") public ResponseEntity<String> _401() { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{\"code\": 401, \"msg\": \"未受權!\"}"); } @GetMapping("451") public ResponseEntity<String> _451() { return ResponseEntity.status(451).body("{\"code\": 451, \"msg\": \"自定義異常!\"}"); }
實測結果
➜ ~ curl 'http://127.0.0.1:8080/code/401' -i HTTP/1.1 401 Content-Type: text/plain;charset=UTF-8 Content-Length: 34 Date: Sun, 05 Jan 2020 01:40:10 GMT {"code": 401, "msg": "未受權!"} ➜ ~ curl 'http://127.0.0.1:8080/code/451' -i HTTP/1.1 451 Content-Type: text/plain;charset=UTF-8 Content-Length: 40 Date: Sun, 05 Jan 2020 01:40:19 GMT {"code": 451, "msg": "自定義異常!"}
從上面的使用實例上看,能夠知道這種使用方式,不單單支持標準的 http code,也支持自定義的 code(如返回 code 451)
這種使用姿式則是直接操做HttpServletResponse
對象,手動錄入返回的結果
/** * response.setStatus 支持自定義http code,並能夠返回結果 * * @param response * @return */ @GetMapping("525") public String _525(HttpServletResponse response) { response.setStatus(525); return "{\"code\": 525, \"msg\": \"自定義錯誤碼 525!\"}"; }
輸出結果
➜ ~ curl 'http://127.0.0.1:8080/code/525' -i HTTP/1.1 525 Content-Type: text/plain;charset=UTF-8 Content-Length: 47 Date: Sun, 05 Jan 2020 01:45:38 GMT {"code": 525, "msg": "自定義錯誤碼 525!"}%
使用方式比較簡單,直接設置 status 便可,支持自定義的 Http Code 返回
使用這種姿式的時候須要注意一下,只支持標準的 http code,並且 response body 中不會有你的業務返回數據,如
/** * send error 方式,只支持標準http狀態碼; 且不會帶上返回的結果 * * @param response * @return * @throws IOException */ @GetMapping("410") public String _410(HttpServletResponse response) throws IOException { response.sendError(410, "send 410"); return "{\"code\": 410, \"msg\": \"Gone 410!\"}"; } @GetMapping("460") public String _460(HttpServletResponse response) throws IOException { response.sendError(460, "send 460"); return "{\"code\": 460, \"msg\": \"Gone 460!\"}"; }
輸出結果
➜ ~ curl 'http://127.0.0.1:8080/code/410' -i HTTP/1.1 410 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 05 Jan 2020 01:47:52 GMT {"timestamp":"2020-01-05T01:47:52.300+0000","status":410,"error":"Gone","message":"send 410","path":"/code/410"}% ➜ ~ curl 'http://127.0.0.1:8080/code/460' -i HTTP/1.1 500 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 05 Jan 2020 01:47:54 GMT Connection: close {"timestamp":"2020-01-05T01:47:54.719+0000","status":460,"error":"Http Status 460","message":"send 460","path":"/code/460"}%
從上面的 case 也能夠看出,當咱們使用 send error 時,若是是標準的 http code,會設置對響應頭;若是是自定義的不被識別的 code,那麼返回的 http code 是 500
上面介紹了幾種常見的設置響應 http code 的姿式,下面小結一下使用時的注意事項
ResponseStatus
裝飾自定義異常類,使用時拋出對應的異常類,從而達到設置響應 code 的效果
@ExceptionHandler
,用來裝飾方法ResponseEntity
形如:
return ResponseEntity.status(451).body("{\"code\": 451, \"msg\": \"自定義異常!\"}");
HttpServletResponse
項目源碼
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛