Java | record | Controller逻辑

java 复制代码
@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final StudentService service;

    public StudentController(StudentService service) {
        this.service = service;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public StudentResponse create(@RequestBody CreateStudentRequest request) {
        return StudentResponse.from(service.createStudent(
                request.name(), request.grade(), request.notes()));
    }

    @GetMapping
    public List<StudentResponse> list() {
        return service.listStudents().stream()
                .map(StudentResponse::from).toList();
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> invalidInput(IllegalArgumentException exception) {
        return Map.of("message", exception.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> invalidJson() {
        return Map.of("message", "请求体必须是有效的学生 JSON 对象");
    }

    public record CreateStudentRequest(
            String name, String grade, String notes) {}

    public record StudentResponse(
            Long id, String name, String grade, String notes) {
        static StudentResponse from(Student student) {
            return new StudentResponse(
                    student.getId(), student.getName(),
                    student.getGrade(), student.getNotes());
        }
    }
}

首先看第一段代码:

java 复制代码
 @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public StudentResponse create(@RequestBody CreateStudentRequest request) {
        return StudentResponse.from(service.createStudent(
                request.name(), request.grade(), request.notes()));
    }

@RestController表示这个类是一个 Spring MVC 的 REST 控制器,可以接受HTTP请求

@RequestMapping("/api/students")表示这个 Controller 里的所有接口,都统一以/api/students

开头。

@PostMapping表示接收 POST /api/students 的请求

首先在类中使用record构造了两个类:

复制代码
CreateStudentRequest

用来便于操作网页端传来的json字符串。

复制代码
StudentResponse

用于将数据库的数据有选择的封装响应对象返回给前端。

为什么要有选择,因为它是专门用来返回给前端的响应对象。实体类Student中的某些字段不方便返回给前端,实体类和接口返回结构最好不要强耦合。如果强耦合,直接返回Student实体类,会将数据库内部结构暴漏出去。这个类控制了前端能看到什么。

这个类中存在的静态方法

java 复制代码
static StudentResponse from(Student student)

就是实现了把 Student 实体转换成 StudentResponse,进行有选择的数据封装。

java 复制代码
@RequestBody CreateStudentRequest request

表示把 HTTP 请求体里的 JSON,自动转换成一个 CreateStudentRequest 对象。

java 复制代码
@ResponseStatus(HttpStatus.CREATED)

表示这个接口成功后返回 HTTP 状态码:201 Created

每次 HTTP 请求最终都要有状态码;成功走正常响应,失败走异常处理或默认错误处理。


java 复制代码
return StudentResponse.from(
    service.createStudent(
        request.name(),
        request.grade(),
        request.notes()
    )
);

可以拆成3步:第一步先让 Service 创建学生

java 复制代码
Student student = service.createStudent(
    request.name(),
    request.grade(),
    request.notes()
);

第二步把实体对象转换成响应对象

java 复制代码
StudentResponse response =
    StudentResponse.from(student);

第三步将响应对象返回给前端。

接着看第二段代码:

java 复制代码
 @GetMapping
    public List<StudentResponse> list() {
        return service.listStudents().stream()
                .map(StudentResponse::from).toList();
    }

@GetMapping表示接收 Get /api/students 的请求

Service返回的是List<Student>,但 Controller 最终要返回List<StudentResponse>,所以需要进行类型转换。

java 复制代码
.map(StudentResponse::from)

是方法引用。

它等价于:

复制代码
.map(student -> StudentResponse.from(student))

意思就是:

复制代码
Student1 → StudentResponse1
Student2 → StudentResponse2
Student3 → StudentResponse3

接着看第三段代码:

java 复制代码
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidInput(IllegalArgumentException exception) {
       return Map.of("message", exception.getMessage());
}

这是一个异常处理。

对应StudentSercice里的

java 复制代码
@Transactional
	public Student createStudent(String name, String grade, String notes) {
		String normalizedName = name == null ? null : name.strip();
		if (normalizedName == null || normalizedName.isEmpty() || normalizedName.length() > 20) {
			throw new IllegalArgumentException("Student name must contain 1 to 20 characters");
		}
		if (notes != null && notes.length() > 200) {
			throw new IllegalArgumentException("Student notes must not exceed 200 characters");
		}

		String normalizedGrade = grade == null || grade.isBlank() ? null : grade.strip();
		return repository.save(new Student(normalizedName, normalizedGrade, notes));
	}

Service 抛出:

复制代码
IllegalArgumentException

Controller 捕获到以后:

复制代码
@ExceptionHandler(IllegalArgumentException.class)

就会调用:

复制代码
invalidInput(...)

然后返回:

复制代码
{
  "message": "Student name must contain 1 to 20 characters"
}

HTTP 状态码:

复制代码
400 Bad Request

接着看第四段代码:

java 复制代码
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidJson() {
     return Map.of("message", "请求体必须是有效的学生 JSON 对象");
}

这个处理的是:

前端发来的 JSON 根本解析不了。

例如发:

复制代码
{name: 张三

这种非法 JSON。

Spring 在进入:

复制代码
create(...)

之前就可能解析失败。

于是抛:

复制代码
HttpMessageNotReadableException

这里捕获之后返回:

复制代码
{
  "message": "请求体必须是有效的学生 JSON 对象"
}

也是:

复制代码
400 Bad Request

注解 请求含义 常见用途
@GetMapping 获取 查询数据
@PostMapping 提交 新增数据
@PutMapping 更新 整体修改数据
@PatchMapping 更新 部分修改数据
@DeleteMapping 删除 删除数据

GET、POST、PUT、DELETE 都是客户端发给服务器的不同类型请求;服务器处理后都会返回响应。


关于record

record 是 Java 专门用来表示只承载数据的简单对象的一种语法。

java 复制代码
public record CreateStudentRequest(
        String name,
        String grade,
        String notes) {
}

如果不用 record,你以前可能会写:

java 复制代码
public class CreateStudentRequest {

    private String name;
    private String grade;
    private String notes;

    public String getName() {
        return name;
    }

    ...
}

但 record 可以直接简化成:

java 复制代码
record CreateStudentRequest(
    String name,
    String grade,
    String notes
) {}

Java 会自动帮你生成:

  • 构造器
  • name()
  • grade()
  • notes()
  • equals()
  • hashCode()
  • toString()

所以这里:

java 复制代码
request.name()
request.grade()
request.notes()

其实就是在取 record 里面的数据。


整份Controller流程

java 复制代码
StudentController

1. POST /api/students
   ↓
   接收创建学生 JSON
   ↓
   调 StudentService
   ↓
   返回 StudentResponse

2. GET /api/students
   ↓
   查询所有学生
   ↓
   Student 转 StudentResponse
   ↓
   返回列表

3. IllegalArgumentException
   ↓
   返回 400 + 错误信息

4. 非法 JSON
   ↓
   返回 400 + JSON 格式错误信息
相关推荐
小羊没烦恼!4 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里4 天前
java中的继承和多态的区别
java
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕4 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖4 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时4 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读4 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
vipxieliang4 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++