springboot HandlerIntercepter Interceptor's Operation to Modify request body Data

  • 2021-10-13 07:32:30
  • OfStack

Learning technology in practical work is the fastest and most profound. Of course, it is necessary to have a sense of continuous learning

Technology stack version:

spring boot 2.0.2

Something happened

Recently, doing business needs, front-end classmate fe will userId And userName Put request header Won.

If you want to use the back-end api interface, userId And userName Each interface is derived from the header Gets from the.

Imagine 1. If you have 10 interfaces, you have to write each interface once.


Object.setUserId(request.getHeader("userId"))

As in the following code snippet


@RestController
@Validated
@RequestMapping("/template")
public class TemplateController {
    // 1 A feign client
    @Autowired 
    TemplateClient templateClient
    @PostMapping(value = "/create", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResultVO create(@RequestBody @Valid TemplateParam param, HttpServletRequest request) {
        //  Every interface must write 1 Pass setXXX() Method 
        param.setUserId(request.getHeader("userId"));
        param.setUserName(request.getHeader("userName"));
        return templateClient.createTemplate(param).toResultVO();
    }
}   
@Data
public class TemplateParam{
    private Long templateId; 
    private Long userId;
    private String userName;
}

Solution

Two sharp weapons that everyone knows,

tomcat Adj. Filter And spring Adj. Intercepter (Specifically, userName0 )

Implementation principle

The specific method is to define 1 Filter Implementation class and 1 userName0 Gets or sets the implementation class of. Define another 1 userName3 Implementation classes, whose functions are

Filter Implementation Class: UserInfoFilter

Create an entry and define an opportunity in this entry: put our custom userName4 Substitute userName3 As the request passes on,

HttpServletRequest Implementation Class: customHttpServletRequestWrapper

Because userName3 Object's userName7 Data can only userName8 , can't userName9 That is, you cannot assign a value again.

And what we need is to give userName3 Assignment, so you need to define 1 userName3 Implementation class: request header2 This implementation class can be assigned to meet our needs.

Implementation Class for HandlerIntercepter: CustomInterceptor

Intercept requests and obtain information about interface methods (method names, parameters, return values, etc.). Thereby realizing the giving of unification 1 request header3 Dynamic assignment

As mentioned above, the specific implementation code is as follows

Code implementation

Declaration basis request header4 : request header5

request header5 : Defines the contents of the userId , userName Entity bean, if you want to inject user information into it, you need to enter the parameter object request header9 Inheritance request header5 In the interceptor, only processing userId1 Is implemented in request header5 Class request header4 .

As above controller Medium create Input parameters of method: TemplateParam , inheritance request header5


@Data
public class TemplateParam extends UserInfoParam{
    private Long templateId; 
    // private Long userId;
    // private String userName;
}
@Data
public class UserInfoParam {
//  Users id
private Long userId;
//  User name 
private String userName;
}

Define the Filter implementation class: UserInfoFilter


@Slf4j
public class UserInfoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        CustomHttpServletRequestWrapper customHttpServletRequestWrapper = null;
        try {
            HttpServletRequest req = (HttpServletRequest)request;
            customHttpServletRequestWrapper = new CustomHttpServletRequestWrapper(req);
        }catch (Exception e){
            log.warn("customHttpServletRequestWrapper Error:", e);
        }
        chain.doFilter((Objects.isNull(customHttpServletRequestWrapper) ? request : customHttpServletRequestWrapper), response);
    }
}

userName3 Implementation class: userName4


public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
    //  Save request body Data of 
    private String body;
    //  Analyse request Adj. inputStream( I.e. body) Data, turned into a string 
    public CustomHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
    public String getBody() {
        return this.body;
    }
    //  Assign a value to body Field 
    public void setBody(String body) {
        this.body = body;
    }
}

userName0 The implementation class of: CustomInterceptor


@Slf4j
public class CustomInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        pushUserInfo2Body(request, handlerMethod);
        return true;
    }
    private void pushUserInfo2Body(HttpServletRequest request, HandlerMethod handlerMethod) {
        try{
            String userId = request.getHeader("userId");
            String userName = request.getHeader("userName");
            MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
            if(ArrayUtils.isEmpty(methodParameters)) {
                return;
            }
            for (MethodParameter methodParameter : methodParameters) {
                Class clazz = methodParameter.getParameterType();
                if(ClassUtils.isAssignable(UserInfoParam.class, clazz)){
                    if(request instanceof CustomHttpServletRequestWrapper){
                        CustomHttpServletRequestWrapper requestWrapper = (CustomHttpServletRequestWrapper)request;
                        String body = requestWrapper.getBody();
                        JSONObject param = JSONObject.parseObject(body);
                        param.put("userId", userId);
                        param.put("userName", Objects.isNull(userName) ? null : URLDecoder.decode(userName, "UTF-8"));
                        requestWrapper.setBody(JSON.toJSONString(param));
                    }
                }
            }
        }catch (Exception e){
            log.warn("fill userInfo to request body Error ", e);
        }
    }

Definition Configuration class Increase the configuration of interceptors and filters

WebMvcConfigurer Subclass: CustomWebMvcConfigurer


@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        CustomInterceptor customInterceptor= new CustomInterceptor();
        registry.addInterceptor(customInterceptor);
    }
    @Bean
    public FilterRegistrationBean servletRegistrationBean() {
        UserInfoFilter userInfoFilter = new UserInfoFilter();
        FilterRegistrationBean<UserInfoFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(userInfoFilter);
        bean.setName("userInfoFilter");
        bean.addUrlPatterns("/*");
        bean.setOrder(Ordered.LOWEST_PRECEDENCE);
        return bean;
    }
}

At this point, the code for realizing the function is finished. Start spring boot App , you can curl Visit restful Interface

http Access


curl -X POST \
  http://localhost:8080/template/create
  -H 'Content-Type: application/json'
  -H 'username: tiankong Sky '
  -H 'userId: 11'
  -d '{
  "templateId": 1000}

Effect

You can see TemplateController.create(…) The typed message, userId And header1 The value of is exactly header Value passed in

toDo


Related articles: