ปัญหาสปริงเว็บ เป็นชุดของไลบรารีที่ทำให้ง่ายต่อการสร้าง application/problem+json จากแอปพลิเคชันฤดูใบไม้ผลิ มันเติมช่องให้เชื่อมต่อห้องสมุดปัญหาและการจัดการข้อยกเว้นของ Web MVC ในฤดูใบไม้ผลิหรือการจัดการข้อยกเว้นของ WebFlux ในฤดูใบไม้ผลิเพื่อให้พวกเขาทำงานร่วมกันได้อย่างราบรื่นในขณะที่ต้องใช้ความพยายามของนักพัฒนาเพิ่มเติม ในการทำเช่นนั้นมันมีจุดมุ่งหมายที่จะทำงานเล็ก ๆ แต่ซ้ำ ๆ - ครั้งเดียวและสำหรับทุกคน
วิธีการทำงานของห้องสมุดนี้ขึ้นอยู่กับสิ่งที่เราเรียกว่า คุณลักษณะคำแนะนำ ลักษณะคำแนะนำคือ @ExceptionHandler ขนาดเล็กที่ใช้ซ้ำได้นำมาใช้เป็นวิธีเริ่มต้นที่วางไว้ในอินเทอร์เฟซวิธีเดียว ลักษณะคำแนะนำเหล่านั้นสามารถรวมกันได้อย่างอิสระและไม่จำเป็นต้องใช้คลาสฐานทั่วไปสำหรับ @ControllerAdvice ของคุณ
- โปรดตรวจสอบ Baeldung: คำแนะนำเกี่ยวกับปัญหาสปริงไลบรารีเว็บสำหรับการแนะนำรายละเอียด!
กระบวนการจัดการปัญหาที่จัดทำโดย AdviceTrait นั้นถูกสร้างขึ้นในลักษณะที่อนุญาตให้ปรับแต่งได้เมื่อใดก็ตามที่จำเป็นต้องเกิดขึ้น ทุกด้านต่อไปนี้ (และอื่น ๆ ) สามารถปรับแต่งได้โดยการใช้อินเทอร์เฟซลักษณะคำแนะนำที่เหมาะสม:
| ด้าน | วิธีการ | ค่าเริ่มต้น |
|---|---|---|
| การสร้าง | AdviceTrait.create(..) | |
| การตัดไม้ | AdviceTrait.log(..) | 4xx As 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 |
ตัวอย่างต่อไปนี้ปรับแต่ง MissingServletRequestParameterAdviceTrait โดยการเพิ่มฟิลด์ส่วนขยาย parameter ลงใน Problem :
@ 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 กลับมาใช้ใหม่
การเปิดใช้งานคุณสมบัติทั้งสองโซ่เชิงสาเหตุและ stacktraces จะให้ผล:
{
" 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) "
]
}
}
} ฤดูใบไม้ผลิอนุญาตให้ จำกัด ขอบเขตของ @ControllerAdvice ไปยังชุดย่อยของคอนโทรลเลอร์:
@ ControllerAdvice ( assignableTypes = ExampleController . class )
public final class ExceptionHandling implements ProblemHandlingโดยการทำเช่นนี้คุณจะสูญเสียความสามารถในการจัดการข้อยกเว้นบางประเภทคือ:
HttpRequestMethodNotSupportedExceptionHttpMediaTypeNotAcceptableExceptionHttpMediaTypeNotSupportedExceptionNoHandlerFoundException เราสืบทอดข้อ จำกัด นี้จากฤดูใบไม้ผลิและขอแนะนำให้ใช้ @ControllerAdvice ที่ไม่ จำกัด
หากคุณมีคำถามข้อกังวลรายงานข้อผิดพลาด ฯลฯ โปรดยื่นปัญหาในตัวติดตามปัญหาของที่เก็บนี้
ในการมีส่วนร่วมเพียงทำคำขอดึงและเพิ่มคำอธิบายสั้น ๆ (1-2 ประโยค) ของการเพิ่มหรือการเปลี่ยนแปลงของคุณ สำหรับรายละเอียดเพิ่มเติมตรวจสอบแนวทางการบริจาค