前端视角 Java Web 入门手册 5.3:真实世界 Web 开发——RESTful API 与 Spring MVC

RESTful API 设计指南 - 阮一峰的网络日志

RESTful API 最佳实践 - 阮一峰的网络日志

RESTful API 设计规范

REST(Representational State Transfer)是一种软件架构风格,用于设计可扩展的网络应用程序。RESTful API 根据 REST 原则构建,旨在利用现有的网络协议(特别是 HTTP)来创建高效、可扩展和易于维护的接口。RESTful API 包含一些关键设计规范和最佳实践:

1. 资源导向

所有的实体(如用户、订单、产品)都被视为资源,每个资源通过唯一的 URI(统一资源标识符)进行标识

plain 复制代码
https://api.example.com/users
https://api.example.com/orders/{id}
https://api.example.com/products/{id}

在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词。一般来说,数据库中的表都是同种记录的"集合"(collection),所以 API 中的名词也应该使用复数。

2. 使用标准的 HTTP 方法

  • GET:检索资源。例如,GET /users 获取用户列表,GET /users/{id} 获取特定用户。
  • POST:创建新资源。例如,POST /users 创建新用户。
  • PUT:更新资源的全部内容。例如,PUT /users/{id} 更新特定用户的所有信息。
  • PATCH:部分更新资源。例如,PATCH /users/{id} 部分更新用户信息。
  • DELETE:删除资源。例如,DELETE /users/{id} 删除特定用户。

3. 版本控制

通过 URI 版本号或其他方式管理 API 的版本,确保向后兼容。例如:

  • /v1/users
  • /api/v2/orders

4. 分页、过滤和排序

对于返回大量数据的 API,提供分页、过滤和排序功能,以提高性能和用户体验。例如:

ini 复制代码
GET /users?page=2&size=20&sort=name,asc
GET /products?category=electronics&price_lt=500

5. 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

plain 复制代码
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

Spring MVC

Spring MVC 是 Spring 框架中的一个模块,专门用于构建 Web 应用程序,借助 Spring MVC,开发者可以方便地创建 RESTful 风格的 Web 服务

控制器

在 RESTful 服务中,控制器负责处理 HTTP 请求,并返回资源表示。Spring MVC 提供了多个注解来简化这一过程

  • @RestController:表示控制器中的所有方法默认返回 JSON 或 XML 数据,而不是视图
  • @RequestMapping:将特定的 HTTP 请求路径和方法与控制器的方法关联起来,实现资源的操作。为了简化常用的HTTP方法映射,Spring MVC提供了一系列快捷注解,如 @GetMapping、@PostMapping 等
java 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {

    // 继承类级别的基础路径 /api/users
    @GetMapping
    public List<User> getAllUsers() {
        // 获取所有用户
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // 根据 ID 获取用户
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // 创建新用户
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // 更新用户信息
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        // 删除用户
    }
}

请求与响应

  • @RequestBody:将HTTP请求体中的JSON或XML数据绑定到方法参数中。
  • @PathVariable:从URL路径中提取变量。
  • @RequestParam:从查询参数(query)中提取变量。
  • @ResponseStatus:用于为方法或异常类设置HTTP响应状态码
java 复制代码
@PutMapping("/users/{id}")
@ResponseStatus(HttpStatus.CREATED)
public User updateUser(@PathVariable Long id, @RequestBody User user) {
    // 方法实现
}

简单 demo

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

    private final Map<Long, User> userRepository = new ConcurrentHashMap<>();

    @GetMapping
    public Collection<User> getAllUsers() {
        return userRepository.values();
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userRepository.get(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        user.setId(System.currentTimeMillis());
        userRepository.put(user.getId(), user);
        return ResponseEntity.status(HttpStatus.CREATED).body(user);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        if (userRepository.containsKey(id)) {
            user.setId(id);
            userRepository.put(id, user);
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        if (userRepository.containsKey(id)) {
            userRepository.remove(id);
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
    }
}
相关推荐
罗技1233 小时前
ES类的索引轮换
java·linux·elasticsearch
liaokailin4 小时前
Spring AI 实战:第十一章、Spring AI Agent之知行合一
java·人工智能·spring
JANYI20184 小时前
C文件在C++平台编译时的注意事项
java·c语言·c++
benpaodeDD5 小时前
双列集合——map集合和三种遍历方式
java
Q_Boom6 小时前
前端跨域问题怎么在后端解决
java·前端·后端·spring
搬砖工程师Cola6 小时前
<Revit二次开发> 通过一组模型线构成墙面,并生成墙。Create(Document, IList.Curve., Boolean)
java·前端·javascript
等什么君!6 小时前
学习spring boot-拦截器Interceptor,过滤器Filter
java·spring boot·学习
caihuayuan47 小时前
Linux环境部署iview-admin项目
java·大数据·sql·spring·课程设计
浪前7 小时前
【项目篇之统一内存操作】仿照RabbitMQ模拟实现消息队列
java·分布式·rabbitmq·ruby
奋进的小暄7 小时前
数据结构(4) 堆
java·数据结构·c++·python·算法