SpringBoot自定义参数解析器同时支持json,form表单,拼接在uri的多种传参方式

前言

一个http请求中有多种入参方式,比如json串,form表单,还有参数拼接在uri等。在SpringBoot里,一个接口接收什么类型参数都有对应实现方式,比如用@RequestBody注解修饰的形参接收json入参,但不能接收其他类型的入参,否则会报Unsupported Media Type错误。如果项目里要求实现一个接口能接收多种入参该如何实现?

需求实现方式说明

SpringBoot里每种接收入参的实现方式其原理都是框架封装了对应的参数解析器,关于参数解析原理可以参考我的这篇文章
比如:

  • RequestResponseBodyMethodProcessor解析器是解析形参带有 @RequestBody注解 并且请求的 Content-Type为application/json 的请求,可以详见RequestResponseBodyMethodProcessor源码
  • ServletModelAttributeMethodProcessor解析器 是解析请求的 Content-Type为application/x-www-form-urlencoded的请求,可以详见 ServletModelAttributeMethodProcessor源码

我们通过自定义参数解析器去实现,具体实现步骤如下:

  • 自定义一个注解,带有该注解的形参能接受json,form表单,拼接uri参数方式入参
  • 自定义参数解析器,根据请求的Content-Type判断传参方式,分别使用对应封装好的解析器解析(也可以自己实现解析参数逻辑,但是解析json串或form参数逻辑比较复杂,所以这里就不举例了,直接用现成的)
  • 将自定义解析器注册到IOC容器里

代码实现逻辑

  • 自定义注解
import java.lang.annotation.*;

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestData {
    
    

    String value() default "";

    /**
     * 参数是否是必填的,默认false,可以设置为true
     */
    boolean required() default false;
    
}
  • 自定义参数解析器
import com.alibaba.fastjson.JSONObject;
import com.changgou.item.annotation.RequestData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

public class RequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
    
    /**
     * 解析Content-Type为application/json的默认解析器是RequestResponseBodyMethodProcessor
     */
    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;

    /**
     * 解析Content-Type为application/x-www-form-urlencoded的默认解析器是ServletModelAttributeMethodProcessor
     */
    private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;

    public RequestHandlerMethodArgumentResolver(RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor,
                                                ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor) {
    
    
        this.requestResponseBodyMethodProcessor = requestResponseBodyMethodProcessor;
        this.servletModelAttributeMethodProcessor = servletModelAttributeMethodProcessor;
    }

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
    
    
        return methodParameter.hasMethodAnnotation(RequestData.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
    
    
        final String applicationXwwwFormUrlencoded = MediaType.APPLICATION_FORM_URLENCODED_VALUE;
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);

        if (request == null) {
    
    
            throw new RuntimeException(" request must not be null!");
        }
        String contentType = request.getContentType();
        // 获取uri上的参数
        Map<String, String[]> params = request.getParameterMap();
        Map<String, Object> paramMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(params)) {
    
    
            for (Map.Entry<String, String[]> entry : params.entrySet()) {
    
    
                String key = entry.getKey();
                String[] value = entry.getValue();
                if (value != null && value.length == 1) {
    
    
                    paramMap.put(key, value[0]);
                }
            }
        }
        // 如果入参是拼接在uri上,则直接把拼在uri的参数封装成一个对象
        if (StringUtils.isBlank(contentType) && !CollectionUtils.isEmpty(paramMap)) {
    
    
            Class classType = methodParameter.getParameterType();
            Object model = Map.class.isAssignableFrom(classType) ?
                    paramMap : JSONObject.parseObject(JSONObject.toJSONString(paramMap), classType);
            return model;
        }
        /*
         * 如果ContentType是application/x-www-form-urlencoded,那么使用ServletModelAttributeMethodProcessor解析器
         *
         * 注:其实默认的,当系统识别到参数前有@RequestBody注解时,就会走RequestResponseBodyMethodProcessor解析器;这里就
         *    相当于在走默认的解析器前走了个判断而已。
         */
        if (applicationXwwwFormUrlencoded.equals(contentType)) {
    
    
            return servletModelAttributeMethodProcessor.resolveArgument(methodParameter,
                    modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
        }
        return requestResponseBodyMethodProcessor.resolveArgument(methodParameter,
                modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
    }
}
  • 参数解析器注册到IOC容器
import com.changgou.item.argumentresolver.RequestHandlerMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
 
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class ArgumentResolversConfig {
    
    

    private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    public ArgumentResolversConfig(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
    
    
        this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
    }

    @PostConstruct
    private void addArgumentResolvers() {
    
    
        // 获取到的是不可变的集合
        List<HandlerMethodArgumentResolver> argumentResolvers =
                requestMappingHandlerAdapter.getArgumentResolvers();
        RequestHandlerMethodArgumentResolver requestHandlerMethodArgumentResolver =
                getRequestHandlerMethodArgumentResolver(argumentResolvers);
        // 获取到的是不可变的集合,所以我们需要新建一个集合来放置参数解析器
        List<HandlerMethodArgumentResolver> myArgumentResolvers =
                new ArrayList<>(argumentResolvers.size() + 1);
        // 将自定义的解析器,放置在第一个; 并保留原来的解析器
        myArgumentResolvers.add(requestHandlerMethodArgumentResolver);
        myArgumentResolvers.addAll(argumentResolvers);
        requestMappingHandlerAdapter.setArgumentResolvers(myArgumentResolvers);
    }

    /**
     * 获取MyHandlerMethodArgumentResolver实例
     */
    private RequestHandlerMethodArgumentResolver getRequestHandlerMethodArgumentResolver(
            List<HandlerMethodArgumentResolver> argumentResolversList) {
    
    
        // 解析Content-Type为application/json的默认解析器
        RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
        // 解析Content-Type为application/x-www-form-urlencoded的默认解析器
        ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor = null;

        for (HandlerMethodArgumentResolver argumentResolver : argumentResolversList) {
    
    
            if (requestResponseBodyMethodProcessor != null && servletModelAttributeMethodProcessor != null) {
    
    
                break;
            }
            if (argumentResolver instanceof RequestResponseBodyMethodProcessor) {
    
    
                requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor) argumentResolver;
                continue;
            }
            if (argumentResolver instanceof ServletModelAttributeMethodProcessor) {
    
    
                servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor) argumentResolver;
            }
        }
        if (requestResponseBodyMethodProcessor == null || servletModelAttributeMethodProcessor == null) {
    
    
            throw new RuntimeException("requestResponseBodyMethodProcessor and "
                    + " servletModelAttributeMethodProcessor must not be null!");
        }
        return new RequestHandlerMethodArgumentResolver(requestResponseBodyMethodProcessor,
                servletModelAttributeMethodProcessor);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44947701/article/details/126257391