欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringMVC打印請求參數(shù)和響應(yīng)數(shù)據(jù)最優(yōu)方案

 更新時(shí)間:2023年07月19日 11:31:04   作者:brucelwl  
項(xiàng)目中經(jīng)常需要打印http請求的參數(shù)和響應(yīng)數(shù)據(jù),本文給大家講解如何在SpringMVC打印請求參數(shù)和響應(yīng)數(shù)據(jù)最優(yōu)方案,感興趣的朋友跟隨小編一起看看吧

項(xiàng)目中經(jīng)常需要打印http請求的參數(shù)和響應(yīng)數(shù)據(jù), 但會出現(xiàn)如下問題:

  • 打印Request Body參數(shù): 請求content-typeapplication/josn格式,如果直接從HttpServletRequest中獲取輸入流, 解析請求數(shù)據(jù), 會導(dǎo)致SpringMVC后續(xù)的請求異常, 讀取輸入流異常.
  • 打印響應(yīng)數(shù)據(jù): 如果直接從HttpServletResponse中獲取輸出流, 解析響應(yīng)數(shù)據(jù), 會導(dǎo)致Web服務(wù)器(Tomcat)后續(xù)響應(yīng)請求異常.

本文講解如何在SpringBoot中使用最優(yōu)方案實(shí)現(xiàn)該功能.

常見方案(非最優(yōu)方案)

常見方案就是通過實(shí)現(xiàn)javax.servlet.FilterHttpServletRequestHttpServletResponse進(jìn)行包裝, 將輸入/輸出流復(fù)制一份, 轉(zhuǎn)成字符串打印, 并且不影響后續(xù)處理.而且SpringMVC已經(jīng)為我們提供了相應(yīng)的包裝類實(shí)現(xiàn)

  • org.springframework.web.util.ContentCachingRequestWrapper
  • org.springframework.web.util.ContentCachingResponseWrapper

但在使用ContentCachingResponseWrapper時(shí), 一定要記住必須調(diào)用ContentCachingResponseWrapper#copyBodyToResponse()將響應(yīng)數(shù)據(jù)寫回HttpServletResponseServletOutputStream,這樣才能成功返回?cái)?shù)據(jù)到客戶端。

注意: 這個(gè)并不是最優(yōu)方案,并且存在諸多缺點(diǎn)

示例代碼如下:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (isStream(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        boolean isFirstRequest = !isAsyncDispatch(request);
        HttpServletRequest requestToUse = request;
        ContentCachingResponseWrapper responseToUse = new ContentCachingResponseWrapper(response);
        if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
            requestToUse = new ContentCachingRequestWrapper(request);
        }
        try {
            filterChain.doFilter(requestToUse, responseToUse);
        } finally {
            accessLog(requestToUse, responseToUse);
            responseToUse.copyBodyToResponse();
        }
}
 private boolean isStream(HttpServletRequest request) {
        return MediaType.TEXT_EVENT_STREAM_VALUE.equalsIgnoreCase(request.getHeader(HttpHeaders.ACCEPT))
                || MediaType.TEXT_EVENT_STREAM_VALUE.equalsIgnoreCase(request.getHeader(HttpHeaders.CONTENT_TYPE));
    }

這個(gè)方案存在如下幾個(gè)缺點(diǎn):

  • 使用包裝類包裝HttpServletRequestHttpServletResponse會導(dǎo)致輸入流和輸出流被多拷貝一次
  • 并不是所有的請求類型都適合對HttpServletResponse包裝, 即使用Spring提供的ContentCachingResponseWrapper也無法實(shí)現(xiàn), 例如SpringMVC所支持的SSE(org.springframework.web.servlet.mvc.method.annotation.SseEmitter)請求,會導(dǎo)致無法向客戶端相應(yīng)數(shù)據(jù). 需要對text/event-stream請求做特殊處理.

SpringWeb 5.1.14版本中ShallowEtagHeaderFilter#disableContentCaching方法的注釋中已經(jīng)說明

/**
	 * This method can be used to disable the content caching response wrapper
	 * of the ShallowEtagHeaderFilter. This can be done before the start of HTTP
	 * streaming for example where the response will be written to asynchronously
	 * and not in the context of a Servlet container thread.
	 * @since 4.2
	 */
	public static void disableContentCaching(ServletRequest request) {
		Assert.notNull(request, "ServletRequest must not be null");
		request.setAttribute(STREAMING_ATTRIBUTE, true);
	}

不方便獲取Request Body對應(yīng)的實(shí)體類類型, 不方便知道響應(yīng)請求的實(shí)體類類型

最優(yōu)方案

實(shí)際上SpringMVC提供了如下兩個(gè)接口,用于在解析Request Body后和響應(yīng)請求前回調(diào)

  • org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceRequestBodyAdvice用于解析Request Body后回調(diào), 可以實(shí)現(xiàn)該接口得到解析后的Body實(shí)體類,RequestBodyAdviceAdapter適配了RequestBodyAdvice
  • org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdviceResponseBodyAdvice用于在向http請求響應(yīng)數(shù)據(jù)之前回調(diào), 可以實(shí)現(xiàn)該接口得到響應(yīng)的實(shí)體類

為了能夠一塊打印請求和響應(yīng)數(shù)據(jù), 必須在請求時(shí)記錄Request Body對象, 這里就考慮使用HttpServletRequestsetAttribute記錄,但是RequestBodyAdvice中無法拿到HttpServletRequest,因此需要在請求時(shí)通過ThreadLocal綁定. 見示例ThreadBindRequestContainer

@Slf4j
@ControllerAdvice
public class RequestResponseBodyAdvice extends RequestBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        HttpServletRequest request = ThreadBindRequestContainer.getServletRequest();
        Method method = parameter.getMethod();
        String declaringClass = method.getDeclaringClass().getSimpleName();
        String handlerMethod = method.toString().substring(method.toString().indexOf(declaringClass));
        request.setAttribute("handlerMethod", handlerMethod);
        request.setAttribute("req_body", body);
        return body;
    }
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
        servletRequest.setAttribute("resp_body", body);
        return body;
    }
}
public class ThreadBindRequestContainer {
    private static final ThreadLocal<HttpServletRequest> threadLocal = new ThreadLocal<>();
    public static void bind(HttpServletRequest request) {
        threadLocal.set(request);
    }
    public static void remove() {
        threadLocal.remove();
    }
    public static HttpServletRequest getServletRequest() {
        return threadLocal.get();
    }
}
@Slf4j
public class RequestResponseBodyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        ThreadBindRequestContainer.bind(request);
        log.info("請求線程" + Thread.currentThread().getName());
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadBindRequestContainer.remove();
        String requestURI = req.getRequestURI();
        Object req_body = req.getAttribute("req_body");
        Object resp_body = req.getAttribute("resp_body");
        Object handlerMethod = req.getAttribute("handlerMethod");
        log.info("請求url:{},處理方法:{}, 參數(shù):{}, 響應(yīng)參數(shù):{}", requestURI, handlerMethod, req_body, resp_body);
        log.info("退出線程" + Thread.currentThread().getName() + "\n");
    }
}

到此這篇關(guān)于SpringMVC打印請求參數(shù)和響應(yīng)數(shù)據(jù)最優(yōu)方案的文章就介紹到這了,更多相關(guān)SpringMVC打印請求參數(shù)和響應(yīng)數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java根據(jù)ip地址獲取詳細(xì)地域信息的方法

    java根據(jù)ip地址獲取詳細(xì)地域信息的方法

    這篇文章主要介紹了java根據(jù)ip地址獲取詳細(xì)地域信息的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • 關(guān)于Intellij IDEA中的Version Control問題

    關(guān)于Intellij IDEA中的Version Control問題

    這篇文章主要介紹了Intellij IDEA中的Version Control問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-11-11
  • SpringMVC如何獲取多種類型數(shù)據(jù)響應(yīng)

    SpringMVC如何獲取多種類型數(shù)據(jù)響應(yīng)

    這篇文章主要介紹了SpringMVC如何獲取多種類型數(shù)據(jù)響應(yīng),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • Java深入學(xué)習(xí)圖形用戶界面GUI之事件處理

    Java深入學(xué)習(xí)圖形用戶界面GUI之事件處理

    這篇文章主要介紹了基于Java GUI 事件處理方式,一個(gè)圖形界面制作完成了,在程序開發(fā)中只是完成了起步的工作。要想讓一個(gè)組件都發(fā)揮自己的作用.就必須對所有的組件進(jìn)行事件處理
    2022-05-05
  • Java中內(nèi)存區(qū)域的劃分與異常詳解

    Java中內(nèi)存區(qū)域的劃分與異常詳解

    最近在看java虛擬相關(guān)知識,把每天看到的一些內(nèi)容做一個(gè)歸納總結(jié),下面這篇文章主要給大家介紹了關(guān)于Java中內(nèi)存區(qū)域的劃分與異常的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧
    2018-06-06
  • Java?斷言?assert的用法詳解

    Java?斷言?assert的用法詳解

    Java?assert斷言機(jī)制是Java5中推出的新特性,它主要用于在程序運(yùn)行時(shí)檢查狀態(tài)或假設(shè)的正確性,本篇文章將全面詳細(xì)地講解Java?assert斷言機(jī)制,包括斷言概述、語法規(guī)則、工作原理、使用場景、注意事項(xiàng)以及示例代碼等方面,需要的朋友可以參考下
    2023-05-05
  • Hibernate雙向多對多映射關(guān)系配置代碼實(shí)例

    Hibernate雙向多對多映射關(guān)系配置代碼實(shí)例

    這篇文章主要介紹了Hibernate雙向多對多映射關(guān)系配置代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java中Set集合轉(zhuǎn)為List集合常見的兩種方式

    Java中Set集合轉(zhuǎn)為List集合常見的兩種方式

    List是Java中比較常用的集合類,指一系列存儲數(shù)據(jù)的接口和類,可以解決復(fù)雜的數(shù)據(jù)存儲問題,這篇文章主要給大家介紹了關(guān)于Java中Set集合轉(zhuǎn)為List集合常見的兩種方式,需要的朋友可以參考下
    2023-12-12
  • Spring?Boot?集成?Quartz并使用Cron?表達(dá)式實(shí)現(xiàn)定時(shí)任務(wù)

    Spring?Boot?集成?Quartz并使用Cron?表達(dá)式實(shí)現(xiàn)定時(shí)任務(wù)

    本篇文章介紹了如何在?Spring?Boot?中集成?Quartz?進(jìn)行定時(shí)任務(wù)調(diào)度,并通過?Cron?表達(dá)式?控制任務(wù)執(zhí)行時(shí)間,Quartz?提供了更強(qiáng)大的任務(wù)調(diào)度能力,比?@Scheduled?注解更靈活,適用于復(fù)雜的定時(shí)任務(wù)需求
    2025-04-04
  • JAVA IO的3種類型區(qū)別解析

    JAVA IO的3種類型區(qū)別解析

    這篇文章主要介紹了JAVA IO的3種類型解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10

最新評論