Proper Handling of PropertyReferenceException for Query Params - java

In a Spring (Boot) application we're heavily using the Pageable interface and have our controller methods mostly defined like so:
public ResponseEntity<List<Thing>> fetchAll(Pageable pageable) {
Page things = this.thingService.findAll(pageable);
return new ResponseEntity<>(things.getContent(), HttpStatus.OK);
}
When accessing it via the following endpoint:
/api/things/?size=5&page=1&sort=name,asc
If the sort property, name in this case, is misspelled or doesn't exist, a PropertyReferenceException is thrown and a 500 sent back to the user with a message such as:
{
"timestamp": "2017-01-30 13:40:47",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.data.mapping.PropertyReferenceException",
"message": "No property name found for type Thing!",
"path": "/api/things"
}
While this is technically the right error message, it doesn't seem like 500 is the appropriate code to respond with. 5xx level message are typically reserved for server issues and this is technically a user issue so more like a 4xx level message.
Best I can see if that maybe it's just a 400. My question is a) is 400 an appropriate response in this situation and b) is there an elegant way to handle this. My current solution is to catch that Exception in my ControllerAdvice class and just return the 400.

Related

How to handle 404 exceptions using #ControllerAdvice and #ExceptionHandler?

I have a problem with Spring's exception handling for controllers. I have a class annotated with #RestControllerAdvice with a couple of #ExceptionHandler's, like this:
#ExceptionHandler(HttpRequestMethodNotSupportedException::class)
fun methodNotSupportedException(
exception: HttpRequestMethodNotSupportedException,
request: HttpServletRequest
): ResponseEntity<ApiError> {
logger().error("Method not supported: {}", exception.message)
val methodNotAllowed = HttpStatus.METHOD_NOT_ALLOWED
val apiError = logAndBuildApiError(request, methodNotAllowed, exception)
return ResponseEntity(apiError, methodNotAllowed)
}
and they work perfectly fine. In this case, when I'm trying to use an non-implemented HTTP method like POST:
{
"requestUri": "/api/v1/items",
"status": 405,
"statusText": "Method Not Allowed",
"createdAt": "2023-01-12T16:50:36.55422+02:00",
"errorMessage": "Request method 'POST' not supported"
}
What I would like to achieve is to handle situations when someone is trying to reach an non-existing endpoint, i.e. the correct one is GET http://localhost:8080/api/v1/items.
But when I'm trying to reach http://localhost:8080/api/v1/itemss, which is of course nonexistent, I recieve a regular Spring whitelabel error page, but I would like to receive a JSON like in the former example:
{
"requestUri": "/api/v1/itemss",
"status": 404,
"statusText": "Not Found",
"createdAt": "2023-01-12T16:52:06.932108+02:00",
"errorMessage": "Some error message"
}
How do I implement a #ExceptionHandler so it could handle exceptions related to non-existing resources?
spring.mvc.throw-exception-if-no-handler-found works in conjunction with
spring.mvc.static-path-pattern. By default, the static path pattern is /**, which includes the whitelabel error pages that you're seeing.
See https://github.com/spring-projects/spring-boot/pull/31660
and https://gitter.im/spring-projects/spring-boot?at=62ba1378568c2c30d30790af
and https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.servlet.spring-mvc.static-content
Option one is to set these two properties in your configuration.
spring:
mvc:
throw-exception-if-no-handler-found: true
static-path-pattern: /static
Option 2 is to add #EnableWebMvc to your spring boot application, and set the spring.mvc.throw-exception-if-no-handler-found property to true. By adding EnableWebMvc you'll be getting the WebMvcConfigurationSupport bean, which will cause Spring not to initialize the WebMvcAutoConfiguration and thereby not set the static-path-pattern.

Customize Spring Boot error response code without changing the default body

Spring Boot by default returns a response body for exceptions that meets my needs out of the box:
{
"timestamp": 1587794161453,
"status": 500,
"error": "Internal Server Error",
"exception": "javax.persistence.EntityNotFoundException",
"message": "No resource exists for the given ID",
"path": "/my-resource/1"
}
However, I would like to customize the response code for different types of exceptions thrown by my application. Some of the exceptions are not ones I own, so I can't just stick a #ResponseStatus annotation on the exception class. I've tries using an #ExceptionHandler method with #ResponseStatus, but that is overwriting the response body, which I don't wish to happen. For instance, I would like to map javax.persistence.EntityNotFoundException to return status code 404.
#ExceptionHandler
#ResponseStatus(HttpStatus.NOT_FOUND)
public void handleEntityNotFoundException(EntityNotFoundException e) {
// This method returns an empty response body
}
This question is similar to mine, but it is also attempting to adjust the response body. I am hoping that there is a shorter, more idiomatic way of adjusting just the status code and not the body.
It turns out that this answer to the question I mentioned had the exact solution needed to solve this problem, even though it didn't quite fully the question asked. The trick was dropping the use of #ResponseStatus from the method, and manually setting the status on the HttpServletResponse using HttpServletResponse.sendError(). This serves the standard Spring Boot exception response, but with the updated status code.
#ExceptionHandler
public void handleEntityNotFoundException(EntityNotFoundException e, HttpServletResponse response) throws IOException {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
{
"timestamp": 1587794161453,
"status": 404,
"error": "Not Found",
"exception": "javax.persistence.EntityNotFoundException",
"message": "No resource exists for the given ID",
"path": "/my-resource/1"
}

Handle Custom Validations and Exceptions in REST with SpringBoot

Sample Project Attached Here
I am following this article API Error Handling (also please suggest if there is a better way to do this) to handle exceptions and validations in a generic way in my spring boot rest service. (i am new to spring and rest, so i am going through different articles for my requirement)
Basic idea about the requirement:
(Need to validate the POST request and send the validation errors to the client in a structure way. There could be multiple validation errors)
Whenever i get a POST request from my client, i need to validate the RequestBody. So I added #Valid on the parameter and #NotNull on the properties which i want to validate. Upon receiving the POST request spring is validating the request and throwing MethodArgumentNotValidException which is fine since i have some mandatory field missing.
I am handling it in a common place with #ControllerAdvice. After hitting the appropriate method handleMethodArgumentNotValid(...), i am constructing my custom error response APICustomError which i following from the above mentioned article.
When i have multiple validation errors, i am able to loop all the errors and adding it to a list and constructing the ResponseEntity with my custom error.
But the returned ResponseEntity does not have my added validation errors.
i understood the article and implemented the same in my project but really didn't get what i am missing.
The below is the output said in the article and what i am expecting is:
{
"apierror":{
"status":"BAD_REQUEST",
"timestamp":"10-07-2019 12:53:24",
"message":"Validation error",
"subErrors":[
{
"object":"person",
"field":"id",
"rejectedValue":null,
"message":"ID cannot be null"
},
{
"object":"person",
"field":"name",
"rejectedValue":null,
"message":"name cannot be null"
}
]
}
}
but below is what i am getting. i don't see the subErrors part at all.
{"message":"Validation Error","debugMessage":null,"detail":null,"httpStatus":"BAD_REQUEST","timestamp":"2019-07-10T17:08:00.52"}
Any help is appreciated.
You need to add getter and setters in APICustomError for properly serialize your object. Also you need public constructor and getter/setters for inner class APIValidationError. I suggest you use Lombok.
After that you will see the errors, something like this...
{
"message": "Validation Error",
"debugMessage": null,
"subErrors": [
{
"object": "personDTO",
"field": "id",
"rejectedValue": null,
"validationErrorMessage": "ID cannot be null."
}
],
"detail": null,
"httpStatus": "BAD_REQUEST",
"timestamp": "2019-07-10T10:25:44.1705441"
}
Try this one
In your controller advice
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status, WebRequest request) {
Response response = new Response();
List<FieldError> errors = ex.getBindingResult().getFieldErrors();
List<SubError> errors = new ArrayList<>();
for (FieldError e : errors) {
SubError error = new SubError():
error.setMessage(String.format(MESSAGE_FORMAT, egetCOde));
errors.add(error);
}
response.setSubError(errors);
return new ResponseEntity(response, HttpStatus.BAD_REQUEST);
}

Spring boot edit json exception with data from server response

My spring boot application works like a middleman. It waits for a request, then formats this request and sends to the server and returns server response to request sender. However when I get response error response from server (For example with status code 400 Bad Request) I want to modify default spring boot JSON exception body by adding error cause which was returned from server in JSON format.
Response from server:
Http status: 400
{
"type": "InvoiceDto",
"currency": "EUR",
"error_code": "NO_AMOUNT"
"error_message": "amount is not set"
"invoice_status": "FAILED",
"payment_id": "20516324",
"order_id": 1209,
}
Spring boot returns exception:
{
"timestamp": 1493211638359,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.web.client.HttpClientErrorException",
"message": "400 Bad Request",
"path": "/sms"
}
I want to edit spring's exception field "message" with server's returned "error_message" value. But it seems that I can't even get Response body because spring boot automatically throws default exception.
My understanding is that you need to provide your own exception mapper. The one used now is the default ErrorController added by autoconfiguration.
The correct way is to define your own ResponseEntityExceptionHandler
you can read about custom Exceptionmappers here

Spring Boot - Error Controller to handle either JSON or HTML

I have a spring boot application.
I have a custom error controller, that is mapped to using ErrorPage mappings. The mappings are largely based on HTTP Status codes, and normally just render a HTML view appropriately.
For example, my mapping:
#Configuration
class ErrorConfiguration implements EmbeddedServletContainerCustomizer {
#Override public void customize( ConfigurableEmbeddedServletContainer container ) {
container.addErrorPages( new ErrorPage( HttpStatus.NOT_FOUND, "/error/404.html" ) )
}
And my error controller:
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFound( HttpServletRequest request ) {
"errors/404"
}
This works fine - If I just enter a random non-existent URL then it renders the 404 page.
Now, I want a section of my site, lets say /api/.. that is dedicated to my JSON api to serve the errors as JSON, so if I enter a random non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to have a #ControllerAdvice that specifically caught a class of custom API exceptions I had defined and returned JSON, and in my standard ErrorController checking the URL and throwing an apprpriate API exception if under that API URL space (but that didn't work, as the ExceptionHandler method could not be invoked because it was a different return type from the original controller method).
Is this something that has been solved?
The problem was my own fault. I was trying to work out why my #ExceptionHandler was not able to catch my exception and return JSON - As I suggested at the end of my question, I thought I was having problems because of conflicting return types - this was incorrect.
The error I was getting trying to have my exception handler return JSON was along the lines of:
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation"
I did some more digging/experimenting to try to narrow down the problem (thinking that the issue was because I was in the Spring error handling flow and in an ErrorController that was causing the problem), however the problem was just because of the content negotiation stuff Spring does.
Because my errorPage mapping in the web.xml was mapping to /error/404.html, Spring was using the suffix to resolve the appropriate view - so it then failed when I tried to return json.
I have been able to resolve the issue by changing my web.xml to /error/404 or by turning off the content negotiation suffix option.
Now, I want a section of my site, lets say /api/.. that is dedicated
to my JSON api to serve the errors as JSON, so if I enter a random
non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to
have a #ControllerAdvice that specifically caught a class of custom
API exceptions I had defined and returned JSON, and in my standard
ErrorController checking the URL and throwing an apprpriate API
exception if under that API URL space (but that didn't work, as the
ExceptionHandler method could not be invoked because it was a
different return type from the original controller method).
I think you need to rethink what you are trying to do here. According to HTTP response codes here
The 404 or Not Found error message is an HTTP standard response code
indicating that the client was able to communicate with a given
server, but the server could not find what was requested.
So when typing a random URL you may not want to throw 404 all the time. If you are trying to handle a bad request you can do something like this
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ErrorResponse> noRequestHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.debug("noRequestHandlerFound: stacktrace={}", ExceptionUtils.getStackTrace(e));
String errorCode = "400 - Bad Request";
String errorMsg = "Requested URL doesn't exist";
return new ResponseEntity<>(new ErrorResponse(errorCode, errorMsg), HttpStatus.BAD_REQUEST);
}
}
Construct ResponseEntity that suites your need.

Categories

Resources