مشكلة Spring Web هي مجموعة من المكتبات التي تجعل من السهل إنتاج application/problem+json استجابات من تطبيق الربيع. إنه يملأ مكانة ، حيث يربط مكتبة المشاكل وإما أن تعامل استثناءات Web MVC أو استثناء Spring WebFlux بحيث تعمل بسلاسة معًا ، مع طلب الحد الأدنى من جهد المطور الإضافي. في القيام بذلك ، يهدف إلى أداء مهمة صغيرة ولكن متكررة - مرة واحدة وإلى الأبد.
تعتمد الطريقة التي تعمل بها هذه المكتبة على ما نسميه سمات المشورة . سمة النصيحة هي عبارة عن @ExceptionHandler صغير يمكن إعادة استخدامه كطريقة افتراضية موضوعة في واجهة طريقة واحدة. يمكن دمج سمات المشورة هذه بحرية ولا تتطلب استخدام فئة قاعدة مشتركة لـ @ControllerAdvice .
؟ يرجى مراجعة Baeldung: دليل لمشكلة مكتبة Web Spring للحصول على مقدمة مفصلة!
تم تصميم عملية معالجة المشكلات التي توفرها 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(..) | ن/أ |
المثال التالي يخصي 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 ;
}
} ملاحظة نظرًا لأن لديك وصولًا كاملاً إلى سياق التطبيق في تلك المرحلة server.error.include-stacktrace فيمكنك توجيه التكوين إلى application.yml .
إن تمكين كل من الميزات والسلاسل السببية و 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 جمل) من الإضافة أو التغيير. لمزيد من التفاصيل ، تحقق من إرشادات المساهمة.