- 最終目標:
在 RestController 的 @ExceptionHandler 中記錄請求正文字串。
- 解釋
默認情況下,當請求是無效的 json 時,springboot 會拋出一個HttpMessageNotReadableException,但訊息非常籠統,并且不包括特定的請求正文。這使得調查變得困難。另一方面,我可以使用過濾器記錄每個請求字串,但是這樣日志將被太多成功的日志淹沒。我只想在請求無效時記錄它。我真正想要的是@ExceptionHandler我會得到那個字串(以前在某個地方)并記錄為錯誤。
為了說明問題,我在 github 中創建了一個演示專案。
- 控制器:
@RestController
public class GreetController {
protected static final Logger log = LogManager.getLogger();
@PostMapping("/")
public String greet(@RequestBody final WelcomeMessage msg) {
// if controller successfully returned (valid request),
// then don't want any request body logged
return "Hello " msg.from;
}
@ExceptionHandler({HttpMessageNotReadableException.class})
public String addStudent(HttpMessageNotReadableException e) {
// this is what I really want!
log.error("{the request body string got somewhere, such as Filters }");
return "greeting from @ExceptionHandler";
}
}
- 客戶端
有效請求
curl -H "Content-Type: application/json" http://localhost:8080 --data '{"from":"jim","message":"nice to meet you!"}'無效請求(無效 json)
curl -H "Content-Type: application/json" http://localhost:8080 --data '{"from":"jim","message""nice to meet you!"}'
我曾經嘗試過HandlerInterceptor,但會遇到一些錯誤,例如
'java.lang.IllegalStateException: 在 getReader() 已經為當前請求呼叫后無法呼叫 getInputStream()'。
經過一番搜索1 2,我決定使用Filterwith ContentCachingRequestWrapper。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpServletRequest);
chain.doFilter(cachedRequest, response);
String requestBody = IOUtils.toString(cachedRequest.getContentAsByteArray(), cachedRequest.getCharacterEncoding());
log.info(requestBody);
}
此代碼運行良好,只是日志位于RestController. 如果我更改順序:
String requestBody = IOUtils.toString(cachedRequest.getReader());
log.info(requestBody);
chain.doFilter(cachedRequest, response);
適用于無效請求,但當請求有效時,出現以下例外:
com.example.demo.GreetController :缺少必需的請求正文:public java.lang.String com.example.demo.GreetController.greet(com.example.demo.WelcomeMessage)
我也嘗試了getContentAsByteArray,getInputStream和getReader方法,因為一些教程說框架會檢查特定的方法呼叫。
按照@MCommonsRequestLoggingFilter的建議進行了嘗試。迪南。
但一切都是徒勞的。
現在我有點困惑。當請求有效和無效時,誰能解釋RestControllerand的執行順序?Filter有沒有更簡單的方法(更少的代碼)來實作我的最終目標?謝謝!
我正在使用springboot 2.6.3,jdk11。
uj5u.com熱心網友回復:
創建一個過濾器,將您的請求包裝在一個
ContentCachingRequestWrapper(僅此而已)中。在您的例外處理方法中使用
HttpServletRequest作為引數作為引數檢查實體是否
ContentCachingRequestWrapper使用
getContentAsByteArray獲取內容。
像這樣的東西。
public class CachingFilter extends OncePerRequestFilter {
protected abstract void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
filterChain.doFilter(new ContentCachingRequestWrapper(request), new ContentCachingResponseWrapper(response));
}
注意:我也包裝了回應,以防萬一您也想要。
現在在您的例外處理方法中使用HttpServletRequest作為引數并使用它對您有利。
@ExceptionHandler({HttpMessageNotReadableException.class})
public String addStudent(HttpMessageNotReadableException e, HttpServletRequest req) {
if (req instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req;
log.error(new String(wrapper.getContentAsByteArray()));
}
return "greeting from @ExceptionHandler";
}
可能是多個過濾器添加了一個包裝器,HttpServletRequest因此您可能需要遍歷這些包裝器,您也可以使用它
private Optional<ContentCachingRequestWrapper> findWrapper(ServletRequest req) {
ServletRequest reqToUse = req;
while (reqToUse instanceof ServletRequestWrapper) {
if (reqToUse instanceof ContentCachingRequestWrapper) {
return Optional.of((ContentCachingRequestWrapper) reqToUse);
}
reqToUse = ((ServletRequestWrapper) reqToUse).getRequest();
}
return Optional.empty();
}
你的例外處理程式看起來像這樣
@ExceptionHandler({HttpMessageNotReadableException.class})
public String addStudent(HttpMessageNotReadableException e, HttpServletRequest req) {
Optional<ContentCachingRequestWrapper) wrapper = findWrapper(req);
wrapper.ifPresent(it -> log.error(new String(it.getContentAsByteArray())));
return "greeting from @ExceptionHandler";
}
但這可能取決于您的過濾器順序以及是否有多個過濾器添加包裝器。
uj5u.com熱心網友回復:
在@M.Deinum 的評論之后,我解決了并希望對其他人有用:
- 添加過濾器
public class MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpServletRequest);
chain.doFilter(cachedRequest, response);
}
}
- 注入
ContentCachingRequestWrapper_ExceptionHandler
@ExceptionHandler({ HttpMessageNotReadableException.class })
public String addStudent(HttpMessageNotReadableException e, ContentCachingRequestWrapper cachedRequest) {
log.error(e.getMessage());
try {
String requestBody = IOUtils.toString(cachedRequest.getContentAsByteArray(), cachedRequest.getCharacterEncoding());
log.error(requestBody);
} catch (IOException ex) {
ex.printStackTrace();
}
return "greeting from @ExceptionHandler";
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/422450.html
標籤:
上一篇:懸停在X上時X或工具提示的句柄
