一:统一处理访问请求(配置拦截器进行处理HandlerInterceptor):
一般情况下,controller中入参很少需要HttpServletRequest和HttpServletResponse参数,但是有时我们需要对访问参数进行日志打印,查看ip等功能时,就需要在入参加上Request和Response,这样每个controller都会增加这两个参数,为了方便维护,可以配置一个interceptor进行统一拦截处理。
package com.test.demo.interceptor.logger;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.time.LocalDateTime; /** * Created by fz */ public class LoggerIntercept implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(LoggerIntercept.class); @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { String method = httpServletRequest.getMethod(); String remoteAddr = httpServletRequest.getHeader("X-Forwarded-For"); String requestURI = httpServletRequest.getRequestURI(); String queryString = httpServletRequest.getQueryString(); logger.info("请求方法={},请求ip={},请求URI={},QueryString={},请求时间={}", method, remoteAddr,requestURI, queryString, LocalDateTime.now()); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { /*无法获取到@ResponseBody的返回值*/ } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { /*pass*/ } }
在spring-web.xml中进行配置:
<bean id="loggerInterceptor" class="com.jd.trip.hotel.ebooking.interceptor.logger.LoggerIntercept"/><!--配置bean-->
<mvc:interceptors> <!--web访问参数拦截器--> <mvc:interceptor> <mvc:mapping path="/**"/><!--拦截器拦截规则,拦截所有--> <mvc:exclude-mapping path="/"/><!--拦截器排除规则--> <ref bean="loggerInterceptor"/> </mvc:interceptor>
</mvc:interceptors>
二:返回值/响应体(@ControllerAdvice,ResponseBodyAdvice)
ResponseBodyAdvice是spring4.1的新特性,其作用是在响应体写出之前做一些处理;比如,修改返回值、加密等。
我在项目中的用到@ControllerAdvice,ResponseBodyAdvice的目的,是为每个请求的返回json中修改一个属性值。
ResponseBodyAdvice 接口源码:
package org.springframework.web.servlet.mvc.method.annotation;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
/**
* Allows customizing the response after the execution of an {@code @ResponseBody}
* or an {@code ResponseEntity} controller method but before the body is written
* with an {@code HttpMessageConverter}.
Implementations may be may be registered directly with
* {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
* or more likely annotated with {@code @ControllerAdvice} in which case they
* will be auto-detected by both.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
public interface ResponseBodyAdvice {
/**
* Whether this component supports the given controller method return type
* and the selected {@code HttpMessageConverter} type.
* @param returnType the return type
* @param converterType the selected converter type
* @return {@code true} if {@link #beforeBodyWrite} should be invoked, {@code false} otherwise
*/
boolean supports(MethodParameter returnType, Class> converterType);
/**
* Invoked after an {@code HttpMessageConverter} is selected and just before
* its write method is invoked.
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified, possibly new instance
*/
T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
Class> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
实战代码如下:
package com.test.demo.interceptor.logger;
import com.test.demo.platform.common.result.base.Result; import com.alibaba.fastjson.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /** * Created by fz */ @ControllerAdvice public class LoggerAdvice implements ResponseBodyAdvice<Result> { private Logger logger = LoggerFactory.getLogger(LoggerAdvice.class); @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { /*支持所有方法*/ return true; } @Override public Result beforeBodyWrite(Result body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { String methodName = returnType.getMethod().getName(); logger.info("{}方法返回值={}", methodName, JSON.toJSONString(body)); return body; } }
result:
package com.test.demo.platform.common.result.base; import com.alibaba.fastjson.annotation.JSONField; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; @ApiModel("服务端响应报文格式") public class Result<T> implements Serializable { private static final long serialVersionUID = -1791232625706807828L; @ApiModelProperty("响应头数据") @JSONField( name = "head", ordinal = 0 ) private ResultHead head; @ApiModelProperty("响应体数据") @JSONField( name = "body", ordinal = 1 ) protected T body; public Result() { } public T getBody() { return this.body; } public void setBody(T body) { this.body = body; } public ResultHead getHead() { return this.head; } public void setHead(ResultHead head) { this.head = head; } public String toString() { return "Result{head=" + this.getHead().toString() + ",data=" + this.body + '}'; } }