关于后端:换种方式看后端参数接收建议躺着看

继续创作,减速成长!这是我参加「掘金日新打算 · 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中的办法。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理