Mybatis拦截器中获取@RequestBody表单的值修改查询SQL

背景:

我们需要获取接口Controller中前端传入的Json对象参数值然后修改本次调用接口的查询sql语句。后台接收参数如果是表单数据的话,通过request.getParameterMap就可以全部获取到了,如果是json对象数据时,我们在过滤器或拦截器里通过request.getInputStream() 读取了request的输入流之后,请求走到controller层时就会报错,问题在于request的输入流只能读取一次不能重复读取。

1.示例:定义Controller查询UserList

c 复制代码
@PostMapping("/user/list")
public PageDataInfo<UserInfo> getUserList(@RequestBody ChkReq req) {
	PageUtils.startPage(req);
	return PageUtils.buildPageDataInfo(userInfoService.getUserList(req));
}

2.定义一个容器,将输入流存储到这个容器里面

c 复制代码
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    /**
     * 存储body数据的容器
     */
    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        // 将body数据存储起来
        String bodyStr = getBodyString(request);
        body = bodyStr.getBytes(Charset.defaultCharset());
    }

    public String getBodyString(final ServletRequest request) {
        try {
            return cloneInputStreamString(request.getInputStream());
        } catch (IOException e) {
            log.error("", e);
            throw new RuntimeException(e);
        }
    }

    public String getBodyString() {
        final InputStream inputStream = new ByteArrayInputStream(body);
        return cloneInputStreamString(inputStream);
    }

    private String cloneInputStreamString(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("", e);
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("", e);
                }
            }
        }
        return sb.toString();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }

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

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

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

}

3.我们要在过滤器中将原生的HttpServletRequest换成RequestWrapper对象

c 复制代码
public class ReplaceStreamFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

4.注册过滤器

c 复制代码
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean someFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(replaceStreamFilter());
        registration.addUrlPatterns("/*");
        registration.setName("streamFilter");
        return registration;
    }
    
    @Bean(name = "replaceStreamFilter")
    public Filter replaceStreamFilter() {
        return new ReplaceStreamFilter();
    }
}

5.然后我们可以在拦截器中获取json数据

c 复制代码
public class MyRequestInterceptor implements HandlerInterceptor {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ("POST".equalsIgnoreCase(request.getMethod()) && request.getContentType() != null && request.getContentType().contains("application/json")) {
            /*try {
                byte[] requestBodyBytes = readRequestBody(request);
                String requestBody = new String(requestBodyBytes, StandardCharsets.UTF_8);*/
            try (BufferedReader reader = request.getReader()) {
                StringBuilder requestBody = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    requestBody.append(line);
                }

                // 将请求体转换为 ChkReq 对象
                ChkReq chkReq = objectMapper.readValue(requestBody.toString(), ChkReq.class);
                // 将 ChkReq 对象存储在 ServletRequestAttributes 中
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                if (attributes != null) {
                    attributes.getRequest().setAttribute("ChkReq", chkReq);
                }
                // 继续处理请求
                return true;
            } catch (IOException e) {
                // 处理异常,例如返回错误响应
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.getWriter().write("Invalid JSON data");
                return false;
            }
        }
        // 如果不是 JSON 请求或者不是 POST 方法,则继续处理请求
        return true;
    }
}

6.注册拦截器

c 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyRequestInterceptor()).addPathPatterns("/**"); // 指定需要拦截的路径
    }
}

7.在Mybatis拦截器中获取request的值修改sql

c 复制代码
@Component
public class MyInterceptor implements InnerInterceptor {

    @SneakyThrows
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        //InnerInterceptor.super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        String sql = boundSql.getSql();
        System.out.println("sql更新之前:" + sql);
        //String condition = " name = '李四' " ;
        String condition = " 1 = 1 ";
        String name = null;
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            ChkReq chkReq = (ChkReq) attributes.getRequest().getAttribute("ChkReq");
            if (chkReq != null) {
                name = "name = '" + chkReq.getName() + "'";
            }
        }
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        Select select = (Select) CCJSqlParserUtil.parse(sql);
        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
        final Expression expression = plainSelect.getWhere();
        final Expression envCondition = CCJSqlParserUtil.parseCondExpression(condition);
        final Expression envCondition2 = CCJSqlParserUtil.parseCondExpression(name);
        if (expression == null) {
            plainSelect.setWhere(envCondition);
            plainSelect.setWhere(envCondition2);
        } else {
            AndExpression andExpression = new AndExpression(expression, envCondition);
            AndExpression andExpression2 = new AndExpression(andExpression, envCondition2);
            plainSelect.setWhere(andExpression2);
        }
        mpBs.sql(plainSelect.toString());
        System.out.println("sql更新之后:" + plainSelect.toString());
    }

}
相关推荐
zhuiQiuMX12 分钟前
分享今天做的力扣SQL题
sql·算法·leetcode
LUCIAZZZ20 分钟前
HikariCP数据库连接池原理解析
java·jvm·数据库·spring·springboot·线程池·连接池
我在北京coding36 分钟前
300道GaussDB(WMS)题目及答案。
数据库·gaussdb
小Tomkk1 小时前
阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库
数据库·mysql·阿里云
明月醉窗台2 小时前
qt使用笔记二:main.cpp详解
数据库·笔记·qt
沉到海底去吧Go2 小时前
【图片自动识别改名】识别图片中的文字并批量改名的工具,根据文字对图片批量改名,基于QT和腾讯OCR识别的实现方案
数据库·qt·ocr·图片识别自动改名·图片区域识别改名·pdf识别改名
老纪的技术唠嗑局3 小时前
重剑无锋,大巧不工 —— OceanBase 中的 Nest Loop Join 使用技巧分享
数据库·sql
BillKu3 小时前
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
java·tomcat·mybatis
未来之窗软件服务3 小时前
JAVASCRIPT 前端数据库-V6--仙盟数据库架构-—-—仙盟创梦IDE
数据库·数据库架构·仙盟创梦ide·东方仙盟·东方仙盟数据库
寒山李白3 小时前
MySQL复杂SQL(多表联查/子查询)详细讲解
sql·mysql·子查询·多表联查