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

3次阅读

共计 10355 个字符,预计需要花费 26 分钟才能阅读完成。

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

正文完
 0