-
Notifications
You must be signed in to change notification settings - Fork 41.1k
DispatcherServlet#setThrowExceptionIfNoHandlerFound doesn't work for REST application #3980
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
We've clearly not done a great job at documenting this or making it particularly easy to use For your specific case I think that you can implement your own @Controller
@RequestMapping("${error.path:/error}")
public class CustomErrorController implements ErrorController {
@Value("${error.path:/error}")
private String errorPath;
@Override
public String getErrorPath() {
return this.errorPath;
}
@RequestMapping
@ResponseBody
public ResponseEntity<Object> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
return new ResponseEntity<Object>(new CustomErrorType(status.value(), status.name()), status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
}
|
@philwebb I originally started with an When I changed the Spring Boot dependency to 1.2.x, the behaviour changed in ways not documented in the migration notes, so I had to scour the web for a better alternative. Using Unfortunately, the mechanisms made available to customise the Can you confirm if the Can you advise me how to prevent the framework from calling |
I'm sorry about that, whatever the issue was we should have at least documented it.
I agree, that does seem to be the best long-term solution. @rstoyanchev from the MVC team could probably confirm this.
I'll double check this when looking into #4000 and make sure we add a test case or sample. I've not had time to dig deeper into this yet.
I will, as soon as I look into #4000 and check the MVC implementation details. My aim with suggesting that you use a custom |
It sounds like when #4000 is resolved, it should be possible to use ResponseEntityExceptionHandler annotated with @ControllerAdvice consistently. If you want to implement your own REST API error handling it's indeed the most idiomatic Spring MVC way. Nothing wrong with relying on the Boot provided ErrorController mechanism either for its default JSON responses or as a place to handle any other unhandled exceptions (e.g. from view rendering). |
Thanks for your help @philwebb, @rstoyanchev. For what it's worth, the undocumented behaviour changes I mentioned largely relate to Spring Security filtering and Using a custom The |
👍 |
It turns out the exception is gone from the request - only the high level details are there. The workaround listed above doesn't help us - we need the exception so that we can map some information there onto our response object. |
@jasperblues Can you provide a small sample that illustrates your problem? |
Sure. The following works fine: @Controller
@RequestMapping("@{error.path:/error}")
class VamprErrorController @Autowired constructor(errorAttributes: ErrorAttributes): ErrorController
{
@Value("@{error.path:/error}")
lateinit var mapping: String
val errorAttributes: ErrorAttributes
init
{
this.errorAttributes = errorAttributes
}
@RequestMapping
@ResponseBody
fun error(request: HttpServletRequest): ResponseEntity<ErrorResponse>
{
val status = getStatus(request)
val attributes = getErrorAttributes(request)
val header = ResponseHeader(ResponseCode.VAMPServiceError, attributes["message"] as String?)
val errorResponse = ErrorResponse(header)
return ResponseEntity(errorResponse, status)
}
private fun getErrorAttributes(request: HttpServletRequest): Map<String, Any>
{
val requestAttributes = ServletRequestAttributes(request)
return this.errorAttributes.getErrorAttributes(requestAttributes, true)
}
//etc utility methods But in the example above, where we have public enum class ResponseCode
{
VAMPServiceError,
VAMPUnauthorized,
VAMPContentNotFound
}
interface ResponseCodeReporter
{
fun responseCode() : ResponseCode
}
/**
* By default, throwable maps to VAMPServiceError. Specific exception types may return alternative a ResponseCode that
* is appropriate to the type.
*/
public fun Throwable.responseCode() : ResponseCode
{
return ResponseCode.VAMPServiceError
} And specific exception types can have their own response code. When it came to wiring it up, the following does not work, because the exception is no longer in the @RequestMapping
@ResponseBody
fun error(request: HttpServletRequest): ResponseEntity<ErrorResponse>
{
val status = getStatus(request)
val requestAttributes = ServletRequestAttributes(request)
val exception = errorAttributes.getError(requestAttributes) //<----- Gone. This is null
val header = ResponseHeader(exception.responseCode(), exception.message)
val errorResponse = ErrorResponse(header)
return ResponseEntity(errorResponse, status)
} If you set a break-point on Like @igilham our API contract includes a custom payload for errors. I need to map the exception onto an application field. |
Thanks, @jasperblues.
What exception are you referring to? It works fine for me if I have a handler method that throws an exception.
@igilham was having trouble with In short, you haven't really given me enough to go on. What you have given me is also written in Kotlin(?) which makes running it rather tricky or means I need to convert it to Java and may inadvertently end up testing something that's subtly different. Can you please provide a complete sample that reproduces the specific problem that you're seeing? A project on GitHub that I can clone and run, along with details of what request to make to trigger the problem would be ideal. |
hello @wilkinsona,
Sure. I'll provide answers to your questions below first. Perhaps there's something that I'm doing obviously wrong - I'm new to Spring Boot. If something's still not clear, I'll set up a sample project with a failing test and accompanying documentation for you.
It was indeed. I think Kotlin is a fantastic pairing for Spring Boot! It provides a fashionable, modernized syntax along with better support for functional programming, where needed, without eschewing the existing Java eco-system. Additionally, we're a mobile and rich-media studio (check out our Spring inspired https://github.com/appsquickly/Typhoon for iOS) and we see the following additional benefits with Kotlin:
. . . but back to the problem at hand: Do you need me to attach the sample project in Java or is it enough to have a gradle.build and failing test that would allow you to observe the problem first-hand?
😊 Ah, sorry, that may be the case. My issue is:
{
"response": {
"code": "VAMPForbidden",
"message": "Description of error",
}
}
In Summary:
|
This would be ideal for me, and was working before. Proceed with sample project? |
@wilkinsona After studying the docs for Spring Boot (rtfm before reading github issues) it turns out that I'm simply asking why my @ControllerAdvice exception handler is not firing. Question here: http://stackoverflow.com/questions/34366964/spring-boot-restore-controlleradvice-aka-remove-basicerrorcontroller You were right, my question was not related to this ticket. Edit: Solved: http://stackoverflow.com/a/34367982/404201 |
Hello, everyone! This issue is still true even for Spring Boot 1.3.2 When |
@xolvo this issue is still open... |
@xolvo What have you done in addition to setting Given the changes in #3998, #3999, and #4000 I think there's a good chance that this issue can now be closed. |
@wilkinsona I tried every possible solution from this issue or stackoverflow. I even created Spring Boot project from scratch with only web component and try |
@xolvo If you are using Spring Boot's default configuration for handling static resources then the resource handler will be handling the request (it's ordered last and mapped to You may want to consider disabling the static resource handling entirely if you're building a RESTful API:
|
Awesome! With this option disabled another option |
@xolvo I'm pleased to hear it's working as expected. Thanks for letting us know. |
Joining a little late... but I do need to use static resource mapping and still need the exception to be thrown in case there was NO static resource. My project is web and not REST. Thanks! |
@checklist This issue is closed, and we believe the problem's been solved. If you're looking for some guidance please ask on Stack Overflow. If you believe you've found a bug please open a new issue with a detailed description and, ideally, a sample that reproduces the problem. |
See #7653 |
This issue occurs in Spring Boot 1.1.12, 1.2.0, and 1.2.5. There are a few related answers on StackOverflow but none of them seem to work.
I'm trying to override the error handling behaviour to return a json-encoded custom POJO to the user when exceptions are thrown by the controllers in my application. This works for most exceptions but I can't get it to work with for 404s where there is no handler matching the path of the request.
Overriding the
DispatcherServlet
and settingsetThrowExceptionIfNoHandlerFound
totrue
does not result in the appropriate exception handler being called.Here is my
DispatcherServlet
Bean declaration:And here's part of the global Exception handler:
I have also tried using the
BeanPostProcessor
interface to customise theDispatcherServlet
as noted in a comment on an answer to this question, but that doesn't work either.The documentation for how to handle global exceptions and customise the
DispatcherServlet
is incredibly obtuse. Am I missing something or is there a bug in there somewhere?The text was updated successfully, but these errors were encountered: