本文代碼已整理上傳
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提交, 均可以正常綁定參數