目录
4.CustomerDispatcherServlet.java
5.CustomerHttpServletRequestWrapper.java
1.结构
上面两个绿色的是为了解决WebInterceptor.java中记录的400:Required request body is missing错误自定义的类。
WebMvcConfig配置类中需要把CustomerDispatcherServlet注册为默认的DispatcherServlet:
//配置类中把CustomerDispatcherServlet注册为默认的DispatcherServlet @Bean @Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { return new CustomerDispatcherServlet(); }
2.WebInterceptor.java
package com.genertech.plm.aia.login.interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 创建拦截器-拦截需要安全访问的请求
* 方法说明
* 1.preHandle():前置处理回调方法,返回true继续执行,返回false中断流程,不会继续调用其它拦截器
* 2.postHandle():后置处理回调方法,但在渲染视图之前
* 3.afterCompletion():全部后置处理之后,整个请求处理完毕后回调。
*/
public class WebInterceptor implements HandlerInterceptor {
private final Logger log = LoggerFactory.getLogger(WebInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//Map<String, String[]> request.getParameterMap(),不适用以@RequestBody注解接收参数的请求
//request.getParameterNames()方法是将发送请求页面中form表单里所有具有name属性的表单对象获取(包括button).返回一个Enumeration类型的枚举.
//GET--获取请求参数
/*String queryString = request.getQueryString();
log.info("GET 请求参数:{}", queryString);*/
//POST--获取请求body(400错误-需要重写ServletInputStream request.getInputStream()方法)
byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
String body = new String(bodyBytes, request.getCharacterEncoding());
log.info("[ WebInterceptor ] >> preHandle requestUrI:{} requestBody:{}", request.getRequestURI(), body);
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
3.WebMvcConfig.java
package com.genertech.plm.aia.login.interceptor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import java.util.List;
@Configuration
public class WebMvcConfig /*extends WebMvcConfigurationSupport*/ implements WebMvcConfigurer {
/**
* 创建拦截器
*/
@Bean
WebInterceptor webInterceptor() {
return new WebInterceptor();
}
/**
* 添加拦截器-进行拦截
* addPathPatterns 添加拦截
* excludePathPatterns 排除拦截
**/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webInterceptor())
.addPathPatterns("/**");
//.excludePathPatterns("/test");//不能包含server.context-path=/cas的cas
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//资源位置映射
//registry.addResourceHandler("/themes/portal/images/**").addResourceLocations("file:C:/Users/Administrator/Desktop/");
}
//配置类中把CustomerDispatcherServlet注册为默认的DispatcherServlet
@Bean
@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new CustomerDispatcherServlet();
}
@Override
public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
}
@Override
public void addFormatters(FormatterRegistry formatterRegistry) {
}
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
}
@Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
}
@Override
public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
@Override
public Validator getValidator() {
return null;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
4.CustomerDispatcherServlet.java
package com.genertech.plm.aia.login.interceptor;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @beLongProjecet: jets-aia
* @beLongPackage: com.genertech.plm.aia.login.interceptor
* @author: sizzled
* @createTime: 2023/02/01 13:33
* @description: 定义一个DispatcherServlet子类来分派自定义的CustomerHttpServletRequestWrapper,
* 还需要在配置类(WebMvcConfig.java)中把CustomerDispatcherServlet注册为默认的DispatcherServlet
* @version: v1.0
*/
public class CustomerDispatcherServlet extends DispatcherServlet {
/**
* 包装成我们自定义的request
* @param request
* @param response
* @throws Exception
*/
@Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.doDispatch(new CustomerHttpServletRequestWrapper(request), response);
}
}
5.CustomerHttpServletRequestWrapper.java
package com.genertech.plm.aia.login.interceptor;
import org.springframework.util.StreamUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
* @beLongProjecet: jets-aia
* @beLongPackage: com.genertech.plm.aia.login.interceptor
* @author: sizzled
* @createTime: 2023/02/01 13:26
* @description: 自定义HttpServletRequestWrapper来包装输入流,解决:
* //400:Required request body is missing
* //其实是ServletInputStream被读取后无法第二次再读取了,所以我们要把读取过的内容存下来,然后需要的时候对外提供可被重复读取的ByteArrayInputStream。
* //对于MVC的过滤器来说,我们就需要重写ServletInputStream request.getInputStream()方法。
* @version: v1.0
*/
public class CustomerHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 缓存下来的HTTP body
*/
private byte[] body;
public CustomerHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtils.copyToByteArray(request.getInputStream());
}
/**
* 重新包装输入流
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream bodyStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bodyStream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
6.补充:AOP方式更简洁
ApplicationLogAspect.java
package com.genertech.plm.aia.login.aop;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 全局日志控制
*/
@Aspect
@Component
public final class ApplicationLogAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("execution(public * com.genertech.plm.aia.login.controller..*.*(..)) && !execution(public * uploadExcel(..)) && !execution(public * excel2007Export(..))")
public void controllerLog() {
}
@Before("controllerLog() ") //在切入点的方法run之前要干的
public void logBeforeController(JoinPoint joinPoint) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
Object[] args = joinPoint.getArgs();
String method = request.getMethod();
String params = "";
int length = args.length;
//获取请求参数集合并进行遍历拼接
if (length > 0) {
if ("POST".equals(method)) {
for (int i = 0; i < length; i++) {
Object object = args[i];
if (i == 0) {
params = JSON.toJSONString(object);
} else {
params += "&" + object.toString();
}
}
} else if ("GET".equals(method)) {
params = request.getQueryString();
}
}
logger.info("-----request :{},----------param:{} ", request.getRequestURL().toString(), params);
}
@AfterReturning(returning = "returnOb", pointcut = "controllerLog())")
public void doAfterReturning(JoinPoint joinPoint, Object returnOb) {
logger.warn("CLASS_METHOD:{}.{}, return :{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), JSON.toJSONString(returnOb));
}
}