统一处理访问请求和返回值/响应体@ControllerAdvice,ResponseBodyAdvice,HandlerInterceptor

  

一:统一处理访问请求(配置拦截器进行处理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 + '}';
    }
}

猜你喜欢

转载自blog.csdn.net/fz13768884254/article/details/81868835