ElIauk

Spring Boot 第一个 REST 接口

最小可运行的接口 Spring Boot 里写一个 REST 接口只需要一个类和一个注解: import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotatio

评论
Spring Boot 第一个 REST 接口

最小可运行的接口

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 把前端和接口放在同一个域名下,从根上避开跨域。

排查步骤

  1. curl -i 确认状态码,先分清是 4xx 还是 5xx;
  2. 404 查启动日志里的 Mapped,确认接口是否注册;
  3. 405 查请求方法,415 查 Content-Type;
  4. 500 看控制台异常栈的第一个 Caused by;
  5. 前端问题打开浏览器 Network 面板,对比实际请求地址和后端路径。

参考

评论

推荐阅读

订阅邮件

订阅更新,及时收到最新文章。

作者