問題Spring Web是一組庫,可輕鬆從Spring應用程序中產生application/problem+json響應。它填充了一個利基市場,因為它連接了問題庫以及Spring Web MVC的異常處理或Spring Webflux的異常處理,因此它們可以無縫地工作,同時需要最少的額外開發人員工作。這樣一又一勞永逸地,它旨在執行一項小而重複的任務。
該庫的工作方式基於我們所謂的建議特徵。建議性狀是一個小的,可重複使用的@ExceptionHandler ,該特徵是在單個方法接口中放置的默認方法。這些建議特徵可以自由組合,也不需要為您的@ControllerAdvice使用普通基類。
?請查看Baeldung:問題的指南Spring Web庫,以詳細介紹!
AdviceTrait提供的問題處理過程的構建方式是在需要時允許自定義的。可以通過實現適當的建議性狀界面來自定義以下所有方面(以及更多):
| 方面 | 方法) | 預設 |
|---|---|---|
| 創建 | AdviceTrait.create(..) | |
| 記錄 | AdviceTrait.log(..) | 4xx作為WARN ,5xx作為ERROR ,包括堆棧跟踪 |
| 內容談判 | AdviceTrait.negotiate(..) | application/json , application/*+json , application/problem+json和application/x.problem+json |
| 倒退 | AdviceTrait.fallback(..) | application/problem+json |
| 後處理 | AdviceTrait.process(..) | N/A。 |
以下示例通過在Problem中添加parameter擴展字段來自定義MissingServletRequestParameterAdviceTrait :
@ ControllerAdvice
public class MissingRequestParameterExceptionHandler implements MissingServletRequestParameterAdviceTrait {
@ Override
public ProblemBuilder prepare ( Throwable throwable , StatusType status , URI type ) {
var exception = ( MissingServletRequestParameterException ) throwable ;
return Problem . builder ()
. withTitle ( status . getReasonPhrase ())
. withStatus ( status )
. withDetail ( exception . getMessage ())
. with ( "parameter" , exception . getParameterName ());
}
}假設有這樣的控制器:
@ RestController
@ RequestMapping ( "/products" )
class ProductsResource {
@ RequestMapping ( method = GET , value = "/{productId}" , produces = APPLICATION_JSON_VALUE )
public Product getProduct ( String productId ) {
// TODO implement
return null ;
}
@ RequestMapping ( method = PUT , value = "/{productId}" , consumes = APPLICATION_JSON_VALUE )
public Product updateProduct ( String productId , Product product ) {
// TODO implement
throw new UnsupportedOperationException ();
}
}以下HTTP請求將分別產生相應的響應:
GET /products/123 HTTP/1.1
Accept: application/xml HTTP/1.1 406 Not Acceptable
Content-Type: application/problem+json
{
"title" : " Not Acceptable " ,
"status" : 406 ,
"detail" : " Could not find acceptable representation "
} POST /products/123 HTTP/1.1
Content-Type: application/json
{} HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Type: application/problem+json
{
"title" : " Method Not Allowed " ,
"status" : 405 ,
"detail" : " POST not supported "
}在繼續之前,請閱讀有關Zalando/問題中的堆棧跟踪和因果鏈的部分。
如果要啟用堆棧跟踪,請按以下方式配置您的ProblemModule :
ObjectMapper mapper = new ObjectMapper ()
. registerModule ( new ProblemModule (). withStackTraces ());默認情況下,問題的因果鏈被禁用,但如果需要,可以被覆蓋:
@ ControllerAdvice
class ExceptionHandling implements ProblemHandling {
@ Override
public boolean isCausalChainsEnabled () {
return true ;
}
}請注意,由於您可以完全訪問應用程序上下文,因此您可以將配置外部化為application.yml server.error.include-stacktrace
啟用這兩個功能,因果鍊和堆棧都將產生:
{
" title " : " Internal Server Error " ,
" status " : 500,
" detail " : " Illegal State " ,
" stacktrace " : [
" org.example.ExampleRestController.newIllegalState(ExampleRestController.java:96) " ,
" org.example.ExampleRestController.nestedThrowable(ExampleRestController.java:91) "
],
" cause " : {
" title " : " Internal Server Error " ,
" status " : 500,
" detail " : " Illegal Argument " ,
" stacktrace " : [
" org.example.ExampleRestController.newIllegalArgument(ExampleRestController.java:100) " ,
" org.example.ExampleRestController.nestedThrowable(ExampleRestController.java:88) "
],
" cause " : {
" title " : " Internal Server Error " ,
" status " : 500,
" detail " : " Null Pointer " ,
" stacktrace " : [
" org.example.ExampleRestController.newNullPointer(ExampleRestController.java:104) " ,
" org.example.ExampleRestController.nestedThrowable(ExampleRestController.java:86) " ,
" sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) " ,
" sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) " ,
" sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) " ,
" java.lang.reflect.Method.invoke(Method.java:483) " ,
" org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) " ,
" org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) " ,
" org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) " ,
" org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) " ,
" org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) " ,
" org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) " ,
" org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) " ,
" org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) " ,
" org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) " ,
" org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) " ,
" org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) " ,
" org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) " ,
" org.junit.runners.ParentRunner.run(ParentRunner.java:363) " ,
" org.junit.runner.JUnitCore.run(JUnitCore.java:137) " ,
" com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) " ,
" com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) " ,
" com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) "
]
}
}
} Spring允許將@ControllerAdvice的範圍限制為某個控制器的子集:
@ ControllerAdvice ( assignableTypes = ExampleController . class )
public final class ExceptionHandling implements ProblemHandling通過這樣做,您將失去處理某些類型的異常的功能:
HttpRequestMethodNotSupportedExceptionHttpMediaTypeNotAcceptableExceptionHttpMediaTypeNotSupportedExceptionNoHandlerFoundException我們從春季繼承了這一限制,因此建議使用不受限制的@ControllerAdvice 。
如果您有疑問,疑慮,錯誤報告等,請在此存儲庫的問題跟踪器中提交問題。
為了做出貢獻,只需提出拉動請求,然後添加您添加或更改的簡短描述(1-2個句子)。有關更多詳細信息,請檢查貢獻指南。