SpringMVC passes json strings, and the background interface receives them through variables

Introduction

In our daily development process, the parameters passed from the front desk to the background are generally json or form forms, and it is best to unify one transmission method. It is not recommended to mix json + form , but because form forms are more troublesome to splicing under the array structure, Generally, json is used to transmit, and SpringBoot (SpringMVC) is used in the backend to receive data. The @RequestBody annotation is generally used to receive data, but the following situations are not easy to handle

@RequestMapping("/login")
public String login(String name,String password){
	....
}
复制代码

The above methods cannot be mapped to receive json objects, because too few fields are encapsulated into objects + @RequestBody and it is too cumbersome. In this case, we can use custom annotations + method parameter parser to solve this problem

1. Import related dependencies

The hutool toolkit is used in the framework, and the json serialization tool is used according to the situation of your own project

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.6</version>
        </dependency>
复制代码

2. Definition Notes

Annotate the parameters that need to be mapped by custom annotations

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JSONParameter {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default false;

    String defaultValue() default "";
}


复制代码

3. Write a custom parameter parser

package com.gosun.swd.commom.resolver;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.gosun.swd.commom.annotation.JSONParameter;
import com.gosun.swd.commom.exception.Assert;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Set;

@Component
public class JSONParameterResolver implements HandlerMethodArgumentResolver {

//    ThreadLocal<JSONObject> paramObject = new ThreadLocal<>();


    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(JSONParameter.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        JSONObject jsonObject = getJsonObject();
        Object o = jsonObject.get(methodParameter.getParameterName());
        JSONParameter parameterAnnotation = methodParameter.getParameterAnnotation(JSONParameter.class);
        //判断是否允许为空
        String message = String.format("参数%s不能为空", methodParameter.getParameterName());
        Assert.assertFalse(parameterAnnotation.required() && o == null, message);
        //如果为空设置默认值
        if (o == null) {
            return convertParameter(methodParameter, parameterAnnotation.defaultValue());
        }
        return convertParameter(methodParameter, o);
    }

    private Object convertParameter(MethodParameter methodParameter, Object o) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
        if (methodParameter.getParameterType().getTypeName().equals(List.class.getTypeName()) ||
                methodParameter.getParameterType().getTypeName().equals(Set.class.getTypeName())) {
            Type actualTypeArgument = ((ParameterizedTypeImpl) methodParameter.getGenericParameterType()).getActualTypeArguments()[0];
            return JSONUtil.toList((JSONArray) o, Class.forName(actualTypeArgument.getTypeName()));
        } else {
            return convertParameter(methodParameter, String.valueOf(o));
        }
    }

    private Object convertParameter(MethodParameter methodParameter, String obj) throws InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor<?>[] constructors = methodParameter.getParameterType().getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterCount() == 1 && constructor.getParameters()[0].getParameterizedType().getTypeName().equals(String.class.getTypeName())) {
                return constructor.newInstance(obj);
            }
        }
        return null;
    }

    private JSONObject getJsonObject() throws IOException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
        StringBuilder responseStrBuilder = new StringBuilder();
        String inputStr;
        while ((inputStr = streamReader.readLine()) != null) {
            responseStrBuilder.append(inputStr);
        }
        String jsonStr = responseStrBuilder.toString();
//        if (StringUtils.isEmpty(jsonStr)) {//如果没有参数,表示不是第一次进入
//            return paramObject.get() == null ? new JSONObject() : paramObject.get();
//        }
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
//        paramObject.set(jsonObject);
        return jsonObject;
    }
}

复制代码

4. Solve the problem of loss of request information for secondary acquisition

1. Write a filter

package com.gosun.swd.commom.resolver;


import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author:
 */
@Component
@WebFilter(filterName = "channelFilter", urlPatterns = {"/*"})
public class ChannelFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            ServletRequest requestWrapper = null;
            if (request instanceof HttpServletRequest) {
                requestWrapper = new RequestWrapper((HttpServletRequest) request);
            }
            if (requestWrapper == null) {
                chain.doFilter(request, response);
            } else {
                chain.doFilter(requestWrapper, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void destroy() {
    }

}
 
复制代码

2. Write the constructor

package com.gosun.swd.commom.resolver;

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

/**
 * @author:
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    //参数字节数组
    private byte[] requestBody;
    //Http请求对象
    private HttpServletRequest request;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.request = request;
    }

    /**
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        /**
         * 每次调用此方法时将数据流中的数据读取出来,然后再回填到InputStream之中
         * 解决通过@RequestBody@RequestParam(POST方式)读取一次后控制器拿不到参数问题
         */
        if (null == this.requestBody) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtils.copy(request.getInputStream(), baos);
            this.requestBody = baos.toByteArray();
        }

        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() {
                return bais.read();
            }
        };
    }

    public byte[] getRequestBody() {
        return requestBody;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}
复制代码

5. Call the example code


	@RequestMapping("/login")
	public String login(@JSONParameter String name, @JSONParameter String password){
	....
	}

	//包含默认值的参数映射
    @PostMapping(value = "/getEvents", consumes = "application/json")
    public ResultVo<Events> getEvents(@JSONParameter(name = "page", defaultValue = "1") Long page, 
                                      @RequestParam(name = "per_page", defaultValue = "10") Long per_page, 
                                      @JSONParameter String type) {
	....
    }
    
    //字段集合
    @PostMapping(value = "/deleteCarInfo", consumes = "application/json")
    public ResultVo deleteCarInfo(@JSONParameter List<Long> ids) {
	....
    }
复制代码

Guess you like

Origin juejin.im/post/7077811223395303460