When throwing an exception from a spring-boot controller, the message in the server response is empty - but only if I don't run locally. That last part is what's confusing me the most. I mean, it would make perfect sense to be able to have spring-boot remove parts from the error response. Like the stacktrace for example, noone wants to send that out except when debugging.
But when I run the application locally, I get the full error response, message, stacktrace and all (even when not running in debug mode, which I first suspected might be the reason for this). A typical error might look something like this:
{
"timestamp": "2020-10-14T09:46:35.784+00:00",
"status": 400,
"error": "Bad Request",
"trace": "webcam.yellow.service.controller.error.BadRequestException: New password must be different from old password! at /*REDACTED*/",
"message": "New password must be different from old password!",
"path": "/users/9"
}
But when I produce the same error on a deployed server, all I get is this:
{
"timestamp": "2020-10-14T09:29:57.720+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/users/9"
}
I don't mind the stacktrace being removed at all (in fact I want it to be removed), but I would really like to receive that error message.
One thought I had was that it might be related to cross-origin access, but I get the same behaviour when producing the error through swagger instead of our frontend, and swagger is same-origin.
I would fully expect such behaviour to be configurable in spring-boot, that would be convenient. Trouble is, I'm not configuring it. I compared the configuration properties of the running server to my local ones and I don't see any property that might be responsible for that. Nor can I find any if I google it. According to all the tutorials I find, this should work just fine. Which it kind of does, except not on the running servers.
Does anybody know what in spring-boot is causing this behaviour and how to configure it? Using spring-boot 2.3.3 by the way.
Additional information:
After some fooling around, I managed to reproduce the problem locally. I get the shortened error response if I build the application, and then run it from the command line directly with java -jar. Running gradle bootRun results in the server returning the full error message.
I've tried to return my own error response through a ControllerAdvice:
#ControllerAdvice
class BadRequestHandler : ResponseEntityExceptionHandler() {
#ExceptionHandler(value = [BadRequestException::class])
protected fun handleBadRequest(ex: BadRequestException, request: WebRequest): ResponseEntity<Any> {
val message = ex.message
return ResponseEntity(message, HttpHeaders(), HttpStatus.BAD_REQUEST)
}
}
This was just intended to be a quick test to see if I could change the server response. Turns out I can't, the client still gets the same response, although the handler is executed. So whatever takes the information out of the request must come further down the chain.
Does anybody have any idea what's happening here??
This is intended behavior since Spring Boot 2.3 as explained here
Setting server.error.include-message=always in the application.properties resolves this issue.
The response can be configured by injecting a custom ErrorController, like for example this one:
#Controller
class ExampleErrorController(private val errorAttributes: ErrorAttributes) : ErrorController {
private val mapper = ObjectMapper()
#RequestMapping("/error")
#ResponseBody
fun handleError(request: HttpServletRequest): String {
val webRequest = ServletWebRequest(request)
val error = errorAttributes.getError(ServletWebRequest(request))
// if it's not a 500, include the error message in the response. If it's a 500, better not...
val errorAttributeOptions = if (error !is HttpServerErrorException.InternalServerError) {
ErrorAttributeOptions.defaults().including(ErrorAttributeOptions.Include.MESSAGE)
} else ErrorAttributeOptions.defaults()
val errorDetails = errorAttributes.getErrorAttributes(
webRequest, errorAttributeOptions)
return mapper.writeValueAsString(errorDetails)
}
override fun getErrorPath(): String = "/error"
}
Note that this takes ErrorAttributeOptions.defaults() as a baseline, then configures what goes in. It appears that this default object is the one used by the default ErrorController spring boot provides, and it is in fact this object that is different depending on whether I run this from gradle/intelij directly or build it into a jar. Why I couldn't find out, but I verified and confirmed the behaviour. I assume it is intended, albeit not widely documented.
Once I learned this, I wondered why it wasn't possible to just configure the default Options object globally for an application rather than providing an entire controller, which in many instances would be sufficient, but it does not look like that's possible at this point.
My service is currently experiencing 415 errors, but I'm not getting any logs to find out what errors they are.
Will I be able to add logging in some kind of filter so that I can know what's going on?
#RequiresHttps
#RequestMapping(value = "/test", method = RequestMethod.POST)
public ResponseEntity<String> doAction(#NonNull #RequestBody CustomRequest[] requests) {
It looks like the 415 is happening inside spring mvc engine and it doesn't event reach your controller so that you can't really place the logs in our code (in doAction method for example).
Try to enable tomcat embedded access logs and you'll see the file with all the requests and return status. These are disabled by default so you should add the following into application.properties or yaml:
server.tomcat.accesslog.enabled=true
There are some configurations / customizations you can do with that, you can read about them in this tutorial for example
I am currently using spring boot 2.1.4 and jackson 2.9.8
When hitting the health endpoint the details are not displayed.
{
status: "UP"
}
I have added the following property to my configuration.
management.endpoint.health.show-details=always
When I look at the environment management.endpoint.health.show-details it is set.
When I monitor org.springframework.boot.actuate.health.HealthWebEndpointResponseMapper.HealthWebEndpointResponseMapper(HealthStatusHttpMapper, ShowDetails, Set<String>) it is showing that ALWAYS is in fact being used, and details is not being stripped out.
However, when it gets to com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector it doesn't add details, because the method getDetails has not been annotated in org.springframework.boot.actuate.health.Health like it has with getStatus (being #JsonWrapped)
How do I get details to be serialized by Jackson, so they can be returned via the health endpoint?
EDIT: This appears to be getting caused by a custom ObjectMapper that is being injected into the spring environment at startup which sets both AUTO_DETECT_GETTERS and AUTO_DETECT_IS_GETTERS to disabled. While this may of worked in spring 4, it no longer appears to work in spring 5.
I am using spring for my project and a question arises in my mind that what is the basic difference between these two HttpStatus
return ResponseEntity.status(HttpStatus.SC_NOT_FOUND).body("Email address not found");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Token Expired");
The first one is from the apache servlet API for status codes from Interface HttpServletResponse found here
SC_NOT_FOUND - Status code (404) indicating that the requested
resource is not available.
The second one is from spring framework http status codes constants from here
NOT_FOUND 404 Not Found.
For spring Framework (& spring boot) the second one is used widely.
There is no difference, it is same status code for HTTP from different libraries.
I'm using Spring Hateoas in a Boot app to avoid manual creation of links in the view. It works great in Thymeleaf views, it works when a controller calls a service to send an email that is also rendered by Thymeleaf.
The code to create link is pretty standard
this.readLink = linkTo(methodOn(PostController.class)
.readPost(eventId, postId))
.withRel("ReadPost");
But for a #Scheduled service generated email, it fails like this
015-08-23 22:28:40.886 ERROR 1180 --- [pool-2-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
java.lang.IllegalStateException: Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?
at org.springframework.util.Assert.state(Assert.java:392) ~[spring-core-4.2.0.RELEASE.jar:4.2.0.RELEASE]
at org.springframework.hateoas.mvc.ControllerLinkBuilder.getCurrentRequest(ControllerLinkBuilder.java:242) ~[spring-hateoas-0.18.0.RELEASE.jar:na]
at org.springframework.hateoas.mvc.ControllerLinkBuilder.getBuilder(ControllerLinkBuilder.java:189) ~[spring-hateoas-0.18.0.RELEASE.jar:na]
at org.springframework.hateoas.mvc.ControllerLinkBuilderFactory.linkTo(ControllerLinkBuilderFactory.java:121) ~[spring-hateoas-0.18.0.RELEASE.jar:na]
Is there anything I can do to get around the lack of an HttpServletRequest due to the code running as a #Scheduled job?
ControllerLinkBuilder currently can only be used from within a request as only that allows it to create a fully qualified link using server and port information from it.
Within an #Scheduled-invoked method that information is not available. If you provide more information on what you're actually creating in that very method, I can suggest workarounds.