Reputation: 2560
I have a Spring 3.2 MVC REST service, which implements error handling by extending ResponseEntityExceptionHandler
. This handles the standard Spring exceptions, and will respond with the appropriate HTTP status and custom "Error" ResponseEntity object in XML or JSON as requested by the client. This works great for all of the standard exceptions except HttpMediaTypeNotAcceptableException
.
The problem I have with this exception is that the reason its thrown in the first place is because the response media type (application/xml, application/json, etc.) could not be determined from the request. If I try to return an "Error" ResponseEntity object for this exception it will fail because the response media type couldn't be determined (which is why this exception is being handled in the first place)...and basically get another HttpMediaTypeNotAcceptableException
thrown from my ExceptionHandler.
I need to figure out a way when handling HttpMediaTypeNotAcceptableException
to specify a valid response media type, so the ResponseEntity makes it to the client. Since requested media type can't be determined, this would probably just be the default for my service (application/xml)
Any ideas?
Upvotes: 3
Views: 2993
Reputation: 11
I found same problem in my development, to so solve this issue you need specify in the reponsebody of your error message present in your all method annoted @ExceptionHandler
of your controllerAdvicer
the contentType
:
for exemple: your receive header Accept: application/pdf
but your application produce application/json
.
class GlobalExceptionHandler
annoted with @ControllerAdvice
to manage specific body response:
package com.obs.sfu.exception;
import javax.validation.ConstraintViolationException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.ws.soap.client.SoapFaultClientException;
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private static final String SIZE_MUST_BE_FROM_1_TO_15_CHARACTERS = "Size must be from 1 to 15 characters";
private static final int CODE_INTERNAL_SERVER_ERROR = 1;
private static final String MESSAGE_INTERNAL_SERVER_ERROR = "Internal error";
private static final String DESCRIPTION_INTERNAL_SERVER_ERROR = "Generic failure message";
private static final int CODE_SERVICE_UNAVAILABLE = 5;
private static final String MESSAGE_SERVICE_UNAVAILABLE = "The service is temporarily unavailable";
private static final String DESCRIPTION_SERVICE_UNAVAILABLE = "The service can not handle the call.";
private static final int CODE_UNSUPPORTED_MEDIA_TYPE = 68;
private static final String MESSAGE_UNSUPPORTED_MEDIA_TYPE = "Unsupported Media Type";
private static final String DESCRIPTION_UNSUPPORTED_MEDIA_TYPE = "The format of the posted body is not supported by the endpoint.";
private static final int CODE_NOT_FOUND = 60;
private static final String MESSAGE_NOT_FOUND = "Resource not found";
private static final String DESCRIPTION_NOT_FOUND = "The Requested URI or the requested resource does not exist.";
private static final int CODE_METHOD_NOT_ALLOWED = 61;
private static final String MESSAGE_METHOD_NOT_ALLOWED = "Method not allowed";
private static final String DESCRIPTION_METHOD_NOT_ALLOWED = "The URI does not support the requested method. The available methods should be set in the response header 'Allow'";
private static final int CODE_NOT_ACCEPTABLE = 62;
private static final String MESSAGE_NOT_ACCEPTABLE = "Not acceptable";
private static final String DESCRIPTION_NOT_ACCEPTABLE = "The Accept incoming header does not match any available content-type.";
private static final int CODE_CONSTRAINT_VIOLATION_FIELD = 20;
private static final String MESSAGE_INVALID_BODY_FIELD = "Invalid Via field";
private static final String HEADER_ALLOW = "Allow";
private static final String HEADER_ALLOW_VALUE = "GET, OPTIONS";
@Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_NOT_FOUND, DESCRIPTION_NOT_FOUND,
CODE_NOT_FOUND);
return ResponseEntity.status(status).contentType(MediaType.APPLICATION_JSON).body(errorDetails);
}
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_METHOD_NOT_ALLOWED,
DESCRIPTION_METHOD_NOT_ALLOWED, CODE_METHOD_NOT_ALLOWED);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set(HEADER_ALLOW, HEADER_ALLOW_VALUE);
return ResponseEntity.status(status).headers(responseHeaders).contentType(MediaType.APPLICATION_JSON)
.body(errorDetails);
}
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_UNSUPPORTED_MEDIA_TYPE,
DESCRIPTION_UNSUPPORTED_MEDIA_TYPE, CODE_UNSUPPORTED_MEDIA_TYPE);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Accept", MediaType.APPLICATION_JSON_VALUE);
return ResponseEntity.status(status).headers(responseHeaders).contentType(MediaType.APPLICATION_JSON)
.body(errorDetails);
}
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_NOT_ACCEPTABLE,
DESCRIPTION_NOT_ACCEPTABLE, CODE_NOT_ACCEPTABLE);
return ResponseEntity.status(status).contentType(MediaType.APPLICATION_JSON).body(errorDetails);
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_INVALID_BODY_FIELD,
SIZE_MUST_BE_FROM_1_TO_15_CHARACTERS, CODE_CONSTRAINT_VIOLATION_FIELD);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON).body(errorDetails);
}
@ExceptionHandler(SoapFaultClientException.class)
public ResponseEntity<Object> handleSoapFaultClient(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_SERVICE_UNAVAILABLE,
DESCRIPTION_SERVICE_UNAVAILABLE, CODE_SERVICE_UNAVAILABLE);
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).contentType(MediaType.APPLICATION_JSON)
.body(errorDetails);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleGlobalException(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(request.getContextPath(), MESSAGE_INTERNAL_SERVER_ERROR,
DESCRIPTION_INTERNAL_SERVER_ERROR, CODE_INTERNAL_SERVER_ERROR);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_JSON)
.body(errorDetails);
}
}
file application.properties
need these properties to manage NoHandlerFoundException
(throwExceptionIfNoHandlerFound
):
spring:
mvc:
throw-exception-if-no-handler-found: true
static-path-pattern: /swagger* # to available swagger-ui.html
Upvotes: 1
Reputation: 3527
Edit: this answer is pretty useless, see comment below. Sorry.
As in this case the communication is pretty messed up anyway, maybe it is enough to set the status code to 400 - Bad Request
and hope the client does not really expect a machine readable answer in a format that is not known to the server anyway. If a response body is send, maybe use Content-Type: text/plain
- that will be of no use for the client, but maybe the person debugging the situation ;) Well, or whatever fits your mood.
Update: a better fitting solution might be to use the method explained in the manual; at: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-customer-servlet-container-error-page however I have to admit I did not really try if that works.
Upvotes: 0