Spring Boot--@PathVariable、@RequestParam、@RequestBody

目录

声明!!

什么是RESTful?

[RESTful 的基本原则](#RESTful 的基本原则)

无状态性(Stateless)

[统一接口(Uniform Interface)](#统一接口(Uniform Interface))

[分层系统(Layered System)](#分层系统(Layered System))

缓存(Cacheable)

[按需代码(Code on Demand, 可选)](#按需代码(Code on Demand, 可选))

[HTTP 协议里面,四个表示操作方式的动词:](#HTTP 协议里面,四个表示操作方式的动词:)

@PathVariable

[@PathVariable 映射 URL 绑定的占位符](#@PathVariable 映射 URL 绑定的占位符)

@PathVariable的参数

基本用法

多个路径变量

默认值

注意事项

@RequestParam

@RequestParam的参数

基本用法

默认值

多个值

必填与可选参数

数据验证

注意事项

@RequestBody

基本用法

支持的数据格式

数据验证

异常处理

注意事项

URL(统一资源定位符)

[URL 的基本结构](#URL 的基本结构)

[完整 URL 示例](#完整 URL 示例)

[URL 编码](#URL 编码)

[URL 的作用](#URL 的作用)

资源定位:

导航:

交互:

搜索引擎优化(SEO):


@PathVariable、@RequestParam、@RequestBody是构建 RESTful Web 服务的非常有用的工具

声明!!

本文大多引用出处来自:

Spring Boot中@PathVariable、@RequestParam和@RequestBody的区别和使用_spring boot pathvariable-CSDN博客

本文仅作学习记录参考,若有侵权,麻烦告知删除

什么是RESTful?

RESTful 是一种基于HTTP协议的软件架构风格,它用于设计网络应用程序接口(API)。

REST 代表"表述性状态转移"(Representational State Transfer),其核心思想是通过一组标准的操作和资源来实现客户端与服务器之间的交互。RESTful API 设计强调简洁、直观和易于理解,遵循无状态通信原则,使得开发者能够快速构建和维护分布式系统。

RESTful 的基本原则

无状态性(Stateless)

每个请求都必须包含所有必要的信息,以使服务器能够理解和处理该请求,而不依赖于任何先前的交互或会话状态。

统一接口(Uniform Interface)

使用一致的方式定义资源及其操作,包括:

资源标识(Resource Identification):每个资源都有一个唯一的URI(统一资源标识符)下文有详解。

自描述消息(Self-descriptive Messages):响应中包含足够的信息让客户端知道如何处理数据,例如内容类型(如JSON、XML)。

HATEOAS(Hypermedia as the Engine of Application State):允许API返回链接到其他相关资源的数据,指导客户端下一步操作。

分层系统(Layered System)

系统可以由多个层级组成,每一层只与紧邻的一层进行交互,从而增加了灵活性和可扩展性。

缓存(Cacheable)

响应可以被标记为可缓存,以便后续相同的请求可以直接从缓存中获取结果,减少对服务器的压力。

按需代码(Code on Demand, 可选)

服务器可以在响应中提供可执行代码(如JavaScript),以增强客户端的功能。这一特性在RESTful API中较少使用。

HTTP 协议里面,四个表示操作方式的动词:

GETPOSTPUTDELETE

它们分别对应四种基本操作:

  • GET 用来获取资源
  • POST 用来新建资源
  • PUT 用来更新资源
  • DELETE 用来删除资源

@PathVariable

主要用于将 URL 中的模板变量映射到方法参数上。它允许开发者从请求路径中提取出特定部分的数据,并直接传递给控制器方法的参数,从而简化了对动态路径元素的处理。

@PathVariable 映射 URL 绑定的占位符

通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器(controller)处理方法的形参中:URL 中的 {xxx } 占位符可以通过@PathVariable("xxx") 绑定到操作方法的形参中。

@PathVariable的参数

  • **String value:**可指定占位符 { } 中的参数名,若只指定value这一个属性可省略属性名不写,若占位符中的参数名和处理方法中的参数名相同可省略此属性;
  • **String name:**等价与value,和value无本质上的差异,两个属性指定其一即可;
  • **boolean required:**是否必需,默认为 true,即 请求中必须包含该参数,如果没有包含,将会抛出异常;

基本用法

假设你有一个 RESTful API 用来管理用户信息,每个用户的详细信息可以通过其唯一的 ID 来访问。你可以定义如下形式的 URL:

复制代码
GET /users/{id}

在这个例子中,{id} 就是一个路径变量,表示用户 ID。为了在控制器方法中获取这个 ID 的值,可以使用 @PathVariable 注解:

复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        // 根据ID查找用户逻辑
        User user = userService.findById(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

在这个例子中,@PathVariable Long id 表示从 URL 路径 /users/{id} 中提取 id 参数,并将其转换为 Long 类型后赋值给方法参数 id。

多个路径变量

当需要同时处理多个路径变量时,可以在同一个方法签名中使用多个 @PathVariable 注解:

复制代码
@GetMapping("/users/{userId}/posts/{postId}")
public ResponseEntity<Post> getPostByUserAndPostId(
        @PathVariable("userId") Long userId,
        @PathVariable("postId") Long postId) {
    // 查找指定用户下的指定帖子逻辑
    Post post = postService.findByUserAndPostId(userId, postId);
    if (post != null) {
        return ResponseEntity.ok(post);
    } else {
        return ResponseEntity.notFound().build();
    }
}

注意:如果你的方法参数名与路径变量名相同,则可以直接省略 @PathVariable 的参数名称(如上面的第一个例子)。如果不同,则必须显式指定路径变量的名字,如 @PathVariable("userId") Long userId

默认值

有时你可能希望为路径变量提供默认值,以防万一客户端没有提供或提供了无效的值。Spring 5.1 及以上版本支持通过 defaultValue属性来设置默认值:

复制代码
@GetMapping("/users/{id}")
public ResponseEntity<String> getUserById(@PathVariable(value = "id", required = false) String id) {
    if (id == null || id.isEmpty()) {
        id = "default"; // 使用默认值
    }
    // 其他业务逻辑...
}

不过需要注意的是,默认值通常不适用于主键等关键字段,因为这些字段往往要求必须由客户端提供准确的值。

注意事项
  • 类型转换@PathVariable 支持自动进行简单的类型转换(如字符串到整数、长整型等),但如果遇到复杂的类型转换需求,可能需要自定义转换器。
  • 验证 :虽然 @PathVariable 可以结合 JSR 303 Bean Validation 注解(如 @NotNull, @Size 等)一起使用来进行参数验证,但在实际应用中,由于路径变量通常是唯一标识符,更多时候是通过服务层逻辑来确保其有效性。
  • 安全性:确保正确处理潜在的安全问题,例如防止注入攻击。避免直接将未经验证的路径变量拼接到 SQL 查询或其他敏感操作中。

总之,@PathVariable 是构建 RESTful Web 服务的一个非常有用的工具,它使得 URL 设计更加清晰且易于理解和维护。正确使用它可以大大提高代码的可读性和灵活性。

@RequestParam

@RequestParam 是 Spring 框架中的一个注解,主要用于将 HTTP 请求中的查询参数(Query Parameters)或表单数据绑定到控制器方法的参数上。它允许开发者轻松地从 GET 或 POST 请求中获取用户传递的参数值,并将其直接映射到方法参数中进行处理。这对于构建动态且灵活的 Web 应用程序非常重要。

@RequestParam的参数

  • **String value:**请求中传入参数的名称,如果不设置value值,则会默认为该变量名;
  • **String name:**等价与value,和value无本质上的差异,两个属性指定其一即可;
  • **boolean required:**是否必需,默认为 true,即 请求中必须包含该参数,如果没有包含,将会抛出异常(可选配置);
  • **String defaultValue:**参数的默认值,如果请求中没有同名的参数时,该变量默认为此值;

基本用法

假设你有一个 RESTful API 用来搜索用户信息,可以通过用户名或者邮箱来查找用户。你可以定义如下形式的 URL:

复制代码
GET /users?username=john&email=john@example.com

在这个例子中,username 和 email 就是查询参数。为了在控制器方法中获取这些参数的值,可以使用 @RequestParam 注解:

复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    @GetMapping
    public ResponseEntity<List<User>> searchUsers(
            @RequestParam(value = "username", required = false) String username,
            @RequestParam(value = "email", required = false) String email) {
        // 根据提供的参数查找用户逻辑
        List<User> users = userService.searchByUsernameOrEmail(username, email);
        return ResponseEntity.ok(users);
    }
}

在这个例子中:

  • @RequestParam(value = "username", required = false) 表示从请求中提取名为 username 的查询参数,并将其赋值给方法参数 username。如果该参数不存在,则不会抛出异常,因为 required = false。
  • 类似地,@RequestParam(value = "email", required = false) 处理 email 参数。

默认值

有时你可能希望为某些参数提供默认值,以防万一客户端没有提供或提供了无效的值。可以通过 defaultValue 属性来设置默认值:

复制代码
@GetMapping
public ResponseEntity<List<User>> searchUsers(
        @RequestParam(value = "page", defaultValue = "1") int page,
        @RequestParam(value = "size", defaultValue = "20") int size) {
    // 分页查询用户逻辑
    Pageable pageable = PageRequest.of(page - 1, size);
    Page<User> userPage = userService.findAll(pageable);
    return ResponseEntity.ok(userPage.getContent());
}

在这个例子中,如果没有提供 page 或 size 参数,它们将分别被赋予默认值 1 和 20。

多个值

对于某些情况下,一个参数可能会有多个值(例如,多选框提交的数据)。可以使用数组或集合类型作为方法参数:

复制代码
@GetMapping("/filter")
public ResponseEntity<List<User>> filterUsers(@RequestParam("role") List<String> roles) {
    // 根据角色过滤用户逻辑
    List<User> filteredUsers = userService.findByRoles(roles);
    return ResponseEntity.ok(filteredUsers);
}

在这个例子中,roles 参数可以接受多个值,如 /filter?role=admin&role=user

必填与可选参数

通过设置 required 属性,可以指定某个参数是否为必填项。如果设置为 true(默认),则当请求中缺少该参数时会抛出 MissingServletRequestParameterException 异常;如果设置为 false,则即使缺少该参数也不会报错,参数值将为 null 或者使用默认值。

复制代码
@RequestParam(value = "username", required = true)

数据验证

虽然 @RequestParam 支持简单的类型转换(如字符串到整数、长整型等),但在实际应用中,通常还需要对参数进行更严格的验证。可以结合 JSR 303 Bean Validation 注解(如 @NotNull, @Size 等)一起使用来进行参数验证:

复制代码
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@GetMapping
public ResponseEntity<List<User>> searchUsers(
        @RequestParam @Size(min = 3, max = 50) String username,
        @RequestParam @NotNull String email) {
    // 验证后的业务逻辑...
}

注意事项

  • 安全性:确保正确处理潜在的安全问题,例如防止SQL注入攻击。避免直接将未经验证的参数拼接到 SQL 查询或其他敏感操作中。
  • 编码问题:对于包含特殊字符或非ASCII字符的参数值,注意URL编码和解码的问题,确保正确传输和解析。

总之,@RequestParam 是构建动态Web应用程序的一个非常有用的工具,它使得从HTTP请求中获取参数变得简单而直观。正确使用它可以大大提高代码的可读性和灵活性,同时也能增强系统的健壮性和安全性。

@RequestBody

@RequestBody 是 Spring 框架中的一个注解,主要用于将 HTTP 请求体(Request Body)中的内容绑定到控制器方法的参数上。它允许开发者直接处理客户端发送过来的 JSON、XML 或其他格式的数据,并将其转换为 Java 对象,从而简化了数据的接收和解析过程。

@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST等方式进行提交。

基本用法

假设你有一个 RESTful API 用来创建新用户。客户端会发送一个包含用户信息的 JSON 对象作为 POST 请求的主体。你可以使用 @RequestBody 注解将这个 JSON 数据映射到一个 Java 对象中:

复制代码
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
    // 处理新用户的逻辑...
    User savedUser = userService.save(user);
    return ResponseEntity.ok(savedUser);
}

在这个例子中:

@RequestBody User user 表示从请求体中读取 JSON 数据,并将其反序列化为 User 类型的对象。

Spring 会自动处理 JSON 到 Java 对象的转换,前提是已经配置了适当的 Jackson 或 Gson 等 JSON 解析库。

支持的数据格式

@RequestBody 可以处理多种数据格式,包括但不限于:

  • JSON:最常用的格式之一,适合表示结构化的数据。
  • XML:某些系统可能更倾向于使用 XML 格式。
  • 自定义格式 :如果需要支持特定的应用程序或行业标准格式,可以通过实现自己的消息转换器来扩展 @RequestBody 的功能。

为了确保 Spring 能够正确地解析和转换这些不同格式的数据,通常需要在请求头中指定 Content-Type,例如:

  • application/json:用于 JSON 格式的数据。
  • application/xml:用于 XML 格式的数据。

数据验证

结合 JSR 303 Bean Validation 注解(如 @NotNull, @Size 等),可以对传入的请求体进行验证,确保接收到的数据符合预期的要求:

复制代码
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
public class User {
    @NotEmpty(message = "用户名不能为空")
    private String username;
    @Size(min = 5, max = 20, message = "密码长度应在5到20个字符之间")
    private String password;
    // Getters and Setters...
}

@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
    // 验证后的业务逻辑...
}

在这个例子中,@Valid 注解告诉 Spring 在调用 createUser 方法之前先对 User 对象进行验证。如果验证失败,Spring 会自动返回一个包含错误信息的响应给客户端。

异常处理

当请求体不符合预期格式或无法正确解析时,可能会抛出异常。常见的异常包括:

  • HttpMessageNotReadableException:当请求体为空或者格式不正确时抛出。
  • MethodArgumentNotValidException:当使用 @Valid 注解且验证失败时抛出。

为了优雅地处理这些异常,可以使用 @ControllerAdvice 和 @ExceptionHandler 来全局捕获并定制响应:

复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<String> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
        return ResponseEntity.badRequest().body("请求体格式错误: " + ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationErrors(MethodArgumentNotValidException ex) {
        StringBuilder errors = new StringBuilder();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            errors.append(error.getDefaultMessage()).append(", ");
        });
        return ResponseEntity.badRequest().body(errors.toString());
    }
}

注意事项

  • 性能考虑:对于大型请求体或频繁的请求,应评估性能影响,并考虑使用流式处理或其他优化策略。
  • 安全性:避免直接将未经验证的请求体数据用于敏感操作,如数据库查询等,以防 SQL 注入或其他类型的攻击。
  • 大小限制:可以通过配置服务器或框架参数来限制请求体的最大允许大小,防止恶意用户上传过大的文件导致服务崩溃。

如果后端参数是一个对象,且该参数前是以**@RequestBody**修饰的,那么前端传递json参数时,必须满足以下要求:

后端**@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(@RequestBody**后的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值符合实体类的对应属性类型时,会调用实体类的setter方法,将值赋给该属性。

json字符串中,如果value为""的话,后端对应属性如果是String类型的,那么接受到的就是"",如果是后端属性的类型是Integer、Double等类型,那么接收到的就是null。

json字符串中,如果value为null的话,后端对应收到的就是null。

如果某个参数没有value的话,在传json字符串给后端时,要么干脆就不把该字段写到json字符串中;要么写value时, 必须有值,null 或""都行。

总之,@RequestBody 是构建 RESTful Web 应用程序的一个非常有用的工具,它使得从 HTTP 请求体中获取和处理复杂数据变得简单而直观。正确使用它可以大大提高代码的可读性和灵活性,同时也能增强系统的健壮性和安全性。

URL(统一资源定位符)

URL是Uniform Resource Locator的缩写,意为"统一资源定位符"。它是互联网上用来标识资源位置的地址,通常用于访问网页、文件、图片等网络资源。一个URL通常包含以下几个部分:

URL 的基本结构

一个典型的 URL 由以下几个部分组成:

复制代码
scheme://[userinfo@]host[:port]/path?query#fragment

scheme(协议) :指定用于访问资源的协议类型,如 httphttpsftp 等。这是 URL 中最前面的部分,后面跟有两个斜杠 //

示例:https

userinfo(用户信息,可选) :包含用户名和密码(已不推荐使用),用于认证目的。如果存在,则以 username:password@ 的形式出现在主机名之前。

示例:alice:bob@

host(主机名):表示托管资源的服务器地址,可以是域名或 IP 地址。

示例:example.com

port(端口号,可选):指明服务器监听的服务端口,默认情况下,HTTP 使用 80 端口,HTTPS 使用 443 端口。如果使用默认端口,通常省略此部分。

示例::8080

path(路径):指定资源在服务器上的具体位置,类似于文件系统的目录路径。它可以包括多个层级。

示例:/articles/2023/12/new-article.html

query(查询字符串,可选) :包含一系列键值对参数,用于向服务器传递额外信息。查询字符串以问号 ? 开始,各个参数之间用与号 & 分隔。

示例:?search=RESTful&lang=en

fragment(片段标识符,可选) :用于指示文档内的某个特定部分,通常用于 HTML 页面中的锚点链接。片段标识符以井号 # 开始。

示例:#section-3

完整 URL 示例

复制代码
https://user:pass@example.com:8080/articles/2023/12/new-article.html?search=RESTful&lang=en#section-3

在这个例子中:

https 是协议。

user:pass@ 是用户信息(尽管这种做法已不推荐)。

example.com 是主机名。

:8080 是端口号。

/articles/2023/12/new-article.html 是路径。

?search=RESTful&lang=en 是查询字符串。

#section-3 是片段标识符。

URL 编码

由于 URL 只能包含某些字符集(如 ASCII 字符),因此对于不在允许范围内的字符(如空格、非英文字符等),需要进行编码处理。URL 编码遵循 RFC 3986 标准,使用百分号 % 后接两位十六进制数来表示特殊字符。

例如:

  • 空格会被编码为 %20
  • 汉字"中"可能会被编码为 %E4%B8%AD

URL 的作用

资源定位

URL 提供了一种标准化的方式来唯一地标识和定位互联网上的任何资源。

导航

用户可以通过点击链接或直接输入 URL 来浏览不同的网页和服务。

交互

开发人员利用 URL 来构建 RESTful API,通过不同的 HTTP 方法(如 GET、POST、PUT、DELETE)对资源进行 CRUD 操作。

搜索引擎优化(SEO)

良好的 URL 结构有助于提高网站在搜索引擎结果页面中的排名,因为清晰且描述性的 URL 更容易被索引和理解。

总之,URL 是互联网通信的基础之一,它不仅简化了我们访问在线资源的过程,也为开发者提供了强大的工具来设计和实现各种网络应用。正确理解和使用 URL 对于创建高效、易用且安全的 Web 应用至关重要。

若本文存在问题,欢迎批评指出,谢谢!

相关推荐
葫芦和十三6 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp6 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑7 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯7 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan10 小时前
多Agent之间的区别
后端
青石路11 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充12 小时前
1.面向对象设计思想
后端
IT_陈寒12 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro13 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗13 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端