最小可运行的接口
Spring Boot 里写一个 REST 接口只需要一个类和一个注解:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/hello")
public class HelloController {
@GetMapping
public String hello(@RequestParam(defaultValue = "Java") String name) {
return "Hello, " + name + "!";
}
}
@RestController 等于 @Controller 加 @ResponseBody:前者把类注册成 Web 组件,后者告诉 Spring 把方法返回值直接写进响应体,而不是去找视图页面。返回字符串时原样输出,返回对象时由 Jackson 自动转成 JSON。
@RequestMapping("/api/hello") 定义类级别的路径前缀,@GetMapping 表示只接受 GET 请求。访问 /api/hello?name=world,响应就是 Hello, world!。
返回 JSON 与日期
把返回值换成对象,Spring Boot 会自动序列化成 JSON。JDK 16 之后可以用 record 简化 VO:
import java.time.LocalDateTime;
public record UserVO(Long id, String name, LocalDateTime createdAt) {
}
@GetMapping("/{id}")
public UserVO detail(@PathVariable Long id) {
return new UserVO(id, "张三", LocalDateTime.now());
}
响应大概是:
{"id":1,"name":"张三","createdAt":"2022-07-22T10:30:00"}
日期默认是 ISO-8601 格式,前端如果习惯 yyyy-MM-dd HH:mm:ss,可以在配置里统一:
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
注意 date-format 只对 java.util.Date 生效,LocalDateTime 需要配合 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 使用。
如果前端需要字符串形式的 ID(比如 Long 超过 JS 安全整数范围),可以在配置里开启 spring.jackson.generator.write-numbers-as-strings=true,或者在字段上加 @JsonSerialize(using = ToStringSerializer.class)。这类问题在雪花 ID 场景特别常见。
用 curl 验证
接口写完先别急着打开浏览器,用 curl 看状态码和响应头最直接:
curl -i "http://localhost:8080/api/hello?name=world"
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Hello, world!
返回 JSON 时 Content-Type 会变成 application/json。如果状态码不是 200,-i 能第一时间看出来,比在浏览器里看到一片白色页面强。
请求注解速查
| 注解 | 位置 | 典型用法 |
|---|---|---|
@GetMapping |
方法 | 查询,参数放 URL |
@PostMapping |
方法 | 新增,数据放 body |
@PutMapping |
方法 | 全量更新 |
@DeleteMapping |
方法 | 删除 |
@PathVariable |
参数 | 取 /users/{id} 里的 id |
@RequestParam |
参数 | 取 ?page=1,可设默认值 |
@RequestBody |
参数 | 取 JSON body,配合 @Valid |
@RequestHeader |
参数 | 取请求头,如 token |
REST 风格里,路径用名词复数,动作交给 HTTP 方法:查用户是 GET /api/users/1,删用户是 DELETE /api/users/1,不要把动作写进路径(/api/getUser、/api/deleteUser)。这样接口一眼就能看出语义,前端也好做统一的请求封装。
@RequestParam 默认是必填的,不传会返回 400;允许不传时加 required = false 并给 defaultValue。defaultValue 写 "0" 时,接收类型是 int 也能自动转换,但写空字符串会转换失败。
返回类型上,String 会被当成响应体内容,不是视图名;想跳页面要用 ModelAndView,或者改用 @Controller。这个差异在 @RestController 和 @Controller 混用时最容易搞混。
传文件用 @RequestPart 或 @RequestParam("file") MultipartFile file,同时要配 spring.servlet.multipart.max-file-size,默认只有 1MB,传大文件会直接报 MaxUploadSizeExceededException。
接口路径建议统一加 /api 前缀,再配合 server.servlet.context-path 区分应用。路径里不要出现动词,也不要把数据库自增主键直接暴露出去,对外可以用业务编号或加密后的 ID。实际项目里还会用 @PatchMapping 做局部更新。
常见坑
1. 接口 404
按顺序排查:请求路径是否和 @RequestMapping 完全一致(包括大小写)、Controller 是否在启动类的同级或子包下、有没有加 @RestController、server.servlet.context-path 是否被改过。启动日志里搜 Mapped "{[/api/hello]",能直接确认接口有没有注册成功。
2. 405 Method Not Allowed
路径对了但请求方法不对,比如接口写的是 @PostMapping,你却用浏览器地址栏访问(浏览器默认发 GET)。用 curl -X POST 再试一次。
3. 415 Unsupported Media Type
服务端写了 @RequestBody,客户端却没带 Content-Type: application/json,或者发的是表单数据。用 curl 时记得加:
curl -X POST "http://localhost:8080/api/users" \
-H "Content-Type: application/json" \
-d '{"name":"张三"}'
4. 返回的字段莫名变少或变多
Jackson 默认输出所有 getter,包括 null 字段。想去掉空值加 @JsonInclude(JsonInclude.Include.NON_NULL);前端要下划线命名,用 @JsonProperty("user_name") 逐个指定,或者统一配置命名策略。
5. 浏览器请求跨域失败
后端返回正常,浏览器控制台却报 CORS 错误。开发阶段可以在 Controller 上加 @CrossOrigin,或者用 IDEA 的代理、Vite 的 proxy;生产环境通常由 Nginx 把前端和接口放在同一个域名下,从根上避开跨域。
排查步骤
curl -i确认状态码,先分清是 4xx 还是 5xx;- 404 查启动日志里的
Mapped,确认接口是否注册; - 405 查请求方法,415 查
Content-Type; - 500 看控制台异常栈的第一个
Caused by; - 前端问题打开浏览器 Network 面板,对比实际请求地址和后端路径。
评论