Перехватчик Springboot HandlerIntercepter реализует модификацию данных тела запроса

Spring Boot

Обучение технологиям на рабочем месте является самым быстрым и глубоким. Конечно, самосознание непрерывного обучения является обязательным.

Версия технического стека:

spring boot 2.0.2

что-то произошло

В последнее время для хозяйственных нужд фронтенд одноклассник Fe будетuserIdиuserNameставитьrequest headerбинго. Внутренний API-интерфейс хочет использоватьuserIdиuserName, каждый интерфейс должен начинаться сheaderполучено в. Представьте, если у вас есть десять интерфейсов, вам нужно написать каждый интерфейсObject.setUserId(request.getHeader("userId")). как следующий фрагмент кода

@RestController
@Validated
@RequestMapping("/template")
public class TemplateController {

    // 一个feign client
    @Autowired 
    TemplateClient templateClient

    @PostMapping(value = "/create", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResultVO create(@RequestBody @Valid TemplateParam param, HttpServletRequest request) {
        // 每个接口都要写一遍setXXX()方法
        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;
}

Решение

Мы все знаем два великих оружия,

Фильтр tomcat и Intercepter spring (в частности, HandlerIntercepter)

Принцип реализации

Конкретный метод заключается в определении класса реализации Filter и класса реализации HandlerIntercepter. Определите другой класс реализации HttpServletRequest, функции которого

Класс реализации фильтра: UserInfoFilter

Создайте запись и определите возможность в этой записи: передайте наш пользовательский CustomHttpServletRequestWrapper вместо HttpServletRequest с запросом

Класс реализации HttpServletRequest: customHttpServletRequestWrapper

Поскольку данные тела объекта HttpServletRequest могут быть только получены, не могут быть установлены, то есть не могут быть повторно назначены. Наше требование заключается в необходимости назначения HttpServletRequest, вам необходимо определить класс, который реализует HttpServletRequest: customHttpServletRequestWrapper, класс реализации может быть назначен для удовлетворения наших потребностей.

Класс реализации HandlerIntercepter: Custominterceptor

Перехватывать запросы и получать информацию, связанную с методом интерфейса (имя метода, параметры, возвращаемое значение и т. д.). Для достижения единого динамического назначения тела запроса

Идея реализации описана выше, а конкретный код реализации выглядит следующим образом.

Код

Объявить базовый компонент: UserInfoParam

UserInfoParam: определяет объектный компонент, содержащий userId и userName. Чтобы внедрить в него информацию о пользователе, входной объект XXXParam должен наследовать UserInfoParam, а перехватчик обрабатывает только компоненты, которые реализуют класс UserInfoParam в @Requestbody. В качестве входного параметра метода создания в контроллере выше: TemplateParam, наследует UserInfoParam

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

@Data public class UserInfoParam {

// 用户id
private Long userId;

// 用户名称
private String userName;

}

Определите класс реализации фильтра: 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);
    }
}

Класс реализации HTTPSERVLETREQUEST: CustomhttpServletRequestWrapper

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {

    // 保存request body的数据
    private String body;

    // 解析request的inputStream(即body)数据,转成字符串
    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;
    }

    // 赋值给body字段
    public void setBody(String body) {
        this.body = body;
    }

}

Класс реализации HandlerIntercepter: 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);
        }
    }

Определите класс Configuration, добавьте конфигурацию перехватчиков и фильтров

Подкласс WebMvcConfigurer: 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;
    }
}

На этом код для реализации функции закончен. Запустите приложение весенней загрузки, вы можете свернуть, чтобы получить доступ к спокойному интерфейсу.

http-доступ

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

Эффект

Вы можете увидеть информацию, напечатанную TemplateController.create(...), значения userId и username точно соответствуют значениям, переданным в заголовке

toDo

Остальное зависит от тебя

Оригинальный адрес:Перехватчик Springboot HandlerIntercepter реализует модификацию данных тела запроса