雖然 http 的提供了一整套完整、定義明確的狀態(tài)碼,但實際的業(yè)務支持中,后端并不總會遵守這套規(guī)則,更多的是在返回結果中,加一個 code 字段來自定義業(yè)務狀態(tài),即便是后端 5xx 了,返回給前端的 http code 依然是 200
那么如果我想遵守 http 的規(guī)范,不同的 case 返回不同的 http code 在 Spring 中可以做呢?
本文將介紹四種設置返回的 HTTP CODE 的方式
-
@ResponseStatus
?注解方式 -
HttpServletResponse#sendError
-
HttpServletResponse#setStatus
-
ResponseEntity
I. 返回 Http Code 的 n 種姿勢
0. 環(huán)境
進入正文之前,先創(chuàng)建一個 SpringBoot 項目,本文示例所有版本為?spring-boot.2.1.2.RELEASE
(需要測試的小伙伴,本機創(chuàng)建一個 maven 項目,在pom.xml
文件中,拷貝下面的配置即可)
<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 這個類中
@RestController
@RequestMapping(path = "code")
publicclass ErrorCodeRest {
}
1. ResponseStatus 使用姿勢
通過注解@ResponseStatus
,來指定返回的 http code, 一般來說,使用它有兩種姿勢,一個是直接加在方法上,一個是加在異常類上
a. 裝飾方法
直接在方法上添加注解,并制定對應的 code
/**
* 注解方式,只支持標準http狀態(tài)碼
*
* @return
*/
@GetMapping("ano")
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "請求參數(shù)異常!")
public String ano() {
return"{\"code\": 400, \"msg\": \"bad request!\"}";
}
實測一下,返回結果如下
? ~ 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":"請求參數(shù)異常!","path":"/code/ano"}%
當我們發(fā)起請求時,返回的狀態(tài)碼為 400,返回的數(shù)據(jù)為 springboot 默認的錯誤信息格式
雖然上面這種使用姿勢可以設置 http code,但是這種使用姿勢有什么意義呢?
如果看過 web 系列教程中的:SpringBoot 系列教程 web 篇之全局異常處理?可能就會有一些映象,配合@ExceptionHandler
來根據(jù)異常返回對應的狀態(tài)碼
一個推薦的使用姿勢,下面表示當你的業(yè)務邏輯中出現(xiàn)數(shù)組越界時,返回 500 的狀態(tài)碼以及完整的堆棧信息
@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);
}
b. 裝飾異常類
另外一種使用姿勢就是直接裝飾在異常類上,然后當你的業(yè)務代碼中,拋出特定的異常類,返回的 httpcode 就會設置為注解中的值
/**
* 異常類 + 注解方式,只支持標準http狀態(tài)碼
*
* @return
*/
@GetMapping("exception/500")
public String serverException() {
thrownew ServerException("內(nèi)部異常哦");
}
@GetMapping("exception/400")
public String clientException() {
thrownew ClientException("客戶端異常哦");
}
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "服務器失聯(lián)了,請到月球上呼叫試試~~")
publicstaticclass ServerException extends RuntimeException {
public ServerException(String message) {
super(message);
}
}
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "老哥,你的請求有問題~~")
publicstaticclass ClientException extends RuntimeException {
public ClientException(String message) {
super(message);
}
}
測試結果如下,在異常類上添加注解的方式,優(yōu)點在于不需要配合@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":"服務器失聯(lián)了,請到月球上呼叫試試~~","path":"/code/exception/500"}%
注意
-
ResponseStatus 注解的使用姿勢,只支持標準的 Http Code(必須是枚舉類
org.springframework.http.HttpStatus
)
?
2. ResponseEntity
這種使用姿勢就比較簡單了,方法的返回結果必須是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)
3. HttpServletResponse
這種使用姿勢則是直接操作HttpServletResponse
對象,手動錄入返回的結果
a. setStatus
/**
* 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 返回
b. sendError
使用這種姿勢的時候需要注意一下,只支持標準的 http code,而且 response body 中不會有你的業(yè)務返回數(shù)據(jù),如
/**
* send error 方式,只支持標準http狀態(tài)碼; 且不會帶上返回的結果
*
* @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
4, 小結
上面介紹了幾種常見的設置響應 http code 的姿勢,下面小結一下使用時的注意事項
ResponseStatus
-
只支持標準的 http code
-
裝飾自定義異常類,使用時拋出對應的異常類,從而達到設置響應 code 的效果
-
缺點對非可控的異常類不可用
-
-
結合
@ExceptionHandler
,用來裝飾方法
ResponseEntity
形如:
return ResponseEntity.status(451).body("{\"code\": 451, \"msg\": \"自定義異常!\"}");
-
我個人感覺是最強大的使用姿勢,就是寫起來沒有那么簡潔
-
支持自定義 code,支持設置 response body
HttpServletResponse
-
setStatus: 設置響應 code,支持自定義 code,支持返回 response body文章來源:http://www.zghlxwxcb.cn/news/detail-848657.html
-
sendError: 只支持標準的 http code,如果傳入自定義的 code,返回的 http code 會是 50文章來源地址http://www.zghlxwxcb.cn/news/detail-848657.html
到了這里,關于SpringBoot 系列 web 篇之自定義返回 Http Code 的 n 種姿勢的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!