当前位置:   article > 正文

SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

虽然 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. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.2.1.RELEASE</version>
  5. <relativePath/><!-- lookup parent from repository -->
  6. </parent>
  7. <properties>
  8. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  9. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  10. <java.version>1.8</java.version>
  11. </properties>
  12. <dependencies>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-web</artifactId>
  16. </dependency>
  17. </dependencies>
  18. <build>
  19. <pluginManagement>
  20. <plugins>
  21. <plugin>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-maven-plugin</artifactId>
  24. </plugin>
  25. </plugins>
  26. </pluginManagement>
  27. </build>
  28. <repositories>
  29. <repository>
  30. <id>spring-snapshots</id>
  31. <name>Spring Snapshots</name>
  32. <url>https://repo.spring.io/libs-snapshot-local</url>
  33. <snapshots>
  34. <enabled>true</enabled>
  35. </snapshots>
  36. </repository>
  37. <repository>
  38. <id>spring-milestones</id>
  39. <name>Spring Milestones</name>
  40. <url>https://repo.spring.io/libs-milestone-local</url>
  41. <snapshots>
  42. <enabled>false</enabled>
  43. </snapshots>
  44. </repository>
  45. <repository>
  46. <id>spring-releases</id>
  47. <name>Spring Releases</name>
  48. <url>https://repo.spring.io/libs-release-local</url>
  49. <snapshots>
  50. <enabled>false</enabled>
  51. </snapshots>
  52. </repository>
  53. </repositories>

下面所有的方法都放在 ErrorCodeRest 这个类中

  1. @RestController
  2. @RequestMapping(path = "code")
  3. publicclass ErrorCodeRest {
  4. }

1. ResponseStatus 使用姿势

通过注解@ResponseStatus,来指定返回的 http code, 一般来说,使用它有两种姿势,一个是直接加在方法上,一个是加在异常类上

a. 装饰方法

直接在方法上添加注解,并制定对应的 code

  1. /**
  2. * 注解方式,只支持标准http状态码
  3. *
  4. * @return
  5. */
  6. @GetMapping("ano")
  7. @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "请求参数异常!")
  8. public String ano() {
  9. return"{\"code\": 400, \"msg\": \"bad request!\"}";
  10. }

实测一下,返回结果如下

  1. ➜ ~ curl 'http://127.0.0.1:8080/code/ano' -i
  2. HTTP/1.1 400
  3. Content-Type: application/json;charset=UTF-8
  4. Transfer-Encoding: chunked
  5. Date: Sun, 05 Jan 2020 01:29:04 GMT
  6. Connection: close
  7. {"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. @ResponseBody
  2. @ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
  3. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  4. public String handleArrayIndexOutBounds(HttpServletRequest request, HttpServletResponse response,
  5. ArrayIndexOutOfBoundsException e) throws IOException {
  6. log.info("array index out conf!");
  7. return"aryIndexOutOfBounds: " + getThrowableStackInfo(e);
  8. }
b. 装饰异常类

另外一种使用姿势就是直接装饰在异常类上,然后当你的业务代码中,抛出特定的异常类,返回的 httpcode 就会设置为注解中的值

  1. /**
  2. * 异常类 + 注解方式,只支持标准http状态码
  3. *
  4. * @return
  5. */
  6. @GetMapping("exception/500")
  7. public String serverException() {
  8. thrownew ServerException("内部异常哦");
  9. }
  10. @GetMapping("exception/400")
  11. public String clientException() {
  12. thrownew ClientException("客户端异常哦");
  13. }
  14. @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "服务器失联了,请到月球上呼叫试试~~")
  15. publicstaticclass ServerException extends RuntimeException {
  16. public ServerException(String message) {
  17. super(message);
  18. }
  19. }
  20. @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "老哥,你的请求有问题~~")
  21. publicstaticclass ClientException extends RuntimeException {
  22. public ClientException(String message) {
  23. super(message);
  24. }
  25. }

测试结果如下,在异常类上添加注解的方式,优点在于不需要配合@ExceptionHandler写额外的逻辑了;缺点则在于需要定义很多的自定义异常类型

  1. ➜ ~ curl 'http://127.0.0.1:8080/code/exception/400' -i
  2. HTTP/1.1 400
  3. Content-Type: application/json;charset=UTF-8
  4. Transfer-Encoding: chunked
  5. Date: Sun, 05 Jan 2020 01:37:07 GMT
  6. Connection: close
  7. {"timestamp":"2020-01-05T01:37:07.662+0000","status":400,"error":"Bad Request","message":"老哥,你的请求有问题~~","path":"/code/exception/400"}%
  8. ➜ ~ curl 'http://127.0.0.1:8080/code/exception/500' -i
  9. HTTP/1.1 500
  10. Content-Type: application/json;charset=UTF-8
  11. Transfer-Encoding: chunked
  12. Date: Sun, 05 Jan 2020 01:37:09 GMT
  13. Connection: close
  14. {"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. @GetMapping("401")
  2. public ResponseEntity<String> _401() {
  3. return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{\"code\": 401, \"msg\": \"未授权!\"}");
  4. }
  5. @GetMapping("451")
  6. public ResponseEntity<String> _451() {
  7. return ResponseEntity.status(451).body("{\"code\": 451, \"msg\": \"自定义异常!\"}");
  8. }

实测结果

  1. ➜ ~ curl 'http://127.0.0.1:8080/code/401' -i
  2. HTTP/1.1 401
  3. Content-Type: text/plain;charset=UTF-8
  4. Content-Length: 34
  5. Date: Sun, 05 Jan 2020 01:40:10 GMT
  6. {"code": 401, "msg": "未授权!"}
  7. ➜ ~ curl 'http://127.0.0.1:8080/code/451' -i
  8. HTTP/1.1 451
  9. Content-Type: text/plain;charset=UTF-8
  10. Content-Length: 40
  11. Date: Sun, 05 Jan 2020 01:40:19 GMT
  12. {"code": 451, "msg": "自定义异常!"}

从上面的使用实例上看,可以知道这种使用方式,不仅仅支持标准的 http code,也支持自定义的 code(如返回 code 451)

3. HttpServletResponse

这种使用姿势则是直接操作HttpServletResponse对象,手动录入返回的结果

a. setStatus
  1. /**
  2. * response.setStatus 支持自定义http code,并可以返回结果
  3. *
  4. * @param response
  5. * @return
  6. */
  7. @GetMapping("525")
  8. public String _525(HttpServletResponse response) {
  9. response.setStatus(525);
  10. return"{\"code\": 525, \"msg\": \"自定义错误码 525!\"}";
  11. }

输出结果

  1. ➜ ~ curl 'http://127.0.0.1:8080/code/525' -i
  2. HTTP/1.1 525
  3. Content-Type: text/plain;charset=UTF-8
  4. Content-Length: 47
  5. Date: Sun, 05 Jan 2020 01:45:38 GMT
  6. {"code": 525, "msg": "自定义错误码 525!"}%

使用方式比较简单,直接设置 status 即可,支持自定义的 Http Code 返回

b. sendError

使用这种姿势的时候需要注意一下,只支持标准的 http code,而且 response body 中不会有你的业务返回数据,如

  1. /**
  2. * send error 方式,只支持标准http状态码; 且不会带上返回的结果
  3. *
  4. * @param response
  5. * @return
  6. * @throws IOException
  7. */
  8. @GetMapping("410")
  9. public String _410(HttpServletResponse response) throws IOException {
  10. response.sendError(410, "send 410");
  11. return"{\"code\": 410, \"msg\": \"Gone 410!\"}";
  12. }
  13. @GetMapping("460")
  14. public String _460(HttpServletResponse response) throws IOException {
  15. response.sendError(460, "send 460");
  16. return"{\"code\": 460, \"msg\": \"Gone 460!\"}";
  17. }

输出结果

  1. ➜ ~ curl 'http://127.0.0.1:8080/code/410' -i
  2. HTTP/1.1 410
  3. Content-Type: application/json;charset=UTF-8
  4. Transfer-Encoding: chunked
  5. Date: Sun, 05 Jan 2020 01:47:52 GMT
  6. {"timestamp":"2020-01-05T01:47:52.300+0000","status":410,"error":"Gone","message":"send 410","path":"/code/410"}%
  7. ➜ ~ curl 'http://127.0.0.1:8080/code/460' -i
  8. HTTP/1.1 500
  9. Content-Type: application/json;charset=UTF-8
  10. Transfer-Encoding: chunked
  11. Date: Sun, 05 Jan 2020 01:47:54 GMT
  12. Connection: close
  13. {"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

  • sendError: 只支持标准的 http code,如果传入自定义的 code,返回的 http code 会是 50

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/538078
推荐阅读
相关标签
  

闽ICP备14008679号