换种方式看后端参数接收、建议躺着看!!!

news2024/12/24 3:11:05


持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情 常用的接收参数注解@RequestParam
@PathVariable
@RequestBody
先看个例子@RestController
public class testController {

@RequestMapping(value = "/demo1",method = RequestMethod.GET)
public String uploadFolder(String username,String password) {
    System.out.println(username);
    System.out.println(password);
    return "ok";
}

}
写了个很简单的例子传递两个参数一个是用户名一个是密码、请求方式是get请求、那我们肯定是直接访问一下这个127.0.0.1:8080/demo1?username=abcd&password=123请求就能接收到前端输入的


@RequestParam可以看到这种简单简单的传参是很方便的、你也可以去使用@RequestParam()注解去去一个接收参数的别名、如@RequestMapping(value = "/demo1",method = RequestMethod.GET)
public String uploadFolder(@RequestParam("username1") String username,

                       @RequestParam("password1") String password) {
System.out.println(username);
System.out.println(password);
return "ok";

}
则请求参数就是127.0.0.1:8080/demo1?username1=abcd&password1=123就是以这种形式传递到后端。在html中页面如何提交?<form action="/demo1" method="get">

<input type="text" name="username">
<input type="text" name="password">
<button type="submit">提交</button>

</form>


@PathVariable再来说另一种简单的就是@PathVariable()注解使用、这种你就可以理解为它回去找url中的内容、而不是作为?或者&拼接传递过来的内容。@RequestMapping(value = "/demo2/{id}",method = RequestMethod.GET)
public String demo2(@PathVariable(value = "id") String id,

                       @RequestParam("username") String username,
                       @RequestParam("password") String password) {
System.out.println(id);
System.out.println(username);
System.out.println(password);
return "ok";

}
可以看到他标记了id为url拼接的参数、http://127.0.0.1:8981/demo2/2...、如果请求是这个http://127.0.0.1:8981/demo2?u...会怎么样?肯定是找不到对应请求......


那上面这种如何在html中使用?<form action="/demo2/213124" method="get">

<input type="text" name="username">
<input type="text" name="password">
<button type="submit">提交</button>

</form>
也就是在action填写参数的占位内容找到具体的Controller接口方法。@RequestBody还有一种接收的方式@RequestBody这种是接收请求体中的内容、下面这方式是将接收一个json字符串的格式数据@RequestMapping(value = "/demo3",method = RequestMethod.GET)
public String demo3(@RequestBody String body) {

System.out.println(body);
return "ok";

}


Map接收或者换成Map做接收的参数、就会将数据解析成键值对。@RequestMapping(value = "/demo3",method = RequestMethod.GET)
public String demo3(@RequestBody Map<String,Object> body) {

System.out.println(body);
return "ok";

}
对象接收最最常见的就是发送json后端使用实体类来接收、并且Header要是"Content-Type", "application/json"@RequestMapping(value = "/demo4",method = RequestMethod.GET)
public String demo4(@RequestBody User user) {

System.out.println(user);
return "ok";

}
要注意的就是User对象要写get、set方法。表单提交然后这种在html提交需要怎么弄?<form action="/demo4" method="get">

<input type="text" name="id">
<input type="text" name="name">
<button type="submit">提交</button>

</form>
@RequestMapping(value = "/demo4")
public String demo4(User user) {

System.out.println(user);
return "ok";

}
把@RequestBody去掉后在form表单提交时不管是post还是get请求都能将数据映射成功。由此可以理解为@RequestBody只接受请求体中的数据、并且会校验Content-Type为application/json的数据。@RequestMapping(value = "/demo4")
public String demo4(@RequestBody User user) {

System.out.println(user);
return "ok";

}
如果加上@RequestBody后from表单使用get请求会出现2022-10-11 10:13:05.902 WARN 11440 --- [nio-8981-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String com.wei.controller.testController.demo4(com.wei.pojo.User)]
使用get请求的话参数是跟在url后的所以他会找不到请求体的内容。如果加上@RequestBody后from表单使用post请求会出现2022-10-11 10:10:30.073 WARN 12856 --- [nio-8981-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
而post请求参数是携带在请求体中的、但是它的Content-Type是application/x-www-form-urlencoded不是application/json所以会导致不支持的类型。组合方式这三种方式也可以组合使用@PathVariable+@RequestParam、@PathVariable+@RequestBody、@PathVariable+@RequestParam+@RequestBody。玩法很多种、但别瞎玩、瞎玩就是折磨自己!!!骚操作通过form表单提交数据、映射数据到对象中、还是上班后发现的能这样玩、但是我在b站看MVC的教程发现别人有教!淦、我之前看的MVC视频都没教我这样玩!!!@RequestMapping(value = "/demo6")
public String demo6(ParameterDTO parameterDTO) {

System.out.println(parameterDTO);
return "ok";

}
@Data
public class ParameterDTO {

private Long id;
private List<Long> idS;
private Map<String,String> map;
private MultipartFile multipartFile;
private List<MultipartFile> multipartFiles;
private ExtraSingle extraSingle;
private List<ExtraMore> extraMores;

@Override
public String toString() {
    return "ParameterDTO{" +
            "id=" + id +
            ", idS=" + idS +
             ", map=" + map +
            ", multipartFile=" + multipartFile.getResource().getFilename() +
            ", multipartFiles=" + multipartFiles.stream().map((Function<MultipartFile, Object>) multipartFile -> multipartFile.getResource().getFilename()).collect(Collectors.toList()) +
            ", extraSingle=" + extraSingle +
            ", extraMores=" + extraMores +
            '}';
}

}

@Data
class ExtraSingle {

private String extraSingleName;
private List<Long> extraSingleIdS;
private MultipartFile extraSingleMultipartFile;
private List<MultipartFile> extraSingleMultipartFiles;

@Override
public String toString() {
    return "ExtraSingle{" +
            "extraSingleName='" + extraSingleName +
            ", extraSingleIdS=" + extraSingleIdS +
            ", extraSingleMultipartFile=" + extraSingleMultipartFile.getResource().getFilename() +
            ", extraSingleMultipartFiles=" + extraSingleMultipartFiles.stream().map((Function<MultipartFile, Object>) multipartFile -> multipartFile.getResource().getFilename()).collect(Collectors.toList()) +
            '}';
}

}

@Data
class ExtraMore {

private String extraMoreName;
private List<Long> extraMoreIdS;
private MultipartFile extraMoreMultipartFile;
private List<MultipartFile> extraMoreMultipartFiles;

@Override
public String toString() {
    return "ExtraMore{" +
            "extraMoreName='" + extraMoreName +
            ", extraMoreIdS=" + extraMoreIdS +
            ", extraMoreMultipartFile=" + extraMoreMultipartFile.getResource().getFilename() +
            ", extraMoreMultipartFiles=" + extraMoreMultipartFiles.stream().map((Function<MultipartFile, Object>) multipartFile -> multipartFile.getResource().getFilename()).collect(Collectors.toList()) +
            '}';
}

}
<form action="/demo6" method="post" enctype="multipart/form-data">

<input type="text" name="id" value="2022">
<input type="text" name="idS" value="2020,2021,2022">
<input type="text" name="map[abc]" value="giao">
<input type="text" name="map[abcKey]" value="giaogiao">
<input type="file" name="multipartFile">
<input type="file" name="multipartFiles[0]">
<input type="file" name="multipartFiles[1]">

<br>
<input type="text" name="extraSingle.extraSingleName" value="extraSingleName名称">
<input type="text" name="extraSingle.extraSingleIdS" value="123">
<input type="file" name="extraSingle.extraSingleMultipartFile">
<input type="file" name="extraSingle.extraSingleMultipartFiles[0]">
<input type="file" name="extraSingle.extraSingleMultipartFiles[1]">


<br>
<input type="text" name="extraMores[0].extraMoreName" value="extraMoreName名称1">
<input type="text" name="extraMores[0].extraMoreIdS" value="1,2,3">
<input type="file" name="extraMores[0].extraMoreMultipartFile">
<input type="file" name="extraMores[0].extraMoreMultipartFiles[0]">
<input type="file" name="extraMores[0].extraMoreMultipartFiles[1]">
<br>
<input type="text" name="extraMores[1].extraMoreName" value="extraMoreName名称2">
<input type="text" name="extraMores[1].extraMoreIdS" value="4,5,6">
<input type="file" name="extraMores[1].extraMoreMultipartFile">
<input type="file" name="extraMores[1].extraMoreMultipartFiles[0]">
<input type="file" name="extraMores[1].extraMoreMultipartFiles[1]">
<button type="submit">提交</button>

</form>
测试结果ParameterDTO{id=2022, idS=[2020, 2021, 2022], map={abcKey=giaogiao, abc=giao},multipartFile=1_2020-08-21_946.png, multipartFiles=[1_2020-08-21_946.png, 1_2020-08-21_946.png], extraSingle=ExtraSingle{extraSingleName='extraSingleName名称', extraSingleIdS=[123], extraSingleMultipartFile=1_2020-08-21_946.png, extraSingleMultipartFiles=[1_2020-08-21_946.png, 1_2020-08-21_946.png]}, extraMores=[ExtraMore{extraMoreName='extraMoreName名称1', extraMoreIdS=[1, 2, 3], extraMoreMultipartFile=1_2020-08-21_946.png, extraMoreMultipartFiles=[1_2020-08-21_946.png, 1_2020-08-21_946.png]}, ExtraMore{extraMoreName='extraMoreName名称2', extraMoreIdS=[4, 5, 6], extraMoreMultipartFile=1_2020-08-21_946.png, extraMoreMultipartFiles=[1_2020-08-21_946.png, 1_2020-08-21_946.png]}]}
要注意的是一些版本可能不支持这样!要注意的是MultipartFile文件类型可能有些版本不会有自动设置值的操作可以换一种方式Map<String, MultipartFile> fileMap = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class).getFileMap();
拿到所有的文件对象、通过name拿你需要的文件对象就可以了。解决疑惑参数值是这么注入数据?Controller中的方法值每一个参数都去找到一个方法参数解析器也就xxxMethodArgumentResolver的一个类、如常见的在方法中写model对象或者request对象时都是通过对应的参数解析器把参数解析后设置对象供我们使用。public String index(Model model,HttpServletRequest request){

...

}
自己写一个参数解析器/**

  • 自定义方法参数解析器
  • @author https://juejin.cn/user/844892...
  • @Date 2022/10/11 16:39
    */

public class CustomArgumentResolver implements HandlerMethodArgumentResolver {

/**
 *  参数是否支持
 * @param parameter 方法参数类型信息、如对应的参数前的注解信息
 * @return 返回true 这表示使用该解析器、后续着会调用 下方resolveArgument(...)方法
 */
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return User.class.equals(parameter.getParameterType());
}

/**
 *  处理并生成参数对象返回
 * @param parameter  方法参数对象
 * @param mavContainer  可以理解为model对象
 * @param webRequest  可以理解为是 HttpServletRequest 请求对象
 * @param binderFactory 数据绑定工厂
 * @return 返回参数对象
 */
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    User user = new User();
    user.setName("自动注入....");
    return user;
}

}
添加到参数解析器中、设置为最前面方便测试。@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

@PostConstruct
public void init() {
    //获取到自定义requestMappingHandlerAdapter的属性(只读)
    List<HandlerMethodArgumentResolver> resolvers = requestMappingHandlerAdapter.getArgumentResolvers();
    //重新创建集合对象
    List<HandlerMethodArgumentResolver> newResolvers =
            new ArrayList<>(resolvers.size() + 1);
    // 添加 自定义解析器 到集合首位
    newResolvers.add(new CustomArgumentResolver());
    // 添加 已注册的 Resolver 对象集合
    newResolvers.addAll(resolvers);
    // 重新设置 Resolver 对象集合
    requestMappingHandlerAdapter.setArgumentResolvers(newResolvers);
}

}
复制代码测试@RequestMapping(value = "/demo4")
public String demo4(@RequestBody User user) {

System.out.println(user);
return "ok";

}
测试结果User(id=null, name=自动注入...., age=null, sex=null)
可以发现@RequestBody是不起作用的、直接是使用我们自定义的CustomArgumentResolver类中resolveArgument()方法返回的对象做为参数塞到Controller请求方法中的。由此可以得出先找到的参数解析器会优先使用、为了测试自定义的参数解析器设置在集合的第一个位置、为啥会这样就得看源码咯。主要的在这个类InvocableHandlerMethod的getMethodArgumentValues方法、这个方法是得到方法参数需要注入的值。


这个地方会处理每一个参数的值。

 红色框、框柱的是获取一个参数解析器、如果最前面的解析器resolver.supportsParameter(parameter)返回的是true的情况下就使用当前解析器做为参数解析使用、所以我们自定义的解析器放在第一个的位置上就会优先使用自定义的。绿色框、框柱的是解析器的处理参数的方法、最后返回方法参数。问题1:我的参数是个对象、底层是这么设置对象属性的。是通过反射执行对应的set方法设置的值、下方有个简洁版例子。public static void main(String[] args) throws Exception {

Method setId = User.class.getMethod("setId", Integer.class);
User user = new User();
setId.invoke(user, 21212);
System.out.println(user);

}
问题2:我Controller方法有两个参数、参数值都通过参数解析器解析出来了值、底层是怎么将值设置到Controller方法的?也是通过反射执行对应的方法、也是调用方法的invoke()来设置参数的传递并执行Controller中的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/95672.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ Reference: Standard C++ Library reference: Containers: map: map: operator=

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/map/operator/ 公有成员函数 <map> std::map::operator C98 copy (1) map& operator (const map& x); C11 copy (1) map& operator (const map& x); move (2) map& oper…

Linux学习04-文件权限与目录配置

1 用户和用户组的概念 Linux中的用户划分很有意义。分为用户、用户组与非本用户组。模式类似于学校的机房。 2 文件权限 使用ls -al查看文件权限&#xff1a; [rootstudy ~]# ls -al -rw-r--r--. 1 root root 1864 May 4 18:01 initial-setup-ks.cfg [ 1…

vue3表单输入绑定 v-model

vue3表单输入绑定 v-model 一、基本使用 1.1、v-model 使用 <template><input type"text" v-model"msg"/><h2>{{msg}}</h2> </template><script setup> import { ref} from vue const msgref("Hello World&quo…

Vue中路由守卫的具体应用

目录 &#x1f53d; 概述 1. 全局守卫——全局钩子函数 1.1 全局前置守卫——beforeEach 1.2 全局后置路由守卫 1.3 整合 2. 路由独享的守卫——路由独享的钩子函数 3.组件内的守卫——组件内的钩子函数 &#x1f53d; 参考资料 Vue-Router导航&#xff08;路由&#x…

卫龙上市酿成资本惨剧:CPE损失上亿美元 高瓴浮亏5000万美元

雷帝网 雷建平 12月16日本是年度最受期待的新消费企业上市&#xff0c;却酿成资本惨剧。“辣条一哥”卫龙&#xff08;股票代码为&#xff1a;“09985”&#xff09;昨日在港交所上市&#xff0c;发行价为10.56港元&#xff0c;募资净额为8.99亿港元&#xff1b;卫龙食品首日开…

大数据中的数据安全

数据安全体系全貌 在数据仓库平台中&#xff0c;对应数据的请求必须严格尊属数据安全体系 数据使用安全 数据安全 认证 授权 认证主要是对用户的身份确认&#xff0c;比如最简单的用户的登录需要账户和密码&#xff1b;像你登录Mysql需要输出用户名和密码。比如大数据中使用…

爱科赛博冲刺科创板:拟募资3.8亿 陕西集成电路与达晨是股东

雷递网 雷建平 12月16日西安爱科赛博电气股份有限公司&#xff08;简称&#xff1a;“爱科赛博”&#xff09;日前递交招股书&#xff0c;准备在科创板上市。爱科赛博计划募资3.8亿。其中&#xff0c;1.2亿用于西安爱科赛博电气股份有限公司精密特种电源产业化建设项目&#xf…

2022.12.5-12.11 AI行业周刊(第127期):一起做时间的朋友

最近沸沸扬扬的就是核酸疫情的事情&#xff0c;突然之间&#xff0c;一下子全国都放开了&#xff0c;很不适应。 大宝在上幼儿园中班&#xff0c;上上个星期&#xff0c;学校的政策&#xff0c;还是没有核酸证明不得入校。 而上周末发的通知&#xff0c;已经是不得在外做核酸…

AIGC音乐生成#riff + diffusion 以生成频谱图图像来转换为音乐 | 无界日报 第2期

小杜无界日报第2期&#xff0c;本期头条 - stable diffusion 微调模型 riff diffusion 以生成频谱图图像来转换为音乐。无界日报2022.12.16第02期- 头条 -riff diffusion#AIGC# #工具#riff diffusion 是 stable diffusion 的微调模型&#xff0c;以生成频谱图图像来转换为音…

(文章复现)8.基于共享储能电站的工业用户日前优化经济调度

目录 复现文章&#xff1a; 摘要&#xff1a; 部分程序&#xff1a; 输出结果&#xff1a; 15r 复现文章&#xff1a; 基于共享储能电站的工业用户日前优化经济调度——李淋&#xff08;电力建设2020&#xff09; 摘要&#xff1a; 文章提出一种基于共享储能电站的工业…

【计算机网络】:2-物理层

物理层 物理层考虑的是怎样才能在各种计算机的传输媒体上传输数据比特流&#xff0c;主要涉及到的领域包括信号传播、电信系统等&#xff0c;与计算机软件相关较少 1.物理层介绍 物理层为数据链路层屏蔽了各种传输媒体的差异&#xff0c;使数据链路层 只需要考虑本层 的的协…

《Linux内核源码分析》课程笔记

《Linux内核源码分析》课程笔记漏洞安全与虚拟内存CPU缓存技术malloc系统调用中断锁与IPC机制MMU内存页回收内核设备驱动程序内核启动流程这个课好烂&#xff0c;就是打广告用的。出现的老师的英语听着难受。 漏洞安全与虚拟内存 CPU架构&#xff1a;1、X86架构&#xff0c;采…

绿盟SecXOps安全智能分析技术白皮书

数据共享 定义内涵 数据共享 是指在多个用户或多个程序之间遵循一定规则共同享用数据&#xff0c;并进行各种操作、运算和分析的一种技术。数据共享包括数据发布、接口、交换等内容。 技术背景 随着数字经济成为拉动全球经济增长的新引擎&#xff0c;大数据成为经济中重要的…

微服务框架 SpringCloud微服务架构 多级缓存 48 多级缓存 48.7 Redis 缓存预热

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 多级缓存 文章目录微服务框架多级缓存48 多级缓存48.7 Redis 缓存预热48.7.1 添加Redis 缓存的需求48.7.2 冷启动和缓存预热48.7.3 缓存预热…

高通平台开发系列讲解(DSI篇)DSI函数的内部逻辑

文章目录 一、dsi_start_data_call函数二、dsi_get_pkt_stats函数三、dsi_stop_data_call函数四、回调流程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢DSI层最上层函数位于dsi_netctrl.c中,该.c位于apps_proc/data/dsi_netctrl目录中,现对部分主要函数的调用流程进…

大数据工程师的“卿卿日常”

大数据工程师在日常生活中做什么&#xff1f; 长按关注《Java学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 扫码关注《Java学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 什么是数据工程&#xff1f; 数据工程是当今世界最关键和最基础的技能…

25岁,一个汽修工人转行 Python 的真实事迹!

前言 我遇到一个汽修工人&#xff0c;他没有事喜欢上网&#xff0c;看新闻&#xff0c;对于编程很有兴趣&#xff0c;也想过转行&#xff0c;但是不知道学习什么好. 如何知道 Python. 我和这位朋友的家很近&#xff0c;在过年的时候&#xff0c;我们聊过 (20 出头), 我了解他是…

在vue中,为什么不能用index作为key?

啊&#xff1f;不是吧&#xff0c;在vue中&#xff0c;不能使用index作为key呢&#xff1f;使用index作为key又好理解又方便&#xff0c;为什么不能使用呢&#xff1f;看文章就知道啦~. 一、key的作用是什么&#xff1f; key的作用众所周知&#xff0c;看一个demo就知道&…

【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)

一、前言 在正式编写 FFmpeg 播放器前&#xff0c;我们需要先简单了解下所要用到的 FFmpeg 库、播放与解码流程、函数和相关结构体。 二、FFmpeg 库简介 库介绍avcodec音视频编解码核心库avformat音视频容器格式的封装和解析avutil核心工具库swscal图像格式转换的模块swresa…

零基础如何学好Python?Python有哪些必须学的知识?

跟几个IT界的大佬提起Python&#xff0c;他们说零基础学好Python很简单&#xff0c;Python进阶需要花费些气力。那么零基础如何学好Python&#xff1f;Python有哪些必须学的知识&#xff1f;学习的策略技巧有哪些&#xff1f;今天小编整理的这篇文章将会给你启发。 Python上手…