乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 SpringMVC概述与DispatcherServlet

    • 本章导读:Spring MVC概述与DispatcherServlet
    • Spring MVC 是什么
    • MVC 设计模式
    • 前端控制器模式
    • DispatcherServlet
    • 核心组件协作
  • 第2章 控制器与请求映射

    • 本章导读:控制器与请求映射
    • Controller
    • RestController
    • RequestMapping
    • GetMapping
    • PostMapping
    • PutMapping
    • DeleteMapping
    • PathVariable
    • RESTful
    • 请求映射原理
  • 第3章 请求参数获取与转换

    • 本章导读:请求参数获取与转换
    • RequestParam
    • RequestBody
    • RequestHeader
    • CookieValue
    • Model
    • ModelAttribute
    • 数据绑定原理
    • 数据校验
  • 第4章 响应数据与视图解析

    • 本章导读:响应数据与视图解析
    • ResponseBody
    • ResponseEntity
    • ModelAndView
    • ViewResolver
    • HttpMessageConverter
    • forward与redirect
  • 第5章 拦截器过滤器与跨域

    • 本章导读:拦截器、过滤器与跨域
    • HandlerInterceptor
    • WebMvcConfigurer
    • CrossOrigin
    • 登录验证实战
  • 第6章 文件上传与异常处理

    • 本章导读:文件上传与异常处理
    • MultipartFile
    • 文件下载
    • ExceptionHandler
    • ControllerAdvice
    • RestControllerAdvice
    • ResponseStatus
  • 第7章 高级特性与最佳实践

    • 本章导读:高级特性与最佳实践
    • SessionAttributes
    • SessionAttribute
    • RedirectAttributes
    • MockMvc测试
    • 国际化
    • 最佳实践
  • 第8章 扩展与异步机制

    • 本章导读:扩展与异步机制
    • 异步请求处理
    • 自定义参数解析器
    • 内容协商

ResponseEntity

本章在 @ResponseBody 的基础上更进一步。@ResponseBody 解决了"数据怎么出去"的问题,但它对 HTTP 响应的控制是有限的——状态码固定 200,响应头无法自定义。ResponseEntity 提供了对 HTTP 响应的完整控制权:状态码、响应头、响应体,三者皆可定制。这是编写专业级 RESTful API 的必备技能。


定义与作用

ResponseEntity<T> 是 Spring MVC 提供的完整 HTTP 响应封装类,它同时携带:

  • HTTP 状态码(Status Code):200、201、404、500 等
  • 响应头(Headers):Content-Type、Location、Cache-Control 等
  • 响应体(Body):任意类型的数据对象

使用 ResponseEntity,Controller 方法不再只是"返回数据",而是"返回一个完整的 HTTP 响应"。

生活类比:正式公函

想象飞翔科技对外发送商务公函:

  • @ResponseBody:直接打电话告诉对方结果(只有内容,没有正式格式)
  • ResponseEntity:发送一份正式公函,包含:
    • 信封上的红色印章(HTTP 状态码:200 表示正常,404 表示查无此人)
    • 信封上的附加说明(响应头:"请查收附件"、"此函限今日回复")
    • 信纸正文(响应体:具体的业务数据)

专业的 RESTful API 就像正式公函——状态码传达结果类别,响应头传递元信息,响应体承载具体数据。


核心原理

ResponseEntity 构建链

构建方式:

Spring 提供了流畅的 Builder API 创建 ResponseEntity:

ResponseEntity.status(HttpStatus.CREATED)           // 状态码
              .header("Location", "/employees/101") // 自定义头
              .body(employee);                        // 响应体

快捷静态方法:

ResponseEntity.ok(body);              // 200 OK
ResponseEntity.notFound().build();   // 404 Not Found
ResponseEntity.noContent().build();  // 204 No Content

适用位置与常用方法

ResponseEntity 作为返回值类型,用于 Controller 方法的返回。

构建方法说明
ResponseEntity.status(int) / .status(HttpStatus)设置状态码
.header(String, String...)添加响应头
.headers(HttpHeaders)批量设置响应头
.body(T)设置响应体
.build()无响应体时构建(用于 204、404 等)

常见状态码使用场景表

状态码含义使用场景ResponseEntity 写法
200OK查询成功、更新成功ResponseEntity.ok(body)
201Created资源创建成功ResponseEntity.status(201).body(body)
204No Content删除成功、无返回数据ResponseEntity.noContent().build()
400Bad Request参数校验失败ResponseEntity.badRequest().body(error)
401Unauthorized未登录/令牌无效ResponseEntity.status(401).body(error)
403Forbidden无权限ResponseEntity.status(403).body(error)
404Not Found资源不存在ResponseEntity.notFound().build()
409Conflict资源冲突(如重复创建)ResponseEntity.status(409).body(error)
500Internal Server Error服务器内部错误ResponseEntity.status(500).body(error)

完整示例

场景

飞翔科技员工管理系统的 RESTful API 需要遵循 HTTP 语义规范。架构师白歌要求:创建员工返回 201 + Location 头,查询不到返回 404,删除成功返回 204。

代码实现

package com.feixiang.web;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/employees")
public class EmployeeController {

    private final List<Employee> employees = Arrays.asList(
        new Employee(1L, "张三", "技术部", 25000),
        new Employee(2L, "李四", "产品部", 22000)
    );

    /**
     * 查询单个员工:存在返回 200,不存在返回 404
     */
    @GetMapping("/{id}")
    public ResponseEntity<Employee> getEmployee(@PathVariable Long id) {
        Optional<Employee> emp = employees.stream()
            .filter(e -> e.getId().equals(id))
            .findFirst();
        
        return emp.map(ResponseEntity::ok)
                  .orElse(ResponseEntity.notFound().build());
    }

    /**
     * 创建员工:返回 201 + Location 头
     */
    @PostMapping
    public ResponseEntity<Employee> createEmployee(@RequestBody Employee employee) {
        employee.setId(100L);
        // 实际项目中保存到数据库
        
        URI location = URI.create("/api/employees/" + employee.getId());
        return ResponseEntity.created(location)
                             .body(employee);
    }

    /**
     * 删除员工:返回 204 No Content
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
        // 实际项目中从数据库删除
        return ResponseEntity.noContent().build();
    }

    /**
     * 参数错误:返回 400 + 错误信息
     */
    @GetMapping("/search")
    public ResponseEntity<?> search(@RequestParam(required = false) String name) {
        if (name == null || name.trim().isEmpty()) {
            return ResponseEntity.badRequest()
                .body("{\"error\": \"name 参数不能为空\"}");
        }
        return ResponseEntity.ok(employees);
    }
}

// Employee.java
package com.feixiang.web;

public class Employee {
    private Long id;
    private String name;
    private String department;
    private Integer salary;

    public Employee() {}
    public Employee(Long id, String name, String department, Integer salary) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getDepartment() { return department; }
    public void setDepartment(String department) { this.department = department; }
    public Integer getSalary() { return salary; }
    public void setSalary(Integer salary) { this.salary = salary; }
}

HTTP 请求示例

示例 1:查询存在员工(200 OK)

curl -i "http://localhost:8080/api/employees/1"

响应:

HTTP/1.1 200
Content-Type: application/json

{"id":1,"name":"张三","department":"技术部","salary":25000}

示例 2:查询不存在员工(404 Not Found)

curl -i "http://localhost:8080/api/employees/999"

响应:

HTTP/1.1 404
Content-Length: 0

示例 3:创建员工(201 Created + Location)

curl -i -X POST "http://localhost:8080/api/employees" \
     -H "Content-Type: application/json" \
     -d '{"name":"王五","department":"运维部","salary":20000}'

响应:

HTTP/1.1 201
Location: /api/employees/100
Content-Type: application/json

{"id":100,"name":"王五","department":"运维部","salary":20000}

示例 4:删除员工(204 No Content)

curl -i -X DELETE "http://localhost:8080/api/employees/1"

响应:

HTTP/1.1 204
Content-Length: 0

易错场景与面试考点

误区一:ResponseEntity 和 @ResponseBody 同时用

现象:方法返回 ResponseEntity,同时标注 @ResponseBody。

纠正:ResponseEntity 已经隐含了 @ResponseBody 的语义——它的 body 会被 HttpMessageConverter 序列化写入响应体。额外加 @ResponseBody 是冗余的,但不会报错。

// 正确:不需要 @ResponseBody
@GetMapping("/{id}")
public ResponseEntity<Employee> getEmployee(@PathVariable Long id) {
    return ResponseEntity.ok(employee);
}

误区二:泛型类型擦除导致序列化问题

现象:ResponseEntity<?> 返回时,Jackson 无法确定具体类型,某些字段可能丢失。

纠正:尽量使用明确的泛型类型:

// 推荐
public ResponseEntity<Employee> getEmployee(...) { ... }

// 避免在确定类型时使用通配符
public ResponseEntity<?> getEmployee(...) { ... }  // 仅在类型不确定时使用

误区三:201 Created 忘记加 Location 头

错误:创建资源返回 201,但响应头中没有 Location。

纠正:HTTP/1.1 规范建议 201 响应包含指向新创建资源的 Location 头。这是 RESTful API 的最佳实践:

URI location = ServletUriComponentsBuilder
    .fromCurrentRequest()
    .path("/{id}")
    .buildAndExpand(employee.getId())
    .toUri();

return ResponseEntity.created(location).body(employee);

面试高频:ResponseEntity 的优势是什么?

标准回答:

  1. 完整控制 HTTP 响应:可以设置任意状态码(200、201、404 等),而 @ResponseBody 固定返回 200
  2. 自定义响应头:如 Location、Cache-Control、X-Request-ID 等
  3. RESTful 语义化:不同业务场景返回不同状态码,让 API 更符合 HTTP 规范
  4. 类型安全:泛型参数 ResponseEntity<T> 保证响应体类型明确
  5. 链式构建:ResponseEntity.status().header().body() 流畅 API,代码简洁

小结

ResponseEntity<T> 是 Spring MVC 中控制 HTTP 响应的终极武器。它将状态码、响应头、响应体封装为一个对象,让开发者可以精确控制每一个 HTTP 响应细节。

核心要点:

  • ResponseEntity 同时控制状态码、响应头、响应体
  • 常用快捷方法:ok()、created()、noContent()、notFound()、badRequest()
  • 创建资源返回 201 + Location 头是 RESTful 最佳实践
  • 删除成功返回 204 No Content,查询不到返回 404

本章与全局的关系:本章讲解了"如何完整控制 HTTP 响应"。下一节 ViewResolver 将回到传统视图渲染模式,讲解 Spring MVC 如何将逻辑视图名解析为物理页面路径。

上一页
ResponseBody
下一页
ModelAndView