401 and 404 error when validation error happen - spring

today I faced an error, I did solve it, but I am having trouble understanding why the error happens that way.
so I was just experimenting with Spring Boot and building a sample application with #RestController, I used "spring-boot-starter-validation" as my user input validation.
my restcontroller endpoint method is like this
#PostMapping("/newEmployee")
#ApiOperation(value = "creates a new employee",
response =EmployeeDTO.class)
public EmployeeDTO newEmployee(#Valid #RequestBody EmployeeDTO newEmployee) {
employeeValidator.validateEmployeeDTO(newEmployee);
return employeeManagementBO.saveOrUpdateEmployee(newEmployee);
}
then I defined a handler method like below
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
//note that if #ResponseBody is not annotated here, you may get weird error like 404 or 401
#ResponseBody
public Map<String, String> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
System.out.println("error caught...");
return errors;
}
so if I remove the #ResponseBody, when I test the endpoint with PostMan, I get error code 404 (but I did debug and saw that the handler method ran), which means that after errors were returned I get 404 error.
however, if I annotate it with #ResponseBody, then everything work as expected and I get a JSON response body back with the expected error structure in a map.
so I can make sense of the #ResponseBody making my return value as the Response of my endpoint, but my question is how does the 404 error happen? also, if I make the return type a String, then I get a 405 error (saying 'POST' is not allowed).
does anybody knows without the #ResponseBody, what is happening? and is there any usecase for not annotating it with #ResponseBody?

Related

Spring RestController returning ResponseEntity<byte[]> in case of errors

I have a rest controller in a spring boot service as follows:
Public ResponseEntity getDocContent(String id)
THis controller action produces MediaType.Octet_Stream
I am wondering what to return in case of non Http OK response when I really don’t have a byte array content but a String with error message. I would not want to produce an octet stream in error cases but an error JSon instead
I can have The service produce both octet stream and application/json but my confusion is around the return type of byte array in case of errors in which case I want to generate a Json and not a byte array
Please give me ideas on how to solve this
Add a controllerAdvice to handle the thrown exceptions in the RestContoller.
#ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(MyException.class)
#ResponseStatus(HttpStatus.CONFLICT) //Just an example code
public ResponseEntity<GeneralMessage> handleGacaException(MyException ex) {
LOGGER.error(ex.getMessage());
GeneralMessage errorMessage = new GeneralMessage(ex.getErrorCode().toString(), ex.getErrorMessage());
return ResponseEntity.status(HttpStatus.CONFLICT).body(errorMessage);
}
}
Than throw MyException on the RestController.

Spring REST Controller Unsupported Media Type or No Handler

If I have a spring REST controller like this
#PostMapping(
value = "/configurations",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public CreateConfigurationResponse createConfiguration(
#RequestBody #Valid #NotNull final CreateConfigurationRequest request) {
// do stuff
}
and a client calls this endpoint with the wrong media type in the Accept header then spring throws a HttpMediaTypeNotAcceptableException. Then our exception handler catches that and constructs a Problem (rfc-7807) error response
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class HttpMediaTypeExceptionHandler extends BaseExceptionHandler {
#ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseEntity<Problem> notAcceptableMediaTypeHandler(final HttpMediaTypeNotAcceptableException ex,
final HttpServletRequest request) {
final Problem problem = Problem.builder()
.withType(URI.create("...."))
.withTitle("unsupported media type")
.withStatus(Status.NOT_ACCEPTABLE)
.withDetail("...error stuff..")
.build();
return new ResponseEntity<>(problem, httpStatus);
}
But since the Problem error response should be sent back with a media type application/problem+json spring then sees that as not acceptable media type and calls the HttpMediaTypeExceptionHandler exception handler again and says that media type is not acceptable.
Is there a way in Spring to stop this second loop into the exception handler and even though the accept header didn't include the application/problem+json media type it will just return that anyway?
So strangely it started working when I changed the return statement from this:
return new ResponseEntity<>(problem, httpStatus);
to this:
return ResponseEntity
.status(httpStatus)
.contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body(problem);
I'm not sure how this makes it work but it does.

Zuul proxy server throwing Internal Server Error when request takes more time to process

Zuul Proxy Error
I am getting this error when the request takes more time to process in the service.But Zuul returns response of Internal Server Error
Using zuul 2.0.0.RC2 release
As far as I understand, in case of a service not responding, a missing route, etc. you can setup the /error endpoint to deliver a custom response to the user.
For example:
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping(value = "/error", produces = "application/json")
public #ResponseBody
ResponseEntity error(HttpServletRequest request) {
// consider putting these in a try catch
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
Throwable exception = (Throwable)request.getAttribute("javax.servlet.error.exception");
// maybe add some error logging here, e.g. original status code, exception, traceid, etc.
// consider a better error to the user here
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{'message':'some error happened', 'trace_id':'some-trace-id-here'}");
}
#Override
public String getErrorPath() {
return "/error";
}
}

Spring MVC - REST Api, keep getting 400 Bad Request when trying to POST

i have a REST api service which should receive POST calls.
I'm using POSTMAN to test them, but i keep getting a 400 Bad Request Error, with no body, maybe i'm building bad my controller...
This is the controller
#PostMapping("/object/delete")
public ResponseEntity<?> deleteObject(#RequestBody long objectId) {
logger.debug("controller hit");
Object o = service.findByObjectId(objectId);
if(o!=null){
service.deleteObject(object);
return new ResponseEntity<>(HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Using #RequestBody i should send the request in JSON, in this way:
{
"objectId":100
}
But i get a 400 error, and the strange think is that my logger logger.debug("controller hit"); it's not printed in logs...
Sending { "objectId":100 } would result in receiving an object X with an objectId attribute in your java method.
If you just need to send an id, you can use #PathVariable
#PostMapping("/object/{id}/delete")
public ResponseEntity<?> deleteObject(#PathVariable("id") long objectId) {
Also, consider using DeleteMapping instead of PostMapping to delete an object.

How to handle Exception occuring when returning StreamingResponseBody from RestController

I have implemented a Spring Rest Controller that streams back large files using the StreamingResponseBody. However, these files are coming from another system and there is the potential for something to go wrong while streaming them back. When this occurs I am throwing a custom Exception (MyException). I am handling the exception in an #ExceptionHandler implementation which is below. I am attempting to set the response httpstatus and error message but I am always receiving http status 406. What is the proper way to handle errors/exceptions while returning a StreamingResponseBody?
#ExceptionHandler(MyException.class)
public void handleParsException( MyException exception, HttpServletResponse response) throws IOException
{
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),exception.getMessage());
}
You should handle all errors in the same way. There are many options.
I prefer next:
Controller Advice
It is a good idea to have an entity to send a generic error response, an example:
public class Error {
private String code;
private int status;
private String message;
// Getters and Setters
}
Otherwise, to handle exceptions you should create a class annotated with #ControllerAdvice and then create methods annotated with #ExceptionHandler and the exception or exceptions (it could be more than one) you want to handle. Finally return ResponseEntity<Error> with the status code you want.
public class Hanlder{
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleResourceNotFoundException(MyException
myException, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.CONFLICT.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.CONFLICT);
}
#ExceptionHandler({DataAccessException.class, , OtherException.class})
public ResponseEntity<?> handleResourceNotFoundException(Exception
exception, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.INTERNAL_ERROR.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.INTERNAL_ERROR);
}
}
Other ways:
Annotate exception directly
Other way is annotating directly the excetion with the status and the reason to return:
#ResponseStatus(value=HttpStatus.CONFLICT, reason="Error with StreamingResponseBody")
public class MyError extends RuntimeException {
// Impl ...
}
Exception Handler in a specific controller
Use a method annotated with #ExceptionHandler in a method of a #Controller to handle #RequestMapping exceptions:
#ResponseStatus(value=HttpStatus.CONFLICT,
reason="Error with StreamingResponse Body")
#ExceptionHandler(MyError.class)
public void entitiyExists() {
}
I figured the problem out. The client was only accepting the file type as an acceptable response. Therefore, when returning an error in the form of an html page I was getting httpstatus 406. I just needed to tell the client to accept html as well to display the message.

Resources