您现在的位置是:首页 >技术交流 >Java --- springboot2请求参数处理网站首页技术交流

Java --- springboot2请求参数处理

鸭鸭老板 2023-07-02 16:00:03
简介Java --- springboot2请求参数处理

目录

一、请求参数处理

1.1、请求映射

1.2、自定义请求规则

1.3、请求处理

1.4、普通参数与基本注解

1.4.1、注解

1.5、参数处理原则

1.6、复杂参数 

1.7、自定义参数对象

1.8、自定义Converter


一、请求参数处理

1.1、请求映射

//    @RequestMapping(value = "/user",method = RequestMethod.GET)
    @GetMapping("/user")
    public String getUser(){
        return "GET-张三";
    }

   // @RequestMapping(value = "/user",method = RequestMethod.POST)
    @PostMapping("/user")
    public String saveUser(){
        return "POST-张三";
    }

    //@RequestMapping(value = "/user",method = RequestMethod.PUT)
    @PutMapping("/user")
    public String putUser(){
        return "PUT-张三";
    }
    //@RequestMapping(value = "/user",method = RequestMethod.DELETE)
    @DeleteMapping("/user")
    public String deleteUser(){
        return "DELETE-张三";
    }
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>你好,老莫</h1>
<form action="/user" method="get">
    <input value="get提交" type="submit">
</form>
<form action="/user" method="post">
    <input value="post提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="DELETE">
    <input value="delete提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_method" type="hidden" value="PUT">
    <input value="put提交" type="submit">
</form>
</body>
</html>

需要手动开启rest风格

 原因是框架底层默认没有开启

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
   return new OrderedHiddenHttpMethodFilter();
}

 rest原理(表单提交时):

1、表单提交会带上_method=put

2、请求过来会被HiddenHttpMethodFilter拦截:①、请求是否正常,并且是post。②、获取到_method的值。③、兼容PUT、DELETE、PATCH。④、原生request(post),包装requestWrapper重写了getMethod方法,返回的是传入的值。⑤、过滤器链放行的时候用wrapper,后面的方法调用getMethod是调用requestWrapper的。

3、使用客户端工具如:postman就不会过filter 

1.2、自定义请求规则

@Configuration(proxyBeanMethods = false)
public class WebConfig {

    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_k");
        return hiddenHttpMethodFilter;
    }
}

1.3、请求处理

 springmvc功能分析都从orgspringframeworkwebservletDispatcherServlet.java -----》doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         //找到当前请求使用哪个Handler(Controller的方法)处理
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
    
handlerMapping:处理器映射

RequestMappingHandlerMapping:保存了所有@RequestMapping和handler的映射规则。

 所有的请求映射在HandlerMapping中,springboot自动配置欢迎页的WelcomePageHandlerMapping,访问/能访问到index.html。请求进来,会按个尝试所有HandlerMapping看是否有请求信息,如果有就找到这个请求对应的handler,如果没有就到下一个HandlerMapping。

1.4、普通参数与基本注解

1.4.1、注解

@RestController
public class ParameterController {
    /**
     * @PathVariable 路径变量
     * @RequestHeader 获取请求头
     * @RequestParam 获取请求参数
     * @CookieValue 获取cookie的值
     * @param id
     * @param name
     * @param pv
     * @return
     */
    @GetMapping("/map/{id}/owner/{name}")
    public Map<String,Object> map(@PathVariable("id") Integer id,
                                  @PathVariable("name") String name,
                                  @PathVariable Map<String ,String > pv,
                                  @RequestHeader("User-Agent") String userAgent,
                                  @RequestHeader Map<String ,String > header,
                                  @RequestParam("age") Integer  age,
                                  @RequestParam("inters")List<String> inters,
                                  @RequestParam Map<String,String> params){
        HashMap<String, Object> map = new HashMap<>();
//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("header",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        return map;
    }

    /**
     * @RequestBody 获取请求体[post]
     * @param content
     * @return
     */
    @PostMapping("/save")
    public Map postMap(@RequestBody String content){
        HashMap<String, Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }
    /**
     *@MatrixVariable:矩阵变量
     * @param low
     * @param brand
     * @return
     * springboot默认是禁用了矩阵变量的功能
     * 手动开启:
     * 1、对于路径的处理:UrlPathHelper进行解析
     * 2、removeSemicolonContent:支持矩阵变量
     */
    @GetMapping("/car/{path}")
    public Map  sell(@MatrixVariable("low") Integer low,
                     @MatrixVariable("brand") List<String> brand,
                     @PathVariable String path){
        HashMap<String , Object> map = new HashMap<>();
        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }
    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossId,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empId){
        HashMap<Object, Object> map = new HashMap<>();
        map.put("bossId",bossId);
        map.put("empId",empId);
        return map;
    }
}
@Controller
public class RequestController {
    @GetMapping("/goto")
    public String gotoPage(HttpServletRequest request){
        request.setAttribute("msg","转发成功");
        return "forward:/success";//转发到/success请求
    }

    /**
     * RequestAttribute :获取request域属性
     * @param msg
     * @param request
     * @return
     */
    @ResponseBody
    @GetMapping("/success")
    public Map success(@RequestAttribute("msg") String msg,
                       HttpServletRequest request){
        HashMap<String , Object> map = new HashMap<>();
        map.put("request-msg",request.getAttribute("msg"));
        map.put("requestAtt_msg",msg);
        return map;
    }
}
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //不移除分号内容,矩阵变量功能生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }

//    @Override
//    public void configurePathMatch(PathMatchConfigurer configurer) {
//        UrlPathHelper urlPathHelper = new UrlPathHelper();
//        //不移除分号内容,矩阵变量功能生效
//        urlPathHelper.setRemoveSemicolonContent(false);
//        configurer.setUrlPathHelper(urlPathHelper);
//    }
}

1.5、参数处理原则

1、HandlerMapping中找到能处理请求的Handler(Controller中的方法)。 

2、在当前Handler中找到一个HandlerAdapter

HandlerAdapter:

 RequestMappingHandlerAdapter:支持方法上标注@RequestMapping

HandlerFunctionAdapter:支持函数式编程。

执行目标方法

// Actually invoke the handler.
//DispatcherServlet ---》doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 //执行目标方法

mav = invokeHandlerMethod(request, response, handlerMethod);

 //真正执行目标方法

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

//获取方法的参数值

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

参数解析器

确定将要执行的目标方法的每一个参数的值

SpringMVC目标方法能写多少种参数类型,取决于参数解析器。

 

 当前解析器是否支持解析这种参数

支持就调用resolveArgument

返回值处理器

 确定目标方法每一个参数的值

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }

   Object[] args = new Object[parameters.length];
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      args[i] = findProvidedArgument(parameter, providedArgs);
      if (args[i] != null) {
         continue;
      }
      if (!this.resolvers.supportsParameter(parameter)) {
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
         args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
      catch (Exception ex) {
         // Leave stack trace for later, exception may actually be resolved and handled...
         if (logger.isDebugEnabled()) {
            String exMsg = ex.getMessage();
            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
            }
         }
         throw ex;
      }
   }
   return args;
}

 在所有参数解析器中判断哪个支持当前参数

@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
   HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
   if (result == null) {
      for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
         if (resolver.supportsParameter(parameter)) {
            result = resolver;
            this.argumentResolverCache.put(parameter, result);
            break;
         }
      }
   }
   return result;
}

解析这个参数的值 

调用HandlerMethodArgumentResolver的resolveArgument方法

1.6、复杂参数 

    @ResponseBody
    @GetMapping("/success")
    public Map success(HttpServletRequest request){
        HashMap<String , Object> map = new HashMap<>();
        Object mapkey = request.getAttribute("mapkey");
        Object modelkey = request.getAttribute("modelkey");
        Object requestkey = request.getAttribute("requestkey");
        map.put("mapkey",mapkey);
        map.put("modelkey",modelkey);
        map.put("requestkey",requestkey);
        return map;
    }

    /**
     * 这三个都可以在request域中放数据
     * @param map
     * @param model
     * @param request
     *
     * @param response
     * @return
     */
    @GetMapping("/param")
    public String param(Map<String,Object> map,
                        Model model,
                        HttpServletRequest request,
                        HttpServletResponse response){
        map.put("mapkey","mapvalue");
        model.addAttribute("modelkey","modelvalue");
        request.setAttribute("requestkey","requestvalue");
        Cookie cookie = new Cookie("key1","value1");
        response.addCookie(cookie);
        return "forward:/success";
    }

1.7、自定义参数对象

@Data
public class Persons {
    private String userName;
    private Date birth;
    private Integer age;
    private Pets pets;
}
@Data
public class Pets {
    private String name;
    private Integer age;
}
  /**
     * 数据绑定:页面提交的请求数据(GET,POST)都可以和对象属性进行绑定
     * @param persons
     * @return
     */
    @PostMapping("/saveuser")
    public Persons saveuser(Persons persons){
        return persons;
    }
<form action="/saveuser" method="post">
    姓名:<input name="userName" value="老莫"/></br>
    年龄:<input name="age" value="12"/></br>
    生日:<input name="birth" value="2011/10/1"/></br>
    宠物名:<input name="pets.name" value="金龙鱼"/></br>
    宠物年龄:<input name="pets.age" value="2"/></br>
    <input value="保存" type="submit">
</form>
ServletModelAttributeMethodProcessor 这个参数处理器支持

是否是简单类型

public static boolean isSimpleValueType(Class<?> type) {
   return (Void.class != type && void.class != type &&
         (ClassUtils.isPrimitiveOrWrapper(type) ||
         Enum.class.isAssignableFrom(type) ||
         CharSequence.class.isAssignableFrom(type) ||
         Number.class.isAssignableFrom(type) ||
         Date.class.isAssignableFrom(type) ||
         Temporal.class.isAssignableFrom(type) ||
         URI.class == type ||
         URL.class == type ||
         Locale.class == type ||
         Class.class == type));
}

@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
   Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

   String name = ModelFactory.getNameForParameter(parameter);
   ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
   if (ann != null) {
      mavContainer.setBinding(name, ann.binding());
   }

   Object attribute = null;
   BindingResult bindingResult = null;

   if (mavContainer.containsAttribute(name)) {
      attribute = mavContainer.getModel().get(name);
   }
   else {
      // Create attribute instance
      try {
         attribute = createAttribute(name, parameter, binderFactory, webRequest);
      }
      catch (BindException ex) {
         if (isBindExceptionRequired(parameter)) {
            // No BindingResult parameter -> fail with BindException
            throw ex;
         }
         // Otherwise, expose null/empty value and associated BindingResult
         if (parameter.getParameterType() == Optional.class) {
            attribute = Optional.empty();
         }
         bindingResult = ex.getBindingResult();
      }
   }

   if (bindingResult == null) {
      // Bean property binding and validation;
      // skipped in case of binding failure on construction.
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      if (binder.getTarget() != null) {
         if (!mavContainer.isBindingDisabled(name)) {
            bindRequestParameters(binder, webRequest);
         }
         validateIfApplicable(binder, parameter);
         if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
         }
      }
      // Value type adaptation, also covering java.util.Optional
      if (!parameter.getParameterType().isInstance(attribute)) {
         attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
      }
      bindingResult = binder.getBindingResult();
   }

   // Add resolved attribute and BindingResult at the end of the model
   Map<String, Object> bindingResultModel = bindingResult.getModel();
   mavContainer.removeAttributes(bindingResultModel);
   mavContainer.addAllAttributes(bindingResultModel);

   return attribute;
}

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);

WebDataBinder:web数据绑定器,将请求参数的值绑定到指定的JavaBean里面

WebDataBinder利用它里面的Converters将请求数据转成指定的数据类型,再次封装到JavaBean中。

GenericConversionService:在设置每一个值的时候,找到它里面所有的converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean ----》Integer).

1.8、自定义Converter

<form action="/saveuser" method="post">
    姓名:<input name="userName" value="老莫"/></br>
    年龄:<input name="age" value="12"/></br>
    生日:<input name="birth" value="2011/10/1"/></br>
<!--    宠物名:<input name="pets.name" value="金龙鱼"/></br>-->
<!--    宠物年龄:<input name="pets.age" value="2"/></br>-->
    宠物:<input name="pets" value="金龙鱼,3">
    <input value="保存" type="submit">
</form>
  @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //不移除分号内容,矩阵变量功能生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }

            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String , Pets>() {
                    @Override
                    public Pets convert(String source) {
                        if (!StringUtils.isEmpty(source)){
                            Pets pets = new Pets();
                            String[] split = source.split(",");
                            pets.setName(split[0]);
                            pets.setAge(Integer.valueOf(split[1]));
                            return pets;
                        }
                        return null;
                    }
                });
            }
        };

    }

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。