
在 SpringBoot 开发中,处理 HTTP 请求参数是我们每天都要面对的工作。而@RequestParam和@RequestBody这两个注解,就像是我们手中的两把利剑,既能高效解决问题,用不好也可能 "误伤" 自己。
作为一名 Java 开发者,我见过太多因为对这两个注解理解不透彻而导致的生产事故:有人把 JSON 参数用@RequestParam接收导致接口一直报 400 错误,有人在 GET 请求里用@RequestBody接收参数结果百思不得其解,还有人因为不清楚参数绑定的优先级而写出难以维护的代码...
本文将从基础到进阶,全方位剖析这两个注解的使用场景、底层原理、常见问题和最佳实践,带你彻底掌握它们的精髓,让你的接口参数处理代码既优雅又健壮!
@RequestParam是 SpringMVC 中最常用的注解之一,用于从 HTTP 请求的参数中提取数据并绑定到控制器方法的参数上。它看似简单,实则暗藏玄机。
@RequestParam主要用于获取 HTTP 请求中的查询参数(query parameters),也就是 URL 中?后面的键值对。例如在http://localhost:8080/user?name=张三&age=20这个 URL 中,name和age就是查询参数。
基本语法如下:
@RequestMapping("/user")
publicStringgetUser(
@RequestParamString name,
@RequestParamint age){
return"姓名:"+ name +",年龄:"+ age;
}
这段代码会自动从请求参数中获取name和age的值,并分别赋值给方法参数。
@RequestParam注解有三个重要属性,掌握它们能让你更灵活地使用这个注解:
value/name:指定请求参数的名称,如果方法参数名与请求参数名一致,可以省略required:表示该参数是否必须,默认值为truedefaultValue:指定参数的默认值,当参数不存在时使用下面通过示例详细说明:
当方法参数名与请求参数名不一致时,需要通过value或name属性指定:
@RequestMapping("/user")
publicStringgetUser(
@RequestParam(value ="userName")String name,// 请求参数是userName,绑定到name变量
@RequestParam(name ="userAge")int age){// 请求参数是userAge,绑定到age变量
return"姓名:"+ name +",年龄:"+ age;
}
此时,我们需要用http://localhost:8080/user?userName=张三&userAge=20来访问接口。
当required属性设为false时,表示该参数不是必需的:
@RequestMapping("/search")
publicStringsearch(
@RequestParamString keyword,
@RequestParam(required =false)Integer page){// page参数非必需
// 如果page为null,则默认查询第一页
int currentPage =(page ==null)?1: page;
return"搜索关键词:"+ keyword +",页码:"+ currentPage;
}
这个接口可以通过http://localhost:8080/search?keyword=java(不带 page 参数)访问,也可以通过http://localhost:8080/search?keyword=java&page=2访问。
如果一个required=true的参数未提供,Spring 会抛出MissingServletRequestParameterException异常,导致接口返回 400 Bad Request 错误。
defaultValue属性可以为参数设置默认值,当参数不存在时自动使用该值:
@RequestMapping("/search")
publicStringsearch(
@RequestParamString keyword,
@RequestParam(defaultValue ="1")int page,// 默认页码为1
@RequestParam(defaultValue ="10")int size){// 默认每页10条
return"搜索关键词:"+ keyword +",页码:"+ page +",每页条数:"+ size;
}
这个接口有以下几种访问方式:
http://localhost:8080/search?keyword=java → page=1, size=10http://localhost:8080/search?keyword=java&page=2 → page=2, size=10http://localhost:8080/search?keyword=java&page=3&size=20 → page=3, size=20需要注意的是,设置了defaultValue后,required属性会自动变为false,即使你显式设置required=true也会被忽略。
@RequestParam不仅能处理基本类型,还能直接绑定数组和集合类型的参数。
当请求参数有多个相同名称时,可以用数组接收:
@RequestMapping("/array")
publicStringhandleArray(@RequestParamString[] hobbies){
return"爱好:"+Arrays.toString(hobbies);
}
访问http://localhost:8080/array?hobbies=读书&hobbies=运动&hobbies=编程,会得到结果:爱好:[读书, 运动, 编程]
处理集合时,需要指定value属性,并且最好指定required属性:
@RequestMapping("/list")
publicStringhandleList(
@RequestParam(value ="ids", required =false)List<Integer> ids){
return"IDs:"+ ids;
}
访问http://localhost:8080/list?ids=1&ids=2&ids=3,会得到结果:IDs:[1, 2, 3]
如果需要处理集合,Spring 需要知道集合的泛型类型。对于基本类型,Spring 可以自动推断,但对于自定义类型,可能需要额外配置。
@RequestParam适用于以下场景:
@RequestParam最主要的应用场景application/x-www-form-urlencoded格式提交,其参数也可以用@RequestParam接收@RequestParam接收当请求参数的类型与方法参数的类型不匹配时,会抛出TypeMismatchException异常。
例如,下面的接口期望接收一个整数:
@RequestMapping("/number")
publicStringhandleNumber(@RequestParamint number){
return"数字:"+ number;
}
如果我们用http://localhost:8080/number?number=abc访问,会得到 400 错误,因为 "abc" 无法转换为整数。
解决方案:
Integer代替int),这样当参数类型不匹配时会得到null而不是抛出异常@ExceptionHandler全局处理类型转换异常// 改进方案
@RequestMapping("/number")
publicStringhandleNumber(@RequestParam(required =false)Integer number){
if(number ==null){
return"请提供有效的数字参数";
}
return"数字:"+ number;
}
当请求参数包含中文时,可能会出现乱码问题。这通常是由于 URL 编码与服务器解码使用的字符集不一致导致的。
解决方案:
@Configuration
publicclassWebConfigimplementsWebMvcConfigurer{
@Bean
publicFilterRegistrationBean<CharacterEncodingFilter>characterEncodingFilter(){
FilterRegistrationBean<CharacterEncodingFilter> registrationBean =newFilterRegistrationBean<>();
CharacterEncodingFilter filter =newCharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
在 SpringBoot 2.x 及以上版本,默认已经配置了 UTF-8 编码,一般不需要额外配置,但如果出现乱码问题,上述配置可以作为解决方案。
有时请求参数名可能与 Java 关键字重名(如int、class等),这时候直接使用关键字作为方法参数名会导致编译错误。
解决方案:使用@RequestParam的value属性指定参数名,方法参数名使用其他合法名称
@RequestMapping("/keyword")
publicStringhandleKeyword(@RequestParam(value ="class")String className){
return"班级:"+ className;
}
这样就可以处理http://localhost:8080/keyword?class=一班这样的请求了。
@RequestBody注解用于将 HTTP 请求的正文(body)解析并绑定到控制器方法的参数上。它主要用于处理非application/x-www-form-urlencoded格式的请求数据,如 JSON、XML 等。
@RequestBody通常用于 POST、PUT 等 HTTP 方法,这些方法的参数通常放在请求体中而不是 URL 中。
基本语法如下:
@PostMapping("/user")
publicStringcreateUser(@RequestBodyUser user){
return"创建用户:"+ user.getName()+",年龄:"+ user.getAge();
}
// User类定义
publicclassUser{
privateString name;
privateint age;
// 必须有默认构造函数
publicUser(){}
// getter和setter方法
publicStringgetName(){return name;}
publicvoidsetName(String name){this.name = name;}
publicintgetAge(){return age;}
publicvoidsetAge(int age){this.age = age;}
}
当我们向http://localhost:8080/user发送 POST 请求,并且请求体是 JSON 格式:
{
"name":"张三",
"age":20
}
Spring 会自动将 JSON 数据解析为 User 对象,并传递给 createUser 方法。
@RequestBody支持多种数据格式,这取决于项目中配置的消息转换器(MessageConverter)。SpringBoot 默认配置了以下几种常用的消息转换器:
application/x-www-form-urlencoded格式,但通常用@RequestParam处理multipart/form-data格式,用于文件上传要使用特定的数据格式,需要确保请求头中的Content-Type与实际数据格式一致:
Content-Type: application/jsonContent-Type: application/xml 或 text/xml虽然@RequestBody主要用于绑定复杂对象,但也可以用于绑定简单类型,如字符串、数字等:
@PostMapping("/message")
publicStringhandleMessage(@RequestBodyString message){
return"收到消息:"+ message;
}
当发送包含纯文本的请求体时,这段代码会直接将文本内容绑定到 message 参数。
但需要注意的是,@RequestBody一次只能绑定一个简单类型参数,因为整个请求体只能解析为一个值。如果需要传递多个简单参数,应该使用对象包装它们,或者考虑使用@RequestParam。
@RequestBody可以直接绑定集合类型,例如List、Map等:
@PostMapping("/users")
publicStringcreateUsers(@RequestBodyList<User> users){
return"创建用户数量:"+ users.size()+",第一个用户:"+ users.get(0).getName();
}
对应的 JSON 请求体:
[
{"name":"张三","age":20},
{"name":"李四","age":22}
]
@PostMapping("/info")
publicStringhandleInfo(@RequestBodyMap<String,Object> info){
return"姓名:"+ info.get("name")+",爱好:"+ info.get("hobby");
}
对应的 JSON 请求体:
{
"name":"张三",
"hobby":"编程"
}
@RequestBody适用于以下场景:
@RequestBody能自动完成对象映射@RequestBody接收数据@RequestBody能简化参数绑定这是使用@RequestBody时最常见的错误,通常有以下几种原因:
@RequestBody标注的参数是必需的,但请求体为空时解决方案:
required属性(默认为 true):@PostMapping("/user")
publicStringcreateUser(@RequestBody(required =false)User user){
if(user ==null){
return"未提供用户信息";
}
return"创建用户:"+ user.getName();
}
@RestControllerAdvice
publicclassGlobalExceptionHandler{
@ExceptionHandler(HttpMessageNotReadableException.class)
publicStringhandleHttpMessageNotReadableException(HttpMessageNotReadableException e){
return"请求数据格式错误:"+ e.getMessage();
}
}
当 JSON 中的字段名与 Java 对象的字段名不一致时,会导致字段无法正确绑定。
解决方案:使用 Jackson 的@JsonProperty注解指定映射关系
publicclassUser{
@JsonProperty("user_name")// 与JSON中的user_name字段对应
privateString name;
@JsonProperty("user_age")// 与JSON中的user_age字段对应
privateint age;
// getter、setter和构造函数省略
}
这样,即使 JSON 中的字段名与 Java 对象不同,也能正确绑定:
{
"user_name":"张三",
"user_age":20
}
日期类型的 JSON 字符串(如 "2023-10-01" 或 "2023/10/01 12:00:00")在默认情况下可能无法正确解析为 Java 的Date或LocalDateTime类型。
解决方案:
@JsonFormat注解指定日期格式:publicclassOrder{
privateString orderId;
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8")
privateLocalDateTime createTime;
// getter、setter和构造函数省略
}
@Configuration
publicclassJacksonConfig{
@Bean
publicObjectMapperobjectMapper(){
ObjectMapper objectMapper =newObjectMapper();
JavaTimeModule javaTimeModule =newJavaTimeModule();
// 配置LocalDateTime的序列化和反序列化格式
javaTimeModule.addSerializer(LocalDateTime.class,
newLocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDateTime.class,
newLocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
objectMapper.registerModule(javaTimeModule);
// 忽略未知属性,避免因JSON中有额外字段而报错
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
return objectMapper;
}
}
虽然@RequestParam和@RequestBody都是用于获取请求参数的注解,但它们在很多方面都有显著区别。理解这些区别是正确使用它们的关键。
这是两者最根本的区别:
@RequestParam:获取 URL 查询参数(query parameters)或表单提交的参数(application/x-www-form-urlencoded)@RequestBody:获取 HTTP 请求体(request body)中的数据形象地说,@RequestParam处理的是 URL 中?后面的数据,而@RequestBody处理的是 HTTP 请求中独立于 URL 的正文部分。
@RequestParam:通常用于 GET 方法,也可以用于 POST、PUT 等方法(当参数在 URL 或表单中时)@RequestBody:通常用于 POST、PUT、PATCH 等方法,不建议用于 GET 方法(GET 方法通常没有请求体)虽然 HTTP 规范并没有禁止 GET 方法有请求体,但大多数浏览器和服务器对 GET 请求体的支持并不完善,因此实际开发中应避免在 GET 方法中使用@RequestBody。
@RequestParam:主要处理键值对形式的数据(key1=value1&key2=value2)@RequestBody:主要处理结构化数据,如 JSON、XML 等@RequestParam可以处理表单提交的application/x-www-form-urlencoded格式数据,而@RequestBody可以处理更复杂的结构化数据。
@RequestParam:可以同时获取多个参数,每个参数对应 URL 或表单中的一个键值对@RequestBody:通常一次只能绑定一个参数,因为整个请求体只能解析为一个对象如果需要获取多个参数,@RequestBody通常会将这些参数封装到一个 Java 对象中。
场景 | 推荐使用 | 原因 |
|---|---|---|
获取简单参数 | @RequestParam | 简单直接,无需额外对象 |
表单提交 | @RequestParam | 表单默认使用 application/x-www-form-urlencoded 格式 |
RESTful API 创建资源 | @RequestBody | 适合传递复杂对象,符合 REST 风格 |
传递大量数据 | @RequestBody | URL 长度有限制,请求体可以容纳更多数据 |
传递复杂对象 | @RequestBody | 自动映射复杂对象,包括嵌套结构 |
分页查询参数 | @RequestParam | 分页参数通常是简单值,适合放在 URL 中 |
搜索过滤条件 | @RequestParam 或 @RequestBody | 简单条件用 @RequestParam,复杂条件用 @RequestBody |
在实际开发中,我们经常需要同时使用@RequestParam和@RequestBody:
@PostMapping("/orders")
publicStringcreateOrder(
@RequestParamString userId,// 从URL参数获取用户ID
@RequestParam(required =false)String couponCode,// 从URL参数获取优惠券代码(可选)
@RequestBodyOrderRequest orderRequest){// 从请求体获取订单详情
return"用户ID:"+ userId +
",优惠券:"+(couponCode !=null? couponCode :"无")+
",订单商品:"+ orderRequest.getProducts().size()+"件";
}
// OrderRequest类
publicclassOrderRequest{
privateList<Product> products;
privateString shippingAddress;
privateString paymentMethod;
// getter、setter和构造函数省略
}
这个接口可以通过以下方式调用:
http://localhost:8080/orders?userId=123&couponCode=SAVE10{
"products":[
{"id":"p1","name":"手机","quantity":1},
{"id":"p2","name":"耳机","quantity":2}
],
"shippingAddress":"北京市朝阳区",
"paymentMethod":"支付宝"
}
这种混合使用的方式结合了两种注解的优势,适用于既需要简单参数又需要复杂对象的场景。
掌握了@RequestParam和@RequestBody的基本用法后,我们来看看一些高级应用和最佳实践,帮助你写出更优雅、更健壮的代码。
无论是@RequestParam还是@RequestBody绑定的参数,都需要进行合法性校验,以确保系统安全和数据正确。
Spring 支持 JSR-303/JSR-380 规范的参数校验,可以通过注解轻松实现:
@PostMapping("/user")
publicStringcreateUser(
@RequestParam@NotBlank(message ="用户名不能为空")String username,
@RequestParam@Min(value =18, message ="年龄不能小于18岁")int age,
@RequestBody@ValidUserDetail detail){
return"创建用户:"+ username;
}
// UserDetail类
publicclassUserDetail{
@NotBlank(message ="邮箱不能为空")
@Email(message ="邮箱格式不正确")
privateString email;
@Size(min =6, max =20, message ="密码长度必须在6-20之间")
privateString password;
// getter、setter和构造函数省略
}
要使用参数校验,需要添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
并在全局异常处理器中处理校验失败的异常:
@RestControllerAdvice
publicclassGlobalExceptionHandler{
@ExceptionHandler(MethodArgumentNotValidException.class)
publicStringhandleMethodArgumentNotValid(MethodArgumentNotValidException e){
// 获取所有校验失败的消息
List<String> errorMessages = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
return"参数校验失败:"+String.join(";", errorMessages);
}
@ExceptionHandler(ConstraintViolationException.class)
publicStringhandleConstraintViolation(ConstraintViolationException e){
List<String> errorMessages = e.getConstraintViolations().stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.toList());
return"参数校验失败:"+String.join(";", errorMessages);
}
}
当@RequestParam和@RequestBody不能满足需求时,我们可以自定义参数解析器来处理特殊的参数绑定逻辑。
例如,我们可以创建一个@CurrentUser注解,用于直接获取当前登录用户:
// 自定义注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceCurrentUser{
}
// 自定义参数解析器
@Component
publicclassCurrentUserArgumentResolverimplementsHandlerMethodArgumentResolver{
@Override
publicbooleansupportsParameter(MethodParameter parameter){
// 只处理带有@CurrentUser注解的User类型参数
return parameter.hasParameterAnnotation(CurrentUser.class)&&
parameter.getParameterType().equals(User.class);
}
@Override
publicObjectresolveArgument(MethodParameter parameter,ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,WebDataBinderFactory binderFactory)throwsException{
// 从请求中获取当前登录用户ID(实际项目中可能从Token、Session等获取)
String userId = webRequest.getHeader("X-User-Id");
// 根据用户ID查询用户信息(实际项目中可能是从数据库或缓存获取)
User currentUser =newUser();
currentUser.setId(userId);
currentUser.setName("当前登录用户");
return currentUser;
}
}
// 注册解析器
@Configuration
publicclassWebConfigimplementsWebMvcConfigurer{
@Override
publicvoidaddArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers){
resolvers.add(newCurrentUserArgumentResolver());
}
}
// 使用示例
@GetMapping("/profile")
publicStringgetProfile(@CurrentUserUser currentUser){
return"当前登录用户:"+ currentUser.getName()+",ID:"+ currentUser.getId();
}
这个例子展示了如何通过自定义参数解析器扩展 Spring 的参数绑定能力,在实际项目中非常实用。
文件上传是 Web 开发中的常见需求,Spring 提供了@RequestParam结合MultipartFile来处理文件上传:
@PostMapping("/upload")
publicStringuploadFile(
@RequestParamString description,// 普通参数
@RequestParam("file")MultipartFile file){// 文件参数
if(file.isEmpty()){
return"请选择要上传的文件";
}
try{
// 获取文件名
String fileName = file.getOriginalFilename();
// 获取文件内容
byte[] bytes = file.getBytes();
// 保存文件(实际项目中通常保存到磁盘或云存储)
Path path =Paths.get("uploads/"+ fileName);
Files.write(path, bytes);
return"文件上传成功:"+ fileName +",描述:"+ description;
}catch(IOException e){
return"文件上传失败:"+ e.getMessage();
}
}
多文件上传可以使用MultipartFile数组:
@PostMapping("/upload-multiple")
publicStringuploadMultipleFiles(
@RequestParam("files")MultipartFile[] files){
List<String> uploadedFiles =newArrayList<>();
for(MultipartFile file : files){
if(!file.isEmpty()){
try{
String fileName = file.getOriginalFilename();
Path path =Paths.get("uploads/"+ fileName);
Files.write(path, file.getBytes());
uploadedFiles.add(fileName);
}catch(IOException e){
return"文件"+ file.getOriginalFilename()+"上传失败:"+ e.getMessage();
}
}
}
return"成功上传文件:"+String.join(",", uploadedFiles);
}
需要注意的是,文件上传的表单需要设置enctype="multipart/form-data"属性:
<formmethod="post"action="/upload"enctype="multipart/form-data">
<inputtype="text"name="description"/>
<inputtype="file"name="file"/>
<buttontype="submit">上传</button>
</form>
在 SpringBoot 中,默认的文件上传大小限制可能比较小,可以通过配置修改:
# 单个文件大小限制
spring.servlet.multipart.max-file-size=10MB
# 总请求大小限制
spring.servlet.multipart.max-request-size=100MB
在 RESTful API 设计中,合理使用@RequestParam和@RequestBody能让 API 更加规范和易用:
GET 请求:使用@RequestParam获取查询参数,用于查询、过滤、分页等操作
@GetMapping("/users")
publicPage<User>getUsers(
@RequestParam(required =false)String name,// 可选的姓名过滤
@RequestParam(defaultValue ="0")int page,// 页码,默认0
@RequestParam(defaultValue ="10")int size){// 每页条数,默认10
// 查询逻辑
}
POST 请求:使用@RequestBody创建资源,请求体包含资源的完整信息
@PostMapping("/users")
publicUsercreateUser(@RequestBody@ValidUserCreateRequest request){
// 创建用户逻辑
}
PUT 请求:使用@RequestBody更新资源,通常包含资源的完整信息
@PutMapping("/users/{id}")
publicUserupdateUser(
@PathVariableString id,// 路径参数,用户ID
@RequestBody@ValidUserUpdateRequest request){
// 更新用户逻辑
}
PATCH 请求:使用@RequestBody部分更新资源,通常只包含需要更新的字段
@PatchMapping("/users/{id}")
publicUserpartialUpdateUser(
@PathVariableString id,
@RequestBodyMap<String,Object> updates){// 只包含需要更新的字段
// 部分更新逻辑
}
DELETE 请求:通常使用路径参数指定要删除的资源 ID,复杂情况可结合@RequestParam
@DeleteMapping("/users/{id}")
publicvoiddeleteUser(@PathVariableString id){
// 删除用户逻辑
}
遵循这些实践可以使你的 API 更加直观和易用,符合 RESTful 设计原则。
了解@RequestParam和@RequestBody的底层原理,不仅能帮助我们更好地理解它们的行为,还能在遇到问题时更快地定位原因。
SpringMVC 处理一个 HTTP 请求的大致流程如下:
@RequestParam和@RequestBody的处理发生在第 4 步,由特定的参数解析器完成。
@RequestParam的解析主要由RequestParamMethodArgumentResolver类完成,其工作流程如下:
@RequestParam注解request.getParameter(name)获取参数值application/x-www-form-urlencoded)request.getParameter(name)方法的底层实现会根据请求的Content-Type不同而有不同的处理逻辑:
Content-Type为application/x-www-form-urlencoded,从请求体中解析参数@RequestBody的解析主要由RequestResponseBodyMethodProcessor类完成,其工作流程如下:
@RequestBody注解Content-Type选择合适的 HttpMessageConverterHttpMessageConverter 是一个接口,不同的实现类处理不同的数据格式:
MappingJackson2HttpMessageConverter:处理 JSON 格式(默认包含)Jaxb2RootElementHttpMessageConverter:处理 XML 格式(需要相应依赖)StringHttpMessageConverter:处理字符串格式FormHttpMessageConverter:处理表单数据Spring 会根据Content-Type请求头和目标类型来选择最合适的 HttpMessageConverter。
SpringMVC 中有多种参数解析器,它们有不同的优先级:
RequestParamMethodArgumentResolver(处理@RequestParam)RequestResponseBodyMethodProcessor(处理@RequestBody)PathVariableMethodArgumentResolver等)这种优先级意味着在解析参数时,Spring 会先尝试用@RequestParam的解析器处理,再尝试用@RequestBody的解析器处理。
但实际上,由于@RequestParam和@RequestBody的适用场景不同(一个处理参数,一个处理请求体),它们很少会产生冲突。
@RequestParam和@RequestBody是 Java 面试中经常被问到的知识点,掌握以下常见问题的答案,能让你在面试中更有优势。
这是最基础也最常被问到的问题,回答时应涵盖数据位置、适用场景、支持的数据格式等方面:
@RequestParam和@RequestBody都是 SpringMVC 中用于获取请求数据的注解,主要区别如下:
@RequestParam用于获取 URL 查询参数或表单参数,@RequestBody用于获取请求体中的数据@RequestParam常用于 GET 方法,@RequestBody常用于 POST、PUT 等方法@RequestParam处理键值对形式的数据,@RequestBody处理 JSON、XML 等结构化数据@RequestParam可以同时获取多个参数,@RequestBody通常一次处理一个对象@RequestParam,复杂对象用@RequestBody回答这个问题时,应结合具体场景说明:
选择使用哪个注解主要取决于参数的类型、位置和复杂度:
@RequestParam@RequestBody@RequestParam,因为 GET 请求的参数通常在 URL 中@RequestBodyapplication/x-www-form-urlencoded)使用@RequestParam@RequestBody简单来说,简单参数用@RequestParam,复杂对象用@RequestBody;URL 中的参数用@RequestParam,请求体中的数据用@RequestBody。
这个问题考察对 HTTP 规范和 SpringMVC 实现的理解:
从技术上讲,SpringMVC 允许在 GET 请求中使用@RequestBody,但这是不推荐的做法,原因如下:
@RequestParam的典型应用场景因此,实际开发中应避免在 GET 请求中使用@RequestBody,坚持使用@RequestParam获取 GET 请求的参数。
这个问题考察对异常处理的理解:
当@RequestParam的required属性为true(默认值)但请求中没有对应的参数时,SpringMVC 会抛出MissingServletRequestParameterException异常。
如果没有全局异常处理器处理这个异常,SpringMVC 会返回 400 Bad Request 响应给客户端。
为了提供更友好的错误信息,建议通过全局异常处理器捕获并处理这个异常:
@RestControllerAdvice
publicclassGlobalExceptionHandler{
@ExceptionHandler(MissingServletRequestParameterException.class)
publicStringhandleMissingParameter(MissingServletRequestParameterException e){
return"缺少必要参数:"+ e.getParameterName();
}
}
另外,可以通过设置required = false并提供默认值来避免这种异常:
@RequestParam(required =false, defaultValue ="default")String param
这个问题考察对实际开发中常见问题的处理能力:
@RequestBody处理日期类型需要考虑 JSON 字符串到日期对象的转换,常用的解决方案有:
使用 @JsonFormat 注解:在实体类的日期字段上添加注解,指定日期格式
@JsonFormat(pattern ="yyyy-MM-dd HH:mm:ss", timezone ="GMT+8")
privateLocalDateTime createTime;
配置全局日期格式:通过配置 Jackson 的 ObjectMapper,设置全局的日期序列化和反序列化格式
@Bean
publicObjectMapperobjectMapper(){
ObjectMapper objectMapper =newObjectMapper();
JavaTimeModulemodule=newJavaTimeModule();
module.addSerializer(LocalDateTime.class,
newLocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
module.addDeserializer(LocalDateTime.class,
newLocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
objectMapper.registerModule(module);
return objectMapper;
}
使用自定义序列化器 / 反序列化器:对于特殊的日期格式,可以编写自定义的序列化器和反序列化器
选择哪种方案取决于项目需求:单个字段的特殊格式用@JsonFormat,统一的日期格式用全局配置。
@RequestParam和@RequestBody是 SpringBoot 开发中处理 HTTP 请求参数的核心注解,掌握它们的使用方法和底层原理,对于编写高质量的 Web 应用至关重要。