I have a ConstraintViolationException handler class that looks like this:
#Produces
#Singleton
#Requires(classes = {ConstraintViolationException.class, ExceptionHandler.class})
public class ConstraintsViolationsExceptionHandler
implements ExceptionHandler<ConstraintViolationException, HttpResponse> {
#Override
public HttpResponse
handle(HttpRequest request, ConstraintViolationException exception) {
return HttpResponse
.status(HttpStatus.FORBIDDEN)
.contentType(MediaType.APPLICATION_JSON)
.characterEncoding("UTF-8")
.body(new SignUpPhoneNumberErrorResponse<>(400,
"Wrong data used",
new ArrayList<>(exception.getConstraintViolations())));
}
}
where SignUpPhoneNumberErrorResponse is my error handling POJO which is getting serialized to JSON absolutely fine.
My Controller looks like this:
#Controller(PhoneAuthAndLoginConstants.CONTROLLER_BASE_PATH)
#Validated
public class UserPhoneNumberRegistrationAndLoginController {
#Inject
MongoDbUserSignUpPhoneNumberDAO mongoDbUserSignUpPhoneNumberDAO;
#Post(uri = PhoneAuthAndLoginConstants.CONTROLLER_SIGN_UP_PATH,
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON)
public Single<ResponseDataEncapsulate>
signUpForPhoneVerification(#Valid #Body UserSignUpPhoneNumberEntity phoneNumber) {
return mongoDbUserSignUpPhoneNumberDAO.sendVerification(phoneNumber);
}
#Post(uri = PhoneAuthAndLoginConstants.CONTROLLER_SIGN_UP_PATH
+
PhoneAuthAndLoginConstants.CONTROLLER_SIGN_UP_VERIFICATION_CODE_PARAM,
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON)
public Single<ResponseDataEncapsulate>
sendUserSignUpConfirmation(#Valid #Body UserAccountStateSignUpEntity verificationData,
HttpHeaders httpHeaders) {
return mongoDbUserSignUpPhoneNumberDAO.signUp(verificationData);
}
}
My POJO for UserAccountStateSignUpEntity looks like this:
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class UserAccountStateSignUpEntity implements UserSignUpEntity {
#NotNull #NotBlank #Size(min = 5, max = 13) private String phoneNumber;
#NotNull #NotBlank #Size(min = 7, max = 7) private String verificationCode;
#JsonIgnore private Boolean verifiedAccount = Boolean.FALSE;
public UserAccountStateSignUpEntity(String phoneNumber, String verificationCode) {
this.phoneNumber = phoneNumber;
this.verificationCode = verificationCode;
this.verifiedAccount = Boolean.TRUE;
}
#Override
public Map<String, Object> makePhoneEntityMapForMongo() {
HashMap<String, Object> returnMap = new HashMap<String, Object>() {{
put("phoneNumber", phoneNumber);
put("verificationCode", verificationCode);
put("verifiedAccount", verifiedAccount);
}};
return Collections.unmodifiableMap(returnMap);
}
}
I send in a request payload like this:
{
"phoneNumber" : "91-123456789",
"verificationCode" : "18887"
}
This should trigger a ConstraintViolationException and my handler code should execute and I should get a HTTP Forbidden. But instead I get the default HTTP Bad Request error message.
Why isn't my handler getting executed?
What can be done to make it execute?
I'm using Micronaut 1.1.3 as the web framework and the Hibernate Validator as the javax.validation implementation.
#Error that can be applied to method to map it to an error route and SignUpPhoneNumberErrorResponse would be returned as a error response body when any ConstraintViolationException occured.
For more detail visit Micronaut docs
#Controller("/${path}")
#Validated
public class UserPhoneNumberRegistrationAndLoginController {
#Post
public HttpResponse method(#Valid #Body UserAccountStateSignUpEntity verificationData, HttpHeaders httpHeaders) {
return null;
}
#Error(exception = ConstraintViolationException.class)
public SignUpPhoneNumberErrorResponse onSavedFailed(HttpRequest request, ConstraintViolationException ex) {
return new SignUpPhoneNumberErrorResponse(500,
"Wrong data used",
String.valueOf(ex.getConstraintViolations().stream().map( e -> e.getPropertyPath()+": "+e.getMessage()).collect(Collectors.toList())),
"Application",
"Error",
System.currentTimeMillis());
}
#Error(status = HttpStatus.NOT_FOUND, global = true)
public HttpResponse notFound(HttpRequest request) {
//return custom 404 error body
}
}
I recently had the same problem with error handling in Micronaut. As I figured out, there is not a org.hibernate.exception.ConstraintViolationException thrown, but a javax.persistence.PersistenceException.
For me, then it worked with #Error(exception = PersistenceException::class) (Kotlin).
#Error annotation handling for ConstraintViolationException worked for me only when it is on controller.
In the end managed to handle it like this, by replacing the micronaut ConstraintExceptionHandler bean:
#Produces
#Replaces(io.micronaut.validation.exceptions.ConstraintExceptionHandler.class)
#Requires(classes = {ConstraintViolationException.class, ExceptionHandler.class})
public class ConstraintExceptionHandler extends io.micronaut.validation.exceptions.ConstraintExceptionHandler {
#Override
public HttpResponse handle(HttpRequest request, ConstraintViolationException exception) {
return return HttpResponse
.badRequest();
}
}
I removed the #Requires(classes = {ConstraintViolationException.class, ExceptionHandler.class}) annotation and replaced the ArrayList<> in the body with a comma separated string and it started to work fine. Although I do not know the consequences of removing the annotation.
I believe the error here is that Micronaut already defined an exception handler for ConstraintViolationException as stated here
In order to overwrite this one I'd recommend just copying and pasting the definition from the page I linked.
#Produces
#Singleton
#Primary
#Requires(classes={javax.validation.ConstraintViolationException.class,ExceptionHandler.class})
public class ConstraintExceptionHandler
extends java.lang.Object
implements ExceptionHandler<javax.validation.ConstraintViolationException,HttpResponse<JsonError>>
(You can fill in the body. The extends java.lang.Object is not necessary)
The important part here is that I added the io.micronaut.context.annotation.Primary annotation. This will favour your handler over the other defined one. I stole this trick from ConversionErrorHandler because I was running into the same issue as you but with that handler.
Now of course you can change the HttpResponse<JsonError> to an HttpResponse<*> appropriate for your needs.
Related
I am building and testing a simple Spring Boot API REST tutorial.
I have faced with an issue that I am trying to understand. I am getting an HTTP 406 (NOT ACCEPTABLE) when calling POST method to create and persist a new entity.
Problem is the entity is persisted but the response to the client is not what should be expected (HTTP 201 CREATED with URI in this case).
Tutorial and TutorialDto classes have the exact same attributes. Here is the definition:
public class TutorialDto {
private long id;
private String title;
private String description;
private boolean published;
...
}
Here is my POST method in #RestController class:
#PostMapping("/tutorial")
public ResponseEntity.BodyBuilder createTutorial(#RequestBody final TutorialDto tutorialDto) {
final TutorialDto createdTutorial = tutorialService.add(tutorialDto);
return ResponseEntity.created(URI.create(String.format("tutorial/%d", createdTutorial.getId())));
}
And here is the #Service method to create the entity:
#Transactional
public TutorialDto add(final TutorialDto tutorialDto) {
final Tutorial createdTutorial = tutorialRepository.save(modelmapper.map(tutorialDto, Tutorial.class));
return Optional.of(modelmapper.map(createdTutorial, TutorialDto.class))
.orElseThrow(() -> new TutorialCreationException(
String.format("Tutorial: %s could not be created", tutorialDto.getTitle()))
);
}
This is the request body:
{
"title": "tutorial",
"description": "This is the first created tutorial"
}
And this is the response body:
{
"timestamp": "2022-04-16T00:40:36.626+00:00",
"status": 406,
"error": "Not Acceptable",
"path": "/api/v1/tutorial"
}
I am getting the HTTP 406 response at the end of the controller method, after returning the "ResponseEntity.created".
Thanks in advance.
Looks like you are using wrong usage of ResponseEntity.BodyBuilder. Here is an example
Hence, your controller code should look something like this:
#PostMapping("/tutorial")
public ResponseEntity createTutorial(#RequestBody final TutorialDto tutorialDto) {
final TutorialDto createdTutorial = tutorialService.add(tutorialDto);
return ResponseEntity.created(URI.create(String.format("tutorial/%d", createdTutorial.getId()))).body(createdTutorial);
}
I am writing a test to test the POST method for failure case in the controller.
It returns a 415, I am expecting 500. I have mocked the response using mockito.
ControllerTest
#Test
#DisplayName("POST /customers - Failure")
void createProductShouldFail() throws Exception {
// Setup mocked service
when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}");
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}
Controller
#PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(#RequestBody Customer customer){
try {
// Create the new product
Customer savedCustomer = customerService.save(customer);
// Build a created response
return ResponseEntity
.created(new URI("/customers/" + savedCustomer.getId()))
.body(savedCustomer);
} catch (URISyntaxException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
Error:
HTTP Method = POST
Request URI = /customers
Parameters = {}
Headers = [Accept:"application/json", Content-Length:"15"]
Body = {"name":"John"}
Session Attrs = {}
Handler:
Type = com.prabhakar.customer.controller.CustomerController
Method = com.prabhakar.customer.controller.CustomerController#saveCustomer(Customer)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 415
Error message = null
Headers = [Accept:"application/json, application/*+json"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
org.opentest4j.AssertionFailedError:
Expected :500
Actual :415
But 415-Unsupported Media Type client error response code.
I have used the same payload for this method,it works.
#Test
#DisplayName("POST /customers - Success")
void createProductShouldSucceed() throws Exception {
// Setup mocked service
Customer mockCustomer = new Customer(1L, "John");
when(customerService.save(any(Customer.class))).thenReturn(mockCustomer);
this.mockMvc.perform(post("/customers")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}"))
// Validate the response code and content type
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
//Validate returned json fields
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.name").value("John"));
}
Update I have added
#RestController
#EnableWebMvc
this throws an error as mocked But the code breaks near mockmvc.perform.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError
How can I verify if this is working.
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
There are two thing you must have in account to solve the problem:
First, Instead of use .accept(MediaType.APPLICATION_JSON) you must use .contentType(MediaType.APPLICATION_JSON).
Second, the other thing you must have in account is, if you are not handling the exception (using a controller advice or other way) you must do it, because when you execute the firts step you will receive the following error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError
The workaround that I took was use #ExceptionHandler in the CustomerController to test your code (this isn't the best place to do this, depending what you are doing. Instead use a #ControllerAdvice. You can find some examples here https://www.baeldung.com/exception-handling-for-rest-with-spring).
Below the complete code that are used to recreate your case.
Customer.class
public class Customer {
private Long id;
private String name;
public Customer(Long id, String name) {
this.id = id;
this.name = name;
}
// + getter and setter
}
CustomerController.class
#RestController
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
#PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(#RequestBody Customer customer) {
try {
// Create the new product
Customer savedCustomer = customerService.save(customer);
// Build a created response
return ResponseEntity
.created(new URI("/customers/" + savedCustomer.getId()))
.body(savedCustomer);
} catch (URISyntaxException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
// Code used to avoid the error explained in the second step
#ExceptionHandler
public ResponseEntity<?> handlingInternalServerError(HttpServerErrorException.InternalServerError ex) {
// code to be executed when the exception is thrown (logs, ...)
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CustomerService.class
#Service
public class CustomerService {
public Customer save(Customer customer) {
return customer;
}
}
CustomerControllerTest.class
#SpringBootTest
#AutoConfigureMockMvc
class CustomerControllerTest {
#MockBean
private CustomerService customerService;
#Autowired
private MockMvc mockMvc;
#Test
#DisplayName("POST /customers - Failure")
void saveCustomer() throws Exception {
Customer customerMock = new Customer(1L, "John");
// Setup mocked service
when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
RequestBuilder requestBuilder = post("/customers")
.content("{\"name\":\"John\"}")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}
}
NOTE: This test was executed using Java 8 and JUnit5
Other NOTE based on your comment:
Ok. #prabhakar-maity, my recommendation based in your case is to use a #ExceptionHandler or #ControllerAdvice instead of try...catch. For example, you have 6 methods in your controller or several controllers and want to handle the same exception (Internal Server Error) and return the same info, so you’ll have to implement a try..catch in each method, while using #ControllerAdive (multiple controllers) or #ExceptionHandler (one controller) you implement your logic in one place
Check this question for more info LINK
You can reference Spring MVC Test Framework - Unsupported Media Type
You may be missing #EnableWebMvc annotation in your controller.
EDIT - for Comment:
Instead of
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
.content("{\"name\":\"John\"}");
MockHttpServletResponse response = result.getResponse();
// Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(),
response.getStatus());
Try:
mockMvc.perform(requestBuilder)
.andExpect(status().isInternalServerError());
I´m having some issues when returning some errors from a rest WebService.
Making a request with the header {"Accept":"application/octet-stream"}
(the service returns a document ResponseEntity<InputStreamResource> if all the process goes well).
When all the process goes well the document is downloaded fine, but when an error is occurred and the code jumps to the #ControllerAdvice and tries to return a JSON error. Here comes the problem, when trying to return the JSON springs crashes:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
Here is a example of some code:
Controller
#RequestMapping(value = "/test", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<CustomError> test() throws Exception {
throw new Exception();
}
ControllerAdvice
#ControllerAdvice
public class ExceptionHandlerAdvice {
private static final Logger logger = LogManager.getLogger(ExceptionHandlerAdvice.class);
#ExceptionHandler({Exception.class,Throwable.class})
#ResponseBody
public ResponseEntity<CustomError> handleUnhandledException(Exception exception) {
CustomError error = new CustomError(exception.getMessage());
return new ResponseEntity<CustomError>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
CustomError:
public class CustomError {
private String errorDescription;
public CustomError(String errorDescription) {
super();
this.errorDescription = errorDescription;
}
public String getErrorDescription() {
return errorDescription;
}
public void setErrorDescription(String errorDescription) {
this.errorDescription = errorDescription;
}
}
I´ve also tried returning new headers on #controllerAdvice
#ExceptionHandler({Exception.class,Throwable.class})
#ResponseBody
public ResponseEntity<CustomError> handleUnhandledException(Exception exception) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
CustomError error = new CustomError(exception.getMessage());
return new ResponseEntity<CustomError>(error,headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
Any idea how can I make this work or ignore Accept header on response?
It´s possible?
Thanks in advance
This exception means your response type not match with your request header. If you are expecting JSON/Stream to be returned, your request header should be {"Accept":"application/octet-stream,application/json"}.
I am hosting Spring Boot app on weblogic 10.3.6 for REST api
I'd like to implement these 2 features:
Whenever my custom exception occurs I would like to send a http response with message e.g. 500 - "request couldn't be parsed because of something..."
Whenever any error is thrown I would like to get the stack trace that is usually printed to console (for debugging purposes)
I tried to solve the first part the following way:
#ControllerAdvice
public class ExceptionHandlerAdvice {
#ExceptionHandler(MyException.class)
public ResponseEntity handleException(MyException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
However as a response I only get 500 Internal Server Error with no message
As for the second part I tried simmilar approach but there was no error message either.
How can I solve this?
EDIT:
The best I could achieve was removing ExceptionHandlerAdvice and using annotation on my exception class instead like this:
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason="This is a message I'd like to send")
It works, but I have to add some variables from code to the message and this method does not allow it
EDIT2:
This is a bit weird, perhaps a behavior of weblogic server, but when I set the httpStatus to HttpStatus.ACCEPTED I can see the message, if it is HttpStatus.Forbidden or any other 4xx error I just get the error without message
Create 'ResponseEntity' object with message and status and return it, it will display with error message.
/**
* HTTP_STATUS - 500 -Service Unavailable.
*
* #param exception
* Catches the following: MyException
* #return
*/
#ExceptionHandler({ MyException.class})
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public static ResponseEntity<?> handleConnectionErrorResponse(MyException exception) {
return new ResponseEntity<String>("Some error occurred at server", HttpStatus.INTERNAL_SERVER_ERROR);
}
Do not return something, throw an :
#ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY)
public class UnprocessableEntity extends RuntimeException {
public UnprocessableEntity(String string) {
super(string);
}
}
Or another thing like that.
I also went through the same requirement.
Below is the code which is working for me:
String errMsg = "{\"errorMessage\":\"Parking Slot is not available\"}";
return new ResponseEntity<String>(errMsg, HttpStatus.INTERNAL_SERVER_ERROR);
Whereas errMsg should be written in the format which you want. Like I had requirment for response in JSON.
Hope this will help some of you
Well In my case I have done custom error handling logic.
We can define a custom Base Response class wich accepts generic type(Eg: user desired model)
Return BaseResponse as a response for each REST Methods
(GET, PUT, POST, DELETE, etc)
BaseResponse.java
public class BaseResponse<T> {
int statusCode;
String message;
T data;
public int getStatusCode() {
return statusCode;
}
public BaseResponse<T> getValidResponse(String message, T data) {
BaseResponse<T> baseResponse = new BaseResponse<T>();
baseResponse.statusCode = 200;
baseResponse.message = message;
baseResponse.data = data;
return baseResponse;
}
public BaseResponse<T> getErrorResponse(int StatusCode, String message) {
BaseResponse<T> baseResponse = new BaseResponse<T>();
baseResponse.statusCode = StatusCode;
baseResponse.message = message;
return baseResponse;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
User.java
public class User {
String userName;
String userAddress;
String userEmail;
String userPhoneNumber;
...
//Getters & Setters
}
YourController.java
....
#PostMapping("/addUser")
public BaseResponse<User> addUser(User user) {
if (user.getUserName() != null && !user.getUserName().equals("")) {
UserEntity userEntity = new UserEntity();
userEntity.setName(user.getUserName());
...
userRepository.save(userEntity);
return new BaseResponse<User>().getValidResponse("Successfully Added User", user);
} else {
return new BaseResponse<User>().getErrorResponse(400, "Name field is required");
}
}
...
#DeleteMapping("/deleteUser/{userId}")
//Using ? for uncertain Response.Eg: Some response might have data response and some may not have data response...
public BaseResponse<?> deleteUser(#PathVariable(value = "userId") int userId) {
//After delete operation...we don't require data response.
return new BaseResponse<User>().getValidResponse("Successfully deleted the User", null);
}
This might not be an exact solution for the question asked but will surely help someone else.
In an REST API generated with JHipster, I want to throw some 404 exceptions. It is normally done with
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
which actualy results in a 404 response to the xhr request. The problem is that in the front side, JHipster parses the response with
angular.fromJson(result)
and such result is empty when the 404 is the actual response, which makes the parse to fail.
If I point to an unmapped URI, lets say /api/user while my controller maps to /api/users (note the plural) the 404 I got from the API has a body in it:
{
"timestamp": "2016-04-25T18:33:19.947+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/user/myuser/contact"
}
which is correctly parse in angular.
How can I create a body like this? Is this exception thrown by spring or is tomcat who throws it?
I tried this: Trigger 404 in Spring-MVC controller? but I cant set the parameters of the response.
Basic Idea
First option is to define error objects and return them as 404 Not Found body. Something like following:
Map<String, String> errors = ....;
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errors);
Instead of returning a typical ResponseEntity, you can throw an Exception that will be resolved to a 404 Not Found. Suppose you have a NotFoundException like:
#ResponseStatus(code = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {}
Then if you throw this exception in your controllers, you would see something like:
{
"timestamp":1461621047967,
"status":404,
"error":"Not Found",
"exception":"NotFoundException",
"message":"No message available",
"path":"/greet"
}
If you want to customize the message and other parts of body, you should define a ExceptionHandler for NotFoundException.
Introducing Exception Hierarchies
If you're creating a RESTful API and want to have different Error Codes and Error Messages for different exceptional cases, you can create a hierarchy of exceptions representing those cases and extract message and code from each one.
For example, you can introduce an exception, say, APIException which is super-class of all other exceptions thrown by your controllers. This class defines a code/message pair like:
public class APIException extends RuntimeException {
private final int code;
private final String message;
APIException(int code, String message) {
this.code = code;
this.message = message;
}
public int code() {
return code;
}
public String message() {
return message;
}
}
Each subclass depending on the nature of its exception can provide some sensible values for this pair. For example, we could have an InvalidStateException:
#ResponseStatus(code = HttpStatus.BAD_REQUEST)
public class InvalidStateException extends APIException {
public InvalidStateException() {
super(1, "Application is in invalid state");
}
}
Or that notorious not found ones:
#ResponseStatus(code = HttpStatus.NOT_FOUND)
public class SomethingNotFoundException extends APIException {
public SomethingNotFoundException() {
super(2, "Couldn't find something!");
}
}
Then we should define an ErrorController that catches those exceptions and turn them to meaningful JSON representations. That error controller may look like following:
#RestController
public class APIExceptionHandler extends AbstractErrorController {
private static final String ERROR_PATH = "/error";
private final ErrorAttributes errorAttributes;
#Autowired
public APIExceptionHandler(ErrorAttributes errorAttributes) {
super(errorAttributes);
this.errorAttributes = errorAttributes;
}
#RequestMapping(path = ERROR_PATH)
public ResponseEntity<?> handleError(HttpServletRequest request) {
HttpStatus status = getStatus(request);
Map<String, Object> errors = getErrorAttributes(request, false);
getApiException(request).ifPresent(apiError -> {
errors.put("message" , apiError.message());
errors.put("code", apiError.code());
});
// If you don't want to expose exception!
errors.remove("exception");
return ResponseEntity.status(status).body(errors);
}
#Override
public String getErrorPath() {
return ERROR_PATH;
}
private Optional<APIException> getApiException(HttpServletRequest request) {
RequestAttributes attributes = new ServletRequestAttributes(request);
Throwable throwable = errorAttributes.getError(attributes);
if (throwable instanceof APIException) {
APIException exception = (APIException) throwable;
return Optional.of(exception);
}
return Optional.empty();
}
}
So, if you throw an SomethingNotFoundException, the returned JSON would be like:
{
"timestamp":1461621047967,
"status":404,
"error":"Not Found",
"message":"Couldn't find something!",
"code": 2,
"path":"/greet"
}
I guess you can do this if you want to return some message or test with your error code
#ResponseBody
public ResponseEntity somthing() {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
return new ResponseEntity<>(new Gson().toJson("hello this is my message"), headers, HttpStatus.NOT_FOUND);
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "message");