200105-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文件中,拷贝下面的配置即可)

org.springframework.boot    spring-boot-starter-parent    2.2.1.RELEASEUTF-8UTF-81.8org.springframework.boot        spring-boot-starter-web    org.springframework.boot                spring-boot-maven-plugin            spring-snapshotsSpring Snapshotshttps://repo.spring.io/libs-snapshot-localtruespring-milestonesSpring Milestoneshttps://repo.spring.io/libs-milestone-localfalsespring-releasesSpring Releaseshttps://repo.spring.io/libs-release-localfalse

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

@RestController@RequestMapping(path = "code")public class ErrorCodeRest {}

1. ResponseStatus 使用姿势

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

a. 装饰方法

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

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

实测一下,返回结果如下

➜  ~ curl 'http://127.0.0.1:8080/code/ano' -iHTTP/1.1 400Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedDate: Sun, 05 Jan 2020 01:29:04 GMTConnection: 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);}

b. 装饰异常类

另外一种使用姿势就是直接装饰在异常类上,然后当你的业务代码中,抛出特定的异常类,返回的 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' -iHTTP/1.1 400Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedDate: Sun, 05 Jan 2020 01:37:07 GMTConnection: 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' -iHTTP/1.1 500Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedDate: Sun, 05 Jan 2020 01:37:09 GMTConnection: 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

@GetMapping("401")public ResponseEntity _401() {    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("{"code": 401, "msg": "未授权!"}");}@GetMapping("451")public ResponseEntity _451() {    return ResponseEntity.status(451).body("{"code": 451, "msg": "自定义异常!"}");}

实测结果

➜  ~ curl 'http://127.0.0.1:8080/code/401' -iHTTP/1.1 401Content-Type: text/plain;charset=UTF-8Content-Length: 34Date: Sun, 05 Jan 2020 01:40:10 GMT{"code": 401, "msg": "未授权!"}➜  ~ curl 'http://127.0.0.1:8080/code/451' -iHTTP/1.1 451Content-Type: text/plain;charset=UTF-8Content-Length: 40Date: 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' -iHTTP/1.1 525Content-Type: text/plain;charset=UTF-8Content-Length: 47Date: Sun, 05 Jan 2020 01:45:38 GMT{"code": 525, "msg": "自定义错误码 525!"}%

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

b. sendError

使用这种姿势的时候需要注意一下,只支持标准的 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' -iHTTP/1.1 410Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedDate: 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' -iHTTP/1.1 500Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedDate: Sun, 05 Jan 2020 01:47:54 GMTConnection: 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
  • sendError: 只支持标准的 http code,如果传入自定义的 code,返回的 http code 会是 500

II. 其他

项目源码

  • 工程:https://github.com/liuyueyi/spring-boot-demo[1]
  • 项目:https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/207-web-response[2]

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

  • 一灰灰 Blog 个人博客 https://blog.hhui.top[3]
  • 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top[4]

return error怎么定义_SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势相关推荐

  1. boot返回码规范 spring_SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势

    200105-SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势 虽然 http 的提供了一整套完整.定义明确的状态码,但实际的业务支持中,后端并不总会遵守这套规则 ...

  2. spring boot 跨域请求_SpringBoot 系列教程 web 篇之自定义请求匹配条件 RequestCondition...

    191222-SpringBoot 系列教程 web 篇之自定义请求匹配条件 RequestCondition 在 spring mvc 中,我们知道用户发起的请求可以通过 url 匹配到我们通过@R ...

  3. return error怎么定义_这一次搞懂Spring自定义标签以及注解解析原理

    自定义标签解析原理 在上一篇分析默认标签解析时看到过这个类DefaultBeanDefinitionDocumentReader的方法parseBeanDefinitions:拉勾IT课小编为大家分解 ...

  4. return error怎么定义_自定义一个注解校验接口参数

    这段时间封装了很多的sdk,有一些sdk要求请求参数做验证,由于封装sdk肯定是用到第三方的依赖越少越好,所以自己不才写了一个注解的校验,本版本校验没有给出自定一点message,如果要可以自己加上, ...

  5. 普通类创建获取session 方式_Springboot系列之RestApi中获取枚举类属性的几种方式...

    前言 我们在日常开发中经常会遇到,实体类中的枚举属性,在通过@ResponseBody注解返回到前端后,默认被解析成了枚举对象的name值,当我们需要获取枚举类的详细属性时就会变得非常麻烦,今天给大家 ...

  6. 如何知道页面浏览时长系列之 Web 篇

    一.前言 页面浏览时长是网站分析中很常见的一个指标,反映用户在某些页面上浏览时间的长短,体现了网站用户黏性.然而精确的页面浏览时长是很难统计的,比如需要考虑单页面网页应用路由切换.用户切换浏览器 ta ...

  7. Android 系统(243)---Android进程系列第一篇---进程基础

    Android进程系列第一篇---进程基础 内容预览.png 概述: 本文主要讲解进程基础,更深入的认识有血有肉的进程,内容涉及进程控制块,信号,进程FD泄露等等.仅供参考,欢迎指正. 一.从Linu ...

  8. JavaScript设计模式系列—模式篇总结(上)

    转载请注明预见才能遇见的博客:http://my.csdn.net/ 原文地址:https://blog.csdn.net/pcaxb/article/details/102517956 JavaSc ...

  9. 设备指纹系列--前端篇

    基础篇请看:设备指纹系列–基础篇 我们接着前文继续写关于设备指纹前端接入方面的内容.话不多说,直接步入正题. 我们会在下文展示5种前端接入的方式,包括web接入.安卓接入.ios接入.微信小程序接入以 ...

最新文章

  1. 【胡策08】解题报告
  2. 变而不变:我看分布式系统发展和阿里实践
  3. 《APUE》读书笔记—第十三章守护进程
  4. 同一个页面同时拥有collectionView和navigationBar和tabBar时可能遇到的问题
  5. rabbitmq的安装全过程
  6. Qt文档阅读笔记-QWebEngineView及QML WebEngineView
  7. pytorch测试用例(查看tensor维度)
  8. mediawiki初学心得及使用方法
  9. 【渝粤教育】电大中专电商运营实操 (5)作业 题库
  10. 关于Ext.grid.EditorGridPanel使用中遇到的问题
  11. PDF怎么拆分?有哪些免费的PDF拆分软件
  12. 【Mathematica】 曲面的绘制
  13. SAP凭证的冲销-FB08
  14. 九江高考2021成绩查询,2021九江市地区高考成绩排名查询,九江市高考各高中成绩喜报榜单...
  15. Mixly实现秒表功能
  16. 我学编程全靠B站了,真香(第一期)
  17. Python缓存提高递归代码性能lru_cache
  18. 【目标检测·yolo系列】YOLOV1目标检测论文(参考 同济子豪兄的解读)笔记
  19. linux用rpm包装ftp,linux以rpm方式安装ftp软件
  20. 国内银行核心系统建设情况调研报告

热门文章

  1. Eigen求解数学问题(一)
  2. python scatter参数详解_matplotlib.pyplot.scatter散点图结构及用法||参数详解
  3. 中文信息处理的主流技术是什么?
  4. 2020北京智源大会 图神经网络专题 总结
  5. hdu5492(2015合肥网络赛I题)
  6. USACO 1.2 挤牛奶
  7. php中进制转换,php中进制转换
  8. sizeof 和 strlen
  9. Hadoop:eclipse配置hadoop-eclipse-plugin(版本hadoop2.7.3)
  10. vue axios全攻略