本文代码已整理上传
githubhtml
上篇文章中已经实现了同一个url不一样视图的返回, 能够返回 json 数据, 也能够返回 html, 可是对于 spring mvc 接收 request 参数来讲, 若是接收的是 json 数据, 须要在参数前加@RequestBody注解 接收 form 表单提交的数据则不需加注解
如何作到自动接收json参数或者from表单参数?
对于http request来讲, 咱们能够根据请求的 content-type
来判断请求传递的参数是什么格式的
content-type 是 application/json
的 request 的 body 是 json 数据
content-type 是 application/x-www-form-urlencoded
的 request 是表单提交java
对于spring mvc, 对接收参数对处理是 HandlerMethodArgumentResolver
实现处理的,
因此思路就是: 找处处理json的HandlerMethodArgumentResolver和处理表单的HandlerMethodArgumentResolver,
而后自定义 本身处理表单和json的HandlerMethodArgumentResolver, 根据 request 的content-type 在选择对应的参数Resolvergit
自定义的 Resolver至关于 json 的Resolver 和 form表单的Resolver的组合
先在GoodsController中写一个方法, 参数是经过 表单提交接收, 而后断点调试github
@Controller @RequestMapping("/goods") public class GoodsController { private static final List<Goods> GOODS_LIST = new ArrayList<>(); static { GOODS_LIST.add(new Goods("998765", "哇哈哈矿泉水", 2.0)); GOODS_LIST.add(new Goods("568925", "蒙牛真果粒", 4.7)); } @RequestMapping("/list") public String list(GoodsCondition condition, Model model) { model.addAttribute("data", GOODS_LIST); return "goods"; } // goods 参数 以表单形式接收 @PostMapping public void add(Goods goods) { GOODS_LIST.add(goods); } }
而后使用接口测试工具(可使用postman
), 测试上面的接口, 表单方式web
经过DispatchServlet的doDispatch方发找处处理参数的地方,
处理参数的地方在org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues() 方法spring
而后委托给HandlerMethodArgumentResolverComposite去处理参数, 这是个全部ArgumentResolver的集合, 自己不处理, 负责找到合适的ArgumentResolverjson
HandlerMethodArgumentResolverComposite#getArgumentResolver() 这个方法就是根据须要绑定的参数找到ArgumentResolver, 因此在这里找到 form 表单的处理Resolver就能够了mvc
断点走到这里, 这个ServletModelAttributeMethodProcessor就是咱们要找的表单处理器,里面的属性annotationNotRequired的值是trueapp
将上面的add方法的参数中加上 @RequestBody注解ide
@PostMapping public void add(@RequestBody Goods goods) { GOODS_LIST.add(goods); }
再用接口测试工具, 以json数据提交
再次断点到HandlerMethodArgumentResolverComposite#getArgumentResolver()看最终的结果
能够看到: 处理json参数的是 RequestResponseBodyMethodProcessor, 里面的属性里有不少HttpMessageConverter
接下来, 就是将这两个 组合起来
建立JsonAndFormArgumentResolver为自定义的resolver
按照上面的分析, 须要在JsonAndFormArgumentResolver内获取 当前的request来判断再使用具体的resolver, 如何获取当前request呢?须要在过滤器中加入 RequestContextFilter, 这是spring提供的, 将当前 request 保存起来
// 配置filters @Override protected Filter[] getServletFilters() { // 字符 CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); encodingFilter.setEncoding(String.valueOf(StandardCharsets.UTF_8)); encodingFilter.setForceEncoding(true); // 请求拦截器 RequestContextFilter requestContextFilter = new RequestContextFilter(); return new Filter[]{encodingFilter, requestContextFilter}; }
使用 spring boot的同窗能够过滤此步骤, 都已经自动配置了
而后, 贴出自定义 resolver的代码
/** * User : liulu * Date : 2018/4/4 09:50 * version $Id: JsonAndFormArgumentResolver.java, v 0.1 Exp $ */ @NoArgsConstructor public class JsonAndFormArgumentResolver implements HandlerMethodArgumentResolver { @Setter private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor; @Setter private ModelAttributeMethodProcessor modelAttributeMethodProcessor; public JsonAndFormArgumentResolver(ModelAttributeMethodProcessor methodProcessor, RequestResponseBodyMethodProcessor bodyMethodProcessor){ this.modelAttributeMethodProcessor = methodProcessor; this.requestResponseBodyMethodProcessor = bodyMethodProcessor; } @Override public boolean supportsParameter(MethodParameter parameter) { return modelAttributeMethodProcessor.supportsParameter(parameter) || requestResponseBodyMethodProcessor.supportsParameter(parameter); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); if (HttpMethod.GET.matches(request.getMethod().toUpperCase())) { return modelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } if (request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) { return requestResponseBodyMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } return modelAttributeMethodProcessor.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } }
类是写好了, 可是总归是要配置才能生效的,配置写在 SpringMvcConfig
中
配置以下:
@Bean public WebMvcConfigurerAdapter webMvcConfigurerAdapter() { return new WebMvcConfigurerAdapter() { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new JsonAndFormArgumentResolver()); } }; } @Bean public BeanPostProcessor jsonAndFormArgumentResolverProcessor() { return new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof RequestMappingHandlerAdapter) { JsonAndFormArgumentResolver jsonAndFormArgumentResolver = null; ModelAttributeMethodProcessor modelAttributeMethodProcessor = null; RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null; for (HandlerMethodArgumentResolver argumentResolver : ((RequestMappingHandlerAdapter) bean).getArgumentResolvers()) { if (argumentResolver instanceof JsonAndFormArgumentResolver) { jsonAndFormArgumentResolver = (JsonAndFormArgumentResolver) argumentResolver; continue; } if (argumentResolver instanceof RequestResponseBodyMethodProcessor) { requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) argumentResolver; continue; } if (argumentResolver instanceof ModelAttributeMethodProcessor) { modelAttributeMethodProcessor = (ModelAttributeMethodProcessor) argumentResolver; } } if (jsonAndFormArgumentResolver != null) { jsonAndFormArgumentResolver.setModelAttributeMethodProcessor(modelAttributeMethodProcessor); jsonAndFormArgumentResolver.setRequestResponseBodyMethodProcessor(requestResponseBodyMethodProcessor); } } return bean; } }; }
第一个bean是让配置的自定义参数处理器生效,
第二个bean是初始化自定义参数处理器中的参数,
到此,自动接收json参数或者from表单参数已经完成, 将参数中的 @RequestBody 注解去掉再运行刚才的表单提交和json提交, 均可以正常绑定参数