虽然http的提供了一整套完整、定义明确的状态码,但实际的业务支持中,后端并不总会遵守这套规则,更多的是在返回结果中,加一个code字段来自定义业务状态,即便是后端5xx了,返回给前端的http code依然是200
那么如果我想遵守http的规范,不同的case返回不同的http code在Spring中可以做呢?
本文将介绍四种设置返回的HTTP CODE的方式
@ResponseStatus 注解方式
HttpServletResponse#sendError
HttpServletResponse#setStatus
ResponseEntity
I. 返回Http Code的n中姿势
0. 环境
进入正文之前,先创建一个SpringBoot项目,本文示例所有版本为 spring-boot.2.1.2.RELEASE
(需要测试的小伙伴,本机创建一个maven项目,在pom.xml文件中,拷贝下面的配置即可)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> </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这个类中
1 2 3 4
| @RestController @RequestMapping(path = "code") public class ErrorCodeRest { }
|
1. ResponseStatus使用姿势
通过注解@ResponseStatus,来指定返回的http code, 一般来说,使用它有两种姿势,一个是直接加在方法上,一个是加在异常类上
a. 装饰方法
直接在方法上添加注解,并制定对应的code
1 2 3 4 5 6 7 8 9 10
|
@GetMapping("ano") @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "请求参数异常!") public String ano() { return "{\"code\": 400, \"msg\": \"bad request!\"}"; }
|
实测一下,返回结果如下
1 2 3 4 5 6 7 8
| ➜ ~ 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的状态码以及完整的堆栈信息
1 2 3 4 5 6 7 8
| @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. 装饰异常类
另外一种使用姿势就是直接装饰在异常类上,然后当你的业务代码中,抛出特定的异常类,返回的httpcode就会设置为注解中的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
@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写额外的逻辑了;缺点则在于需要定义很多的自定义异常类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ➜ ~ 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"}%
|
注意
- ResponseStatus注解的使用姿势,只支持标准的Http Code(必须是枚举类
org.springframework.http.HttpStatus)
2. ResponseEntity
这种使用姿势就比较简单了,方法的返回结果必须是ResponseEntity,下面给出两个实际的case
1 2 3 4 5 6 7 8 9
| @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\": \"自定义异常!\"}"); }
|
实测结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ➜ ~ 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
1 2 3 4 5 6 7 8 9 10 11
|
@GetMapping("525") public String _525(HttpServletResponse response) { response.setStatus(525); return "{\"code\": 525, \"msg\": \"自定义错误码 525!\"}"; }
|
输出结果
1 2 3 4 5 6 7
| ➜ ~ 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中不会有你的业务返回数据,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@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!\"}"; }
|
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ➜ ~ 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
形如:
1
| return ResponseEntity.status(451).body("{\"code\": 451, \"msg\": \"自定义异常!\"}");
|
- 我个人感觉是最强大的使用姿势,就是写起来没有那么简洁
- 支持自定义code,支持设置 response body
HttpServletResponse
- setStatus: 设置响应code,支持自定义code,支持返回response body
- sendError: 只支持标准的http code,如果传入自定义的code,返回的http code会是500
II. 其他
web系列博文
项目源码
1. 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

打赏
如果觉得我的文章对您有帮助,请随意打赏。
微信打赏
支付宝打赏