一灰灰blog 一灰灰blog
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)

一灰灰blog

资深搬运工
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)
  • 基础系列

  • DB系列

  • 搜索系列

  • MQ系列

  • WEB系列

    • Request

    • Response

      • 【WEB系列】Freemaker环境搭建
      • 【WEB系列】Thymeleaf环境搭建
      • 【WEB系列】Beetl环境搭建
      • 【WEB系列】返回文本、网页、图片的操作姿势
      • 【WEB系列】请求重定向
      • 【WEB系列】404、500异常页面配置
      • 【WEB系列】全局异常处理
      • 【WEB系列】自定义异常处理HandlerExceptionResolver
      • 【WEB系列】开启GZIP数据压缩
      • 【WEB系列】RestTemplate 4xx/5xx 异常信息捕获
      • 【WEB系列】自定义返回Http Code的n种姿势
        • I. 返回Http Code的n中姿势
          • 0. 环境
          • 1. ResponseStatus使用姿势
          • 2. ResponseEntity
          • 3. HttpServletResponse
          • 4, 小结
        • II. 其他
          • 1. 一灰灰Blog
      • 【WEB系列】异步请求知识点与使用姿势小结
      • 【WEB系列】SSE服务器发送事件详解
      • 【WEB系列】thymeleaf foreach踩坑记录
      • 【WEB系列】如何支持下划线驼峰互转的传参与返回
    • RestTemplate

    • WebClient

    • WebFlux

    • WebSocket

    • Web三剑客

    • 实例

    • 其他

  • 中间件

  • 运维

  • SpringSecurity

  • SpringCloud

  • Spring系列
  • WEB系列
  • Response
一灰灰
2020-01-05

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

<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>
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

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

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

# 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!\"}";
}
1
2
3
4
5
6
7
8
9
10

实测一下,返回结果如下

➜  ~ 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"}%
1
2
3
4
5
6
7
8

当我们发起请求时,返回的状态码为400,返回的数据为springboot默认的错误信息格式

虽然上面这种使用姿势可以设置http code,但是这种使用姿势有什么意义呢?

如果看过web系列教程中的:SpringBoot系列教程web篇之全局异常处理 (opens new window) 可能就会有一些映象,配合@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);
}
1
2
3
4
5
6
7
8

# 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);
    }
}
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

测试结果如下,在异常类上添加注解的方式,优点在于不需要配合@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"}%
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意

  • 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\": \"自定义异常!\"}");
}
1
2
3
4
5
6
7
8
9

实测结果

➜  ~ 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": "自定义异常!"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

从上面的使用实例上看,可以知道这种使用方式,不仅仅支持标准的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!\"}";
}
1
2
3
4
5
6
7
8
9
10
11

输出结果

➜  ~ 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!"}%
1
2
3
4
5
6
7

使用方式比较简单,直接设置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!\"}";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

输出结果

➜  ~ 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"}%
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

从上面的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\": \"自定义异常!\"}");
1
  • 我个人感觉是最强大的使用姿势,就是写起来没有那么简洁
  • 支持自定义code,支持设置 response body

HttpServletResponse

  • setStatus: 设置响应code,支持自定义code,支持返回response body
  • sendError: 只支持标准的http code,如果传入自定义的code,返回的http code会是500

# II. 其他

# web系列博文

  • 191222-SpringBoot系列教程web篇之自定义请求匹配条件RequestCondition (opens new window)
  • 191206-SpringBoot系列教程web篇Listener四种注册姿势 (opens new window)
  • 191122-SpringBoot系列教程web篇Servlet 注册的四种姿势 (opens new window)
  • 191120-SpringBoot系列教程Web篇之开启GZIP数据压缩 (opens new window)
  • 191018-SpringBoot系列教程web篇之过滤器Filter使用指南扩展篇 (opens new window)
  • 191016-SpringBoot系列教程web篇之过滤器Filter使用指南 (opens new window)
  • 191012-SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver (opens new window)
  • 191010-SpringBoot系列教程web篇之全局异常处理 (opens new window)
  • 190930-SpringBoot系列教程web篇之404、500异常页面配置 (opens new window)
  • 190929-SpringBoot系列教程web篇之重定向 (opens new window)
  • 190913-SpringBoot系列教程web篇之返回文本、网页、图片的操作姿势 (opens new window)
  • 190905-SpringBoot系列教程web篇之中文乱码问题解决 (opens new window)
  • 190831-SpringBoot系列教程web篇之如何自定义参数解析器 (opens new window)
  • 190828-SpringBoot系列教程web篇之Post请求参数解析姿势汇总 (opens new window)
  • 190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总 (opens new window)
  • 190822-SpringBoot系列教程web篇之Beetl环境搭建 (opens new window)
  • 190820-SpringBoot系列教程web篇之Thymeleaf环境搭建 (opens new window)
  • 190816-SpringBoot系列教程web篇之Freemaker环境搭建 (opens new window)
  • 190421-SpringBoot高级篇WEB之websocket的使用说明 (opens new window)
  • 190327-Spring-RestTemplate之urlencode参数解析异常全程分析 (opens new window)
  • 190317-Spring MVC之基于java config无xml配置的web应用构建 (opens new window)
  • 190316-Spring MVC之基于xml配置的web应用构建 (opens new window)
  • 190213-SpringBoot文件上传异常之提示The temporary upload location xxx is not valid (opens new window)

项目源码

  • 工程:https://github.com/liuyueyi/spring-boot-demo (opens new window)
  • 项目:https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/207-web-response (opens new window)

# 1. 一灰灰Blog

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

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

  • 一灰灰Blog个人博客 https://blog.hhui.top (opens new window)
  • 一灰灰Blog-Spring专题博客 http://spring.hhui.top (opens new window)

一灰灰blog

编辑 (opens new window)
#Web
上次更新: 2021/10/15, 19:56:22
【WEB系列】RestTemplate 4xx/5xx 异常信息捕获
【WEB系列】异步请求知识点与使用姿势小结

← 【WEB系列】RestTemplate 4xx/5xx 异常信息捕获 【WEB系列】异步请求知识点与使用姿势小结→

最近更新
01
【基础系列】基于maven多环境配置
04-25
02
【WEB系列】内嵌Tomcat配置Accesslog日志文件生成位置源码探索
04-24
03
【搜索系列】ES查询常用实例演示
04-18
更多文章>
Theme by Vdoing | Copyright © 2017-2022 一灰灰Blog
MIT License | 鄂ICP备18017282号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×