• Spring常见问题解决 - @RequestParam和@PathVariable的区别以及400报错问题


    一. @RequestParam和@PathVariable的区别

    • @RequestParam:主要用于获取请求体中的参数。(针对请求体) URL上的参数,? 后面的。
    • @PathVariable:主要用于获取URL上的参数,一般结合Mapping中设置的URL中的{xxx}来使用。(针对URL

    看下第一种@RequestParam的使用:

    @Controller
    public class MyController {
        @PostMapping("/testRequestParam")
        @ResponseBody
        public String hello(@RequestParam("name") String name, @RequestParam("age") Integer age) {
            return "name: " + name + ",age: " + age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    访问http://localhost:8080/testRequestParam?name=ljj&age=20:
    在这里插入图片描述

    看下第二种@PathVariable的使用:

    @GetMapping("/testPathVariable/{name}/{age}")
    @ResponseBody
    public String testPathVariable(@PathVariable("name") String name, @PathVariable("age") Integer age) {
        return "name: " + name + ",age: " + age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    访问:http://localhost:8080/testPathVariable/ljj/20
    在这里插入图片描述

    从这两个使用案例来看,其实这两个注解的区别就比较明显了。

    二. @RequestParam 的不正确编写导致的400报错

    在上面的案例中,我们通过URL携带了两个参数:agename。并且后端能够正常地接收并输出。那么如果我们指携带一个参数,会怎么样呢?

    2.1 案例复现

    在这里插入图片描述

    这是典型的一个案例,即我们在代码编写的时候:

    • 有时候可能URL上携带的参数比较多,但是我们往往忽略了一些参数是否是可选的,导致出现了这样的报错。
    • 当然,如果你多传一个参数,是不会有啥影响的,只要后端代码没有指定,就不会接收。

    2.2 原理分析

    Spring中对于@RequestParam注解的解析,是通过RequestParamMethodArgumentResolver类来完成参数解析的:

    public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
    	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
    		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    		MethodParameter nestedParameter = parameter.nestedIfOptional();
    		// ..
    		// 获取指定参数名称的值
    		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    		if (arg == null) {
    			// 如果对应参数的默认值不是空,那么就使用默认值
    			if (namedValueInfo.defaultValue != null) {
    				arg = resolveStringValue(namedValueInfo.defaultValue);
    			}
    			// 否则,查看这个参数的值是否是必须设置的,如果必须设置,但是没有对应的值,那么按错误处理
    			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
    				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
    			}
    			// 如果不是必须的,就按照null来处理
    			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    		}
    		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
    			arg = resolveStringValue(namedValueInfo.defaultValue);
    		}
    		// ..
    		return arg;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    这里我们可以看出,对于@RequestParam的使用而言,其值的解析会有这么几个分支处理:

    • 如果这个参数对应的值存在,就直接返回。
    • 如果这个参数对应的值不存在,那么先看这个参数是否设置了默认值,若有,则返回默认值。
    • 如果默认值也没有,那么看这个参数是否是required(默认为true),以及该参数本身是否可选isOptional()。如果满足这俩条件,但是没有可选的值,就报错。
    • 否则按照null处理。

    那么知道了原理,我们就可以在代码上做出对应的修改了。

    2.3 问题解决

    解决方案一:我们给对应的参数设置一个defaultValue默认值。

    @PostMapping("/testRequestParam")
    @ResponseBody
    public String hello(@RequestParam("name") String name,
                        @RequestParam(value = "age", defaultValue = "33") Integer age) {
        return "name: " + name + ",age: " + age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果:
    在这里插入图片描述


    解决方案二:设置requirefalse

    @PostMapping("/testRequestParam")
    @ResponseBody
    public String hello(@RequestParam("name") String name,
                        @RequestParam(value = "age", defaultValue = "10") Integer age) {
        return "name: " + name + ",age: " + age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果:
    在这里插入图片描述


    解决方案三:修改参数类型为 Optional

    @PostMapping("/testRequestParam")
    @ResponseBody
    public String hello(@RequestParam("name") String name,
                        @RequestParam(value = "age") Optional age) {
        return "name: " + name + ",age: " + age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果:
    在这里插入图片描述


    解决方案四:对这个可选参数添加注解@Nullable

    @PostMapping("/testRequestParam")
    @ResponseBody
    public String hello(@RequestParam("name") String name,
                        @RequestParam(value = "age") @Nullable Integer age) {
        return "name: " + name + ",age: " + age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果:
    在这里插入图片描述

    2.4 总结

    • 如果希望从URL路径上获取参数,请使用@PathVariable
    • 如果希望从URL上携带的参数上获取参数,请使用@RequestParam
    • 使用@RequestParam的时候,请注意对应的参数是否是必传的,如果不是,请加上require=false,或者其他方式来避免报400错误(缺少属性值)。
  • 相关阅读:
    Ioc容器加载过程-bean生命周期源码解析
    idea模板设置
    电力巡检智能化系统
    linux后台运行java项目/ jar包:nohup 命令
    Java 面试被问框架源码看过吗?70 道 SSM 面试题及学习笔记值得收藏
    一个超好看的音乐网站设计与实现(HTML+CSS)
    我的创作纪念日
    SSM框架学习记录-Spring_day02
    反转字符串中的单词-力扣
    python 函数参数 必选参数 默认参数 可变参数 关键字参数
  • 原文地址:https://blog.csdn.net/Zong_0915/article/details/126544712