开发者

解决HttpServletRequest无法获取@RequestBody修饰的参数问题及分析

目录
  • HttpServletRequest无法获取@RequestBody修饰的参数
    • 一、先编写一个http工具类用来读取ServletRequest的内容
    • 二、添加请求体复制包装器
    • 三、添加请求拦截器配置类
    • 四、业务代码中使用方式
  • 总结

    HttpServletRequest无法获取@RequestBody修饰的参数

    在使用springboot设计controller时我们通常会在某个请求如post中使用@RequestBody来修饰参数如:

    解决HttpServletRequest无法获取@RequestBody修饰的参数问题及分析

    在一些特殊场景下,我们需要在service层的代码去拿到当前上下文请求(HttpServletRequest)中的一些信息如请求体,这个时候被@RequestBody所修饰的请求是无法获取的。

    原因如下:

    1、请求体流只能读取一次:Servlet 规范中,HttpServletRequest 的输入流 (getInputStream()) 是单向的,一旦被 @RequestBody 读取后,流就会关闭,无法再次读取。

    2、@RequestBody 优先处理:Spring MVC 在处理控制器方法时,会先解析 @RequestBody,导致后续通过 HttpServletpythonRequest 获取请求体时为空。

    下面简单演示下解决方案:

    一、先编写一个http工具类用来读取ServletRequest的内容

    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    
    import Javax.servlet.ServletInputStream;
    import javax.servlet.ServletRequest;
    import java.io.BufphpferedReader;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    
    /**
     * http工具类
     */
    @Slf4j
    public class HttpUtils {
        /**
         * 从request获取body的数据
         *
         * @param request 请求
         * @return body数据字符串
         */
        public static String getBodyStr(ServletRequest request) {
            StringBuilder sb = new StringBuilder();
            try {
                try (ServletInputStream inputStream = request.getInputStream()) {
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
                        String line;
                        while ((line = reader.readLine()) != null) {
                            sb.append(line);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("get request body error: ", e);
            }
            return sb.toString();
        }
    
        /**
         * 从request获取body的数据,并转换成对象(适用于使用@RequestBody修饰的参数)
         *
         * @param request request 请求
         * @param clazz   对象类型
         * @return 对象
         */
        public static <T> T getBodyToObject(ServletRequest request, Class<T> clazz) {
            String bodyStr = getBodyStr(request);
            if (StringUtils.isBlank(bodyStr)) {
                return null;
            }
            return JSON.parseobject(bodyStr, clazz);
        }
    
        /**
         * 从request获取body的数据,并转换成集合对象(适用于使用@RequestBody修饰的参数)
         *
         * @param request request 请求
         * @param clazz   集合对象类型
         * @return 集合对象
         */
        public static <T> List<T> getBodyToList(ServletRequest request, Class<T> clazz) {
            String bodyStr = getBodyStr(request);
            if (StringUtils.isBlank(bodyStr)) {
                return null;
            }
            return JSON.parseArray(bodyStr, clazz);
        }
    }

    二、添加请求体复制包装器

    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InpupythontStreamReader;
    import java.nio.charset.StandardCharsets;
    
    /**
     * 请求体复制包装器
     */
    public class BodyCopyWrapper extends HttpServletRequestWrapper {
        private byte[] requestBody;
    
        public BodyCopyWrapper(HttpServletRequest request) throws IOException {
            super(request);
            requestBody = HttpUtils.getBodyStr(request).getBytes(StandardCharsets.UTF_8);
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream basis = new ByteArrayInputStream(requestBody);
            return new ServletInputStream() {
                @Override
                public int read() throws IOException {
                    return basis.read();
                }
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener readListener) {
                }
            };
        }
    
        public void setInputStream(byte[] body) {
            this.requestBody = body;
        }
    }

    三、添加请求拦截器配置类

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * 请求拦截器配置类
     */
    @Slf4j
    @Configuration
    public class FilterConfig {
        @Bean
        public FilterRegistrationBean<RequestWrapperFilter> requestWrapperFilter() {
            FilterRegistrationBean<RequestWrapperFilter> bean = new FilterRegistrationBean<>();
            bean.setFilter(new RequestWrapperFilter());
            bean.addUrlPatterns("/*");
            bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
            return bean;
        }
    
        public static class RequestWrapperFilter implements Filter {
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chai编程客栈n) throws ServletException
                    , IOException {
                ServletRequest requestWrapper = null;
                if (request instanceof HttpServletRequest) {
                    requestWrapper = new BodyCopyWrapper((HttpServletRequest) request);
                }
                if (null == r编程客栈equestWrapper) {
                    log.warn("未进行request包装返回原来的request");
                    chain.doFilter(request, response);
                } else {
                    chain.doFilter(requestWrapper, response);
                }
            }
        }
    }

    四、业务代码中使用方式

    HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
    //转对象
    UserLoginDTO userLoginDTO = HttpUtils.getBodyToObject(request, UserLoginDTO.class);
    //直接获取内容字符串
    String bodyStr = HttpUtils.getBodyStr(request);

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜