I have a Spring RequestMapping that is taking a JSON structure and converting it to a POJO. If I send the route a malformed JSON structure, I get back a "400 Bad Request" but what I'd like to receive is an error message stating why it was a bad request. I have a Validator also checking the JSON structure, but it seems that it's not called if the structure is unable to be converted. Is there a way in which I can access the built in HttpMessageConverter errors or exceptions?
I think you can use a ResponseEntityExceptionHandler for this, by overriding the handleHttpMessageNotReadable method.
From: 17.11.3 Handling Standard Spring MVC Exceptions:
If you prefer to write error content via #ExceptionHandler methods you can extend ResponseEntityExceptionHandler instead. This is a convenient base for #ControllerAdvice classes providing an #ExceptionHandler method to handle standard Spring MVC exceptions and return ResponseEntity. That allows you to customize the response and write error content with message converters. See the ResponseEntityExceptionHandler javadocs for more details.
An example usage of the ResponseEntityExceptionHandler can be found here: http://www.jayway.com/2013/02/03/improve-your-spring-rest-api-part-iii/
Otherwise you can find more spring mvc exception handling approaches here:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
If your needs aren't too complex you might just want to use a SimpleMappingExceptionResolver
ResponseEntityExceptionHandler is used to identify the Spring MVC specific exceptions & to implement a common error response handling strategy for them.
It consists of many methods for example:
handleMissingServletRequestParameter > handle if invalid request params
handleHttpMessageNotReadable > handles if invalid request body
handleExceptionInternal > A single place to customize the response body of all exception types.
The below mentioned code overrides handleExceptionInternal method
#ControllerAdvice
public class JavaWebExeptionHandler extends ResponseEntityExceptionHandler {
public class ExpnDetails {
public int expnCode;
public String expnMessage;
}
// A single place to customize the response body of all exception types.
#Override
protected ResponseEntity<ExpnDetails> handleExceptionInternal(Exception ex, Object body,
HttpHeaders headers,HttpStatus status, WebRequest request) {
return new ResponseEntity<Object>(new ExpnDetails(status.value(), ex.getMessage()), status);
}
}
So all the Spring MVC specific exception are handled at one place and ExpnDetails object is sent as response body which consists of a expnCode & expnMessage.
Output:
{
"expnCode": 415,
"expnMessage": "Content type 'application/json1' not supported"
}
{
"expnCode": 400,
"expnMessage": "JSON parse error: Unexpected character ('"
' (code 34)): was expecting comma to separate Object entries; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('
"' (code 34)): was expecting comma to separate Object entries at [Source: (PushbackInputStream); line: 3, column: 4]"
}
Note this solution also helps in hiding container(ex. tomcat server) details to appear in response error when request fails.
Related
In the Spring Framework validating the request respond with error 400 (Bad Request), specially when validating the request body and the request fields decorated with the
javax.validation.constraints.* annotations (which specified in JSR 303).
For make it more clear lets go through the example:
I have decorated the fields of the class ContactDetails with #Email and #NotEmpty constraints
class ContactDetails {
#Email
String email;
#NotEmpty
String message;
}
In the controller I used #Valid annotation to make Spring Validator validate the http request body.
#RestController
class NotificationController {
#PostMapping("/sendNotification")
public String sendNotification(#Valid #RequestBody ContactDetails contactDetails) {
...
}
}
If the validation fails, it will trigger a MethodArgumentNotValidException. By default, Spring will translate this exception to a HTTP status 400 (Bad Request).
But for validating the request params or path variables based on Spring documentations I will decorate the controller class with #Validated and just using javax.validation.constraints.* annotations on the parameters and I expect the same result same as validating the request body.
#Validated
#RestController
class NotificationController {
#GetMapping("/getContactDetailsByEmail/{email}")
public ContactDetails findContactDetails(#Email String email) {
...
}
}
In contrast to request body validation a failed validation will trigger a ConstraintViolationException instead of a MethodArgumentNotValidException. Spring does not register a default exception handler for this exception, so it will by default cause a response with HTTP status 500 (Internal Server Error).
I expected to get error 400 for this scenario and I do not know if I missed any thing in my code?
That would be great if any body can help me with this scenario why Spring has different approaches for validating the parameters.
You can create the answer you want by using the fields in the ConstraintViolationException with the following method;
#ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handlePathVariableError(final ConstraintViolationException exception) {
log.error(exception.getMessage(), exception);
final List<SisSubError> subErrors = new ArrayList<>();
exception.getConstraintViolations().forEach(constraintViolation -> subErrors.add(generateSubError(constraintViolation)));
final SisError error = generateErrorWithSubErrors(VALIDATION_ERROR, HttpStatus.BAD_REQUEST, subErrors);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
In the first case - with argument annotated with #RequestBody - Spring uses RequestResponseBodyMethodProcessor to validate it and throws MethodArgumentNotValidException if validation fails, which is handled later by ResponseEntityExceptionHandler or DefaultHandlerExceptionResolver by translating it into 400 BAD REQUEST http response code.
In the second case Spring is using AOP for method validation with MethodValidationInterceptor class, which throws ConstraintViolationException if validation fails.
But, unlike the first case, Spring doesn't provide a default exception handler for ConstraintViolationException, so it's translated into 500 http response code.
That's why developers should consider creating their own exception handlers for this kind of method-level validation.
I'm running a GraphQL API using GraphQL-SPQR and Spring Boot.
At the moment, I am throwing RuntimeExceptions to return GraphQL errors. I have a customExceptionHandler that implements DataFetcherExceptionHandler that returns errors in the correct format, as shown below:
class CustomExceptionHandler : DataFetcherExceptionHandler {
override fun onException(handlerParameters: DataFetcherExceptionHandlerParameters?): DataFetcherExceptionHandlerResult {
// get exception
var exception = handlerParameters?.exception
val locations = listOf(handlerParameters?.sourceLocation)
val path = listOf(handlerParameters?.path?.segmentName)
// create a GraphQLError from your exception
if (exception !is GraphQLError) {
exception = CustomGraphQLError(exception?.localizedMessage, locations, path)
}
// cast to GraphQLError
exception as CustomGraphQLError
exception.locations = locations
exception.path = path
val errors = listOf<GraphQLError>(exception)
return DataFetcherExceptionHandlerResult.Builder().errors(errors).build()
}
}
I use the CustomExceptionHandler as follows (in my main application class):
#Bean
fun graphQL(schema: GraphQLSchema): GraphQL {
return GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy(CustomExceptionHandler()))
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(CustomExceptionHandler()))
.build()
}
I'd like to set a header variable for a UUID that corresponds to the exception, for logging purposes. How would I do that?
Even better, is it possible to create a Spring Bean that puts the UUID in the header for all queries and mutations?
Thanks!
when you're using spring boot, there's two options:
you're using the spring boot graphql spqr starter (which brings it's own controller to handle all graphQL requests)
you're using plain graphql-spqr and have your own controller to handle GraphQL requests
In any case, you've got a few options:
Making your CustomExceptionHandler a Spring Bean and Autowiring HttpServletResponse
That would probably be the easiest way to go - and it would probably work in any case: You could simply make your CustomExceptionHandler a Spring bean and have it autowire the HttpServletRequest - in the handler method, you could then set it to whatever you would like it to be. Here's some dummy code in Java (sorry, I am not proficient enough in Kotlin):
#Component
class CustomExceptionHandler implements DataFetcherExceptionHandler {
private final HttpServletResponse response;
public CustomExceptionHandler(HttpServletResponse response) {
this.response = response;
}
#Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
response.setHeader("X-Request-ID", UUID.randomUUID().toString());
// ... your actual error handling code
}
}
This is going to work because spring will realise that HttpServletRequest differs for each request. It will therefore inject a dynamic proxy into your error handler that will point to the actual HttpServletResponse instance for every request.
I would argue, that it's not the most elegant way, but it will certainly solve your problem.
for the graphql-spqr spring boot starter
There's a default controller implementation that is used in projects using this starter. That controller will handle every graphql request that you receive. You can customise it, by implementing your own GraphQLExecutor and making it a spring bean. That executor is responsible to call the GraphQL engine, pass the parameters in and output the response. Here's the default implementation, that you might want to base your work on.
Similarly to the previous solution, you could autowire the HttpServletResponse in that class and set a HTTP Response header.
That solution would allow you to decide, if you want to set a request id in all cases, or just in specific error cases. (graphql.execute returns an object from which you can get the information if and what errors existed)
when using graphql-spqr without the spring boot starter
Locate your GraphQL controller, add an argument to that method of type HttpServletRequest - and then add headers to that as you prefer (see previous section on some more specific suggestions)
I am building a File Controller that utilizes zero-copy NIO functionality using Spring Boot 2.0 Spring Webflux. I also want to send custom response headers (for response caching, etc)
The code looks like below:
#RestController
#RequestMapping("/file")
public class FileController {
#GetMapping(value = "/{fileName:.+}")
public Mono<ServerResponse> getFile(#PathVariable("fileName") String fileName) {
Resource body = new ClassPathResource(fileName);
return ServerResponse.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, String.format("inline; filename=\"%s\"", fileName))
.contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromResource(body));
}
}
But when I execute the request from browser, I get the below error ins server logs:
2018-07-23 12:07:53.928 ERROR 9772 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter : Unhandled failure: Type definition error: [simple type, class org.springframework.web.reactive.function.server.DefaultServerResponseBuilder$BodyInserterResponse]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.web.reactive.function.server.DefaultServerResponseBuilder$BodyInserterResponse and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS), response already set (status=null)
2018-07-23 12:07:53.928 WARN 9772 --- [ctor-http-nio-3] o.s.h.s.r.ReactorHttpHandlerAdapter : Handling completed with error: Type definition error: [simple type, class org.springframework.web.reactive.function.server.DefaultServerResponseBuilder$BodyInserterResponse]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.web.reactive.function.server.DefaultServerResponseBuilder$BodyInserterResponse and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
I don't know whats going wrong here. If I return Mono<Resource> from the controller method, then everything works fine but then I loose flexibility of providing customer response headers.
Any help on this is appreciated.
ServerResponse should be used with Spring WebFlux functional only - for this use case with the annotation model, ResponseEntity is the proper choice.
Because the WebFlux annotation model doesn't recognize the ServerResponse type, it is assuming it should serialize it (here, with Jackson).
I have a controller with method parameter as model say
public Response create(Customer customer){
}
Customer model :customer model looks like
#JsonTypeInfo( use = JsonTypeInfo.Id.NAME,property = "type")
#JsonSubTypes({#Type(value = Config.class, name = "IPC")})
public class Customer(){
private String type; }
From swagger UI if i send type as IPC its works fine, but any other value than IPC throws an 400 exception while binding.How can i catch this exception inside controller
try to use the #ExceptionHandler annotation
The documentation of Spring 4 (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-exceptionhandler) states that
The HandlerExceptionResolver interface and the
SimpleMappingExceptionResolver implementations allow you to map
Exceptions to specific views declaratively along with some optional
Java logic before forwarding to those views. However, in some cases,
especially when relying on #ResponseBody methods rather than on view
resolution, it may be more convenient to directly set the status of
the response and optionally write error content to the body of the
response.
You can do that with #ExceptionHandler methods. When declared within a
controller such methods apply to exceptions raised by #RequestMapping
methods of that controller (or any of its sub-classes). You can also
declare an #ExceptionHandler method within an #ControllerAdvice class
in which case it handles exceptions from #RequestMapping methods from
many controllers. Below is an example of a controller-local
#ExceptionHandler method:
So, in your controller you can have a method like this
#ExceptionHandler(MethodArgumentNotValidException.class)
public String handleArgumentNotValid(MethodArgumentNotValidException e, ModelMap map, HttpServletRequest request) {
List<ObjectError> errors = e.getBindingResult() .getAllErrors();
//you can get the exception e,
//you can get the request
//handle whatever you want the then return to view
return "your view that you will handle the exception";
}
I would like to implement a generic controller with one or two methods that react to any GET request. I am trying to simplify this to the point where I can return byte (image etc.) or character based (XML, CSS) without having to map each content type and put a RequestMapping in for each.
The app must be abel to handle any request with any content type.
My dispatcher is currently set to handle all requests via /.
The couple of attempts I have made so far throw ambigious handler errors, or the mapping doesn;t work to the point where text is sent back as byte[] or the other way around.
Has anyone made anything like this work ?
Regards,
Andy
You can have a controller like so
#Controller
public class YourController {
#RequestMapping("/*")
public String doLogic(HttpServletRequest request, HttpServletResponse response) throws Exception {
OutputStream out = response.getOutputStream();
out.write(/* some bytes, eg. from an image*/); // write the response yourself
return null; // this is telling spring that this method handled the response itself
}
}
The controller is mapped to every url and every http method. Spring has a set of accepted return types for its handler methods. With String, if you return null, Spring assumes you've handled the response yourself.
As #NilsH commented, you might be better off using a simple servlet for this.