Skip to content

响应


返回 JSON(最常用场景)

自动序列化

java
@RestController
public class UserController {

    @GetMapping("/users")
    public List<User> index() {
        return userService.findAll();         // 自动转 JSON 数组
    }

    @GetMapping("/users/{id}")
    public User show(@PathVariable Long id) {
        return userService.findById(id);      // 自动转 JSON 对象
    }
}

只要类上有 @RestController,方法的返回值就会被 Jackson 库自动序列化成 JSON。 原理:Spring 检测到 @ResponseBody@RestController 自带),就调用 ObjectMapper 把 Java 对象转成 JSON。

ResponseEntity:精确控制

java
@PostMapping("/users")
public ResponseEntity<User> create(@RequestBody User user) {
    User saved = userService.save(user);

    return ResponseEntity
            .status(HttpStatus.CREATED)          // 201
            .header("X-Resource-Id", saved.getId().toString())
            .body(saved);
}

@DeleteMapping("/users/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
    userService.delete(id);
    return ResponseEntity.noContent().build();   // 204
}

@GetMapping("/users/{id}")
public ResponseEntity<User> show(@PathVariable Long id) {
    return userService.findById(id)
            .map(ResponseEntity::ok)             // 200
            .orElse(ResponseEntity.notFound().build());  // 404
}

统一响应格式

PHP 项目中常见的统一响应格式:

php
// Laravel 中可能在 AppServiceProvider 或 BaseController 中定义
return response()->json([
    'code' => 0,
    'message' => 'success',
    'data' => $data
]);

Spring Boot 中实现方式:

定义通用响应类

java
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;

    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> r = new ApiResponse<>();
        r.code = 0;
        r.message = "success";
        r.data = data;
        return r;
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        ApiResponse<T> r = new ApiResponse<>();
        r.code = code;
        r.message = message;
        return r;
    }

    // getter / setter
}

全局统一包装

java
// 方式一:控制器统一返回 ApiResponse
@GetMapping("/users")
public ApiResponse<List<User>> index() {
    return ApiResponse.success(userService.findAll());
}

// 方式二:使用 ResponseBodyAdvice 全局拦截(类似中间件处理响应)
@ControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 排除已经包装过的和某些特殊类型
        return !returnType.getParameterType().equals(ApiResponse.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, ...) {
        return ApiResponse.success(body);
    }
}

⚠️ 注意:方式二是全局 AOP,会影响到所有返回值,404 等异常情况需要额外处理。建议初期直接用方式一,手动包装返回值。


异常时返回错误 JSON

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiResponse<Void> handleNotFound(ResourceNotFoundException e) {
        return ApiResponse.error(1001, e.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Map<String, String>> handleValidation(
            MethodArgumentNotValidException e) {

        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getFieldErrors()
            .forEach(err -> errors.put(err.getField(), err.getDefaultMessage()));

        return ApiResponse.error(1002, "Validation failed")
                .data(errors);  // 伪代码,需要自己扩展 ApiResponse
    }
}

⚠️ 注意事项

1. null 会被序列化为 null,不是字段缺失

java
public class User {
    private String name;
    private String email;
    private String phone;  // 可能为 null
}
// 结果: {"name":"foo", "email":"foo@bar.com", "phone":null}

需要忽略 null 字段:

yaml
# application.yml
spring:
  jackson:
    default-property-inclusion: non_null

2. 循环引用会导致栈溢出

java
public class User {
    private List<Post> posts;  // User → Post
}
public class Post {
    private User author;       // Post → User(循环)
}
// 序列化时:JsonMappingException: Infinite recursion

解决:在双向关联的一侧加 @JsonIgnore@JsonBackReference

3. 没有 dd() / dump()

java
// PHP:dd($variable) 打印并终止
// Java 中:
System.out.println(variable);        // 打印到控制台,不终止
log.debug("variable: {}", variable); // 推荐,日志方式

Java 中没有 dd()。调试推荐用 IDE 断点调试,比 dd() 高效得多。

面向 PHP 开发者的 Spring Boot 文档