方式一
這種方式線上高并發看鍊路追蹤發現new RequestWrapper耗時3秒 後來改為第二種方式
裝飾requset
@Component
@WebFilter(filterName = "wrapperFilter", urlPatterns = {"/**"})
public class WrapperFilter
implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
chain.doFilter(requestWrapper, response);
}
}
package cn.wine.ms.promotion.configuartion;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.Objects;
/**
* @author lau
* 解決因為ServletInputStream 讀取body流 讀取後指針不能還原其他地方不能讀取多次
*/
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
private Reader reader;
public RequestWrapper(HttpServletRequest request)
throws IOException {
super(request);
try {
StringBuffer stringBuffer = new StringBuffer();
request.getReader().lines().forEach(stringBuffer::append);
body = stringBuffer.toString().getBytes();
} catch (IllegalStateException e) {
body = new byte[]{};
log.error("{}接口body為空或丢失",request.getRequestURI());
//部分接口,檔案上傳,body為空,會報IllegalStateException
}
}
public RequestWrapper(HttpServletRequest request, Reader reader) {
super(request);
this.reader = reader;
StringBuffer stringBuffer = new StringBuffer();
new BufferedReader(reader).lines().forEach(stringBuffer::append);
body = stringBuffer.toString().getBytes();
}
@Override
public BufferedReader getReader() throws IOException {
if (Objects.isNull(reader)){
return new BufferedReader(new InputStreamReader(getInputStream()));
}else {
return new BufferedReader(reader);
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
}
方式二
package cn.wine.ms.promotion.configuartion;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* @Project 商品uaa
* @PackageName cn.wine.ms.promotion.configuartion
* @ClassName RequestMappingHandlerAdapterCusotmer
* @Author qiang.li
* @Date 2021/2/23 11:34 上午
* @Description 用于對springRequestMappingHandlerAdapter做一些定制化配置
*/
@Configuration
public class RequestMappingHandlerAdapterCustomizeConfig implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Override
public void afterPropertiesSet() throws Exception {
//相容判斷 防止使用非注解方式 如xml配置
if(requestMappingHandlerAdapter==null){
return;
}
//定制化配置@RequestBody 入參解析器
replaceRequestResponseBodyMethodProcessor();
}
/**
* 使用RequestResponseBodyMethodProcessorWrapper 替換預設的使用RequestResponseBodyMethodProcessor
* 解決因為Request.getInputStream()隻能讀取一次 不能多次讀取的問題
* 1.在原有的基礎上增加将解析結果存入Request attributes 供後續使用
* HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
* Object data = request.getAttribute(RequestResponseBodyMethodProcessorWrapper.resolveArgumentKey);
*/
public void replaceRequestResponseBodyMethodProcessor(){
//獲得解析器處理集合
List<HandlerMethodArgumentResolver> handlerMethodArgumentResolver= requestMappingHandlerAdapter.getArgumentResolvers();
if(!CollectionUtils.isEmpty(handlerMethodArgumentResolver)){
Optional<HandlerMethodArgumentResolver> resolverOptional=handlerMethodArgumentResolver.stream().filter(c->c instanceof RequestResponseBodyMethodProcessor).findAny();
if(resolverOptional.isPresent()){
//因為架構是不可變的 改為可變類型
List<HandlerMethodArgumentResolver> newHandlerMethodArgumentResolvers=new ArrayList<>(handlerMethodArgumentResolver);
//替換為RequestResponseBodyMethodProcessorWrapper
Collections.replaceAll(newHandlerMethodArgumentResolvers, resolverOptional.get(), new RequestResponseBodyMethodProcessorWrapper(resolverOptional.get()));
//替換list
requestMappingHandlerAdapter.setArgumentResolvers(newHandlerMethodArgumentResolvers);
}
}
}
}
package cn.wine.ms.promotion.configuartion;
import org.springframework.core.MethodParameter;
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.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/**
* @Project 商品uaa
* @PackageName cn.wine.ms.promotion.configuartion
* @ClassName RequestResponseBodyMethodProcessorWrapper
* @Author qiang.li
* @Date 2021/2/23 1:20 下午
* @Description RequestResponseBodyMethodProcessor裝飾器,在原有功能上做将結果儲存到request attribute的增加
*/
public class RequestResponseBodyMethodProcessorWrapper implements HandlerMethodArgumentResolver {
public final static String resolveArgumentKey="resolveArgumentObject";
private HandlerMethodArgumentResolver handlerMethodReturnValueHandler;
public RequestResponseBodyMethodProcessorWrapper(HandlerMethodArgumentResolver handlerMethodReturnValueHandler){
this.handlerMethodReturnValueHandler=handlerMethodReturnValueHandler;
}
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//委托
return handlerMethodReturnValueHandler.supportsParameter(methodParameter);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
//委托
Object argumentObject= handlerMethodReturnValueHandler.resolveArgument(methodParameter,modelAndViewContainer,nativeWebRequest,webDataBinderFactory);
//功能增強 将解析結果存入request attribute供後續使用
if(argumentObject!=null){
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
request.setAttribute(resolveArgumentKey,argumentObject);
}
return argumentObject;
}
}
使用 我的場景是異常攔截器列印入參
/**
* 擷取body
* @param httpServletRequest
* @return
*/
public static String readBody(HttpServletRequest httpServletRequest) {
try {
//request body不能重複讀取 處理方案請看cn.wine.ms.promotion.configuartion.RequestMappingHandlerAdapterCustomizeConfig
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
Object data = request.getAttribute(RequestResponseBodyMethodProcessorWrapper.resolveArgumentKey);
return JSON.toJSONString(data);
}catch (Exception e){
log.error("擷取body資料異常:{}",e);
return "getError";
}
}