spring mvc 拓展 -- controller 方法不加注解自动接收json参数或者from表单参数

本文代码已整理上传 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的组合

form 表单的 HandlerMethodArgumentResolver

先在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

clipboard.png

断点走到这里, 这个ServletModelAttributeMethodProcessor就是咱们要找的表单处理器,里面的属性annotationNotRequired的值是trueapp

处理 json 的 HandlerMethodArgumentResolver

将上面的add方法的参数中加上 @RequestBody注解ide

@PostMapping
    public void add(@RequestBody Goods goods) {
        GOODS_LIST.add(goods);
    }

再用接口测试工具, 以json数据提交

clipboard.png

再次断点到HandlerMethodArgumentResolverComposite#getArgumentResolver()看最终的结果

clipboard.png

能够看到: 处理json参数的是 RequestResponseBodyMethodProcessor, 里面的属性里有不少HttpMessageConverter

接下来, 就是将这两个 组合起来

自定义 HandlerMethodArgumentResolver

建立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);
    }
}

配置自定义 HandlerMethodArgumentResolver

类是写好了, 可是总归是要配置才能生效的,配置写在 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提交, 均可以正常绑定参数

相关文章
相关标签/搜索