I have this InstantDesrializer
#Slf4j
public class InstantDeserializer extends StdDeserializer<Instant> {
public InstantDeserializer() {
this(null);
}
public InstantDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Instant deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
log.info(node.asText());
TemporalAccessor parse = null;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT).withZone(ZoneOffset.UTC);
try {
parse = dateTimeFormatter.parse(node.asText());
} catch (Exception e) {
e.printStackTrace();
throw new IOException();
}
log.info(Instant.from(parse).toString());
return Instant.from(parse);
}
}
And then corresponding IOException in #ControllerAdvice
#ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException e) {
return ResponseEntity.status(422).build();
}
And this in my DTO:
#NotNull
#JsonDeserialize(using = InstantDeserializer.class)
// #DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private Instant timestamp;
Even when uncommented #DateTimeFormat, it's not working
Ideally, it should return 422 status. But, it returns 400.
Maybe I'm just missing something so small, which I'm not able to figure out.
This approach was suggested here:
Throw custom exception while deserializing the Date field using jackson in java
Your Controller method is never called because JSON body parsing threw an exception.
Your #ContollerAdvice is not applied because the Controller method is not called.
Your handleIOException method is not called, and your 422 status is not applied.
I suspect this is the situation in more detail...
An HTTP request includes a json body.
The controller method matching the #RequestMapping and other annotations of the request takes an instance of your DTO Class as a parameter.
Spring attempts to deserialize the incoming json body before calling your controlling method. It must do this in order to pass the DTO object.
Deserialization uses your custom deserializer which throws an IOException.
This IOException occurs before your controller method is called. In fact, your controller method is never called for this request.
Spring handles the exception using its default behavior, returning an HTTP 400. Spring has a very broad RFC 7231 conception of HTTP 400.
Since your controller method is never called, the #ControllerAdvice is never applied and your #ExceptionHandler does not see the exception. Status is not set to 422.
Why do I believe this?
I frequently see this kind of behavior from Spring, and I think it is the expected behavior. But I have not tracked down the documentation or read the source to be sure.
What can you do about it?
A simple approach which you may not like is to declare your controller method to take inputs that almost never fail, such as String.
You take over the responsibility for validating and deserializing inputs, and you decide what status and messages to return.
You call Jackson to deserialize. Your #ExceptionHandler methods are used.
Bonus: You can return the text of Jackson's often useful parse error messages. Those can help clients figure out why their json is rejected.
I would not be surprised if Spring offers a more stylish approach, a class to be subclassed, a special annotation. I have not pursued that.
What should you do about it?
400 vs. 422 is a case I prefer not to litigate. Depending on your priorities it might be best to accept Spring's convention.
RFC 7231 On Status 400
The 400 (Bad Request) status code indicates that the server cannot or
will not process the request due to something that is perceived to be
a client error (e.g., malformed request syntax, invalid request
message framing, or deceptive request routing).
If the HTTP status code police pick on you, you could point to this and say "I perceive this input to be a client error." Then argue that 422 is inappropriate unless you are serving WebDAV just to keep them off balance.
You do not need an handleIOException method, just add #ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
to your CustomException.
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public class MyException extends JsonProcessingException {
public MyException(String message) {
super(message);
}
}
So when you make invalid request
with body
{"timestamp":"2018-04-2311:32:22","id":"132"}
Response will be:
{
"timestamp": 1552990867074,
"status": 422,
"error": "Unprocessable Entity",
"message": "JSON parse error: Instant field deserialization failed; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Instant field deserialization failed (through reference chain: TestDto[\"timestamp\"])"
}
With valid request works fine:
{"timestamp":"2018-04-23T11:32:22.213Z","id":"132"}
Response:
{
"id": "132",
"timestamp": {
"nano": 213000000,
"epochSecond": 1514700142
}
}
The exception thrown by Jackson in case of a deserialization error is HttpMessageNotReadableException.
In your custom deserializer, you can throw your own deserialization exception which extends JsonProcessingException.
In your ControllerAdvice you can handle the HttpMessageNotReadableException and get the cause of this which is your custom exception.
This way, you can throw the http code you want.
#ExceptionHandler({HttpMessageNotReadableException.class})
#ResponseBody
public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
Throwable cause = ex.getCause();
if (cause.getCause() instanceof YourOwnException) {
//Return your response entity with your custom HTTP code
}
//Default exception handling
}
I'm writing a REST web app (NetBeans 6.9, JAX-RS, TopLink Essentials) and trying to return JSON and HTTP status code. I have code ready and working that returns JSON when the HTTP GET method is called from the client. Essentially:
#Path("get/id")
#GET
#Produces("application/json")
public M_機械 getMachineToUpdate(#PathParam("id") String id) {
// some code to return JSON ...
return myJson;
}
But I also want to return an HTTP status code (500, 200, 204, etc.) along with the JSON data.
I tried to use HttpServletResponse:
response.sendError("error message", 500);
But this made the browser think it's a "real" 500 so the output web page was a regular HTTP 500 error page.
I want to return an HTTP status code so that my client-side JavaScript can handle some logic depending on it (to e.g. display the error code and message on an HTML page). Is this possible or should HTTP status codes not be used for such thing?
Here's an example:
#GET
#Path("retrieve/{uuid}")
public Response retrieveSomething(#PathParam("uuid") String uuid) {
if(uuid == null || uuid.trim().length() == 0) {
return Response.serverError().entity("UUID cannot be blank").build();
}
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Entity not found for UUID: " + uuid).build();
}
String json = //convert entity to json
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}
Take a look at the Response class.
Note that you should always specify a content type, especially if you are passing multiple content types, but if every message will be represented as JSON, you can just annotate the method with #Produces("application/json")
There are several use cases for setting HTTP status codes in a REST web service, and at least one was not sufficiently documented in the existing answers (i.e. when you are using auto-magical JSON/XML serialization using JAXB, and you want to return an object to be serialized, but also a status code different than the default 200).
So let me try and enumerate the different use cases and the solutions for each one:
1. Error code (500, 404,...)
The most common use case when you want to return a status code different than 200 OK is when an error occurs.
For example:
an entity is requested but it doesn't exist (404)
the request is semantically incorrect (400)
the user is not authorized (401)
there is a problem with the database connection (500)
etc..
a) Throw an exception
In that case, I think that the cleanest way to handle the problem is to throw an exception. This exception will be handled by an ExceptionMapper, that will translate the exception into a response with the appropriate error code.
You can use the default ExceptionMapper that comes pre-configured with Jersey (and I guess it's the same with other implementations) and throw any of the existing sub-classes of javax.ws.rs.WebApplicationException. These are pre-defined exception types that are pre-mapped to different error codes, for example:
BadRequestException (400)
InternalServerErrorException (500)
NotFoundException (404)
Etc. You can find the list here: API
Alternatively, you can define your own custom exceptions and ExceptionMapper classes, and add these mappers to Jersey by the mean of the #Provider annotation (source of this example):
public class MyApplicationException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public MyApplicationException() {
super();
}
public MyApplicationException(String msg) {
super(msg);
}
public MyApplicationException(String msg, Exception e) {
super(msg, e);
}
}
Provider :
#Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
#Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Note: you can also write ExceptionMappers for existing exception types that you use.
b) Use the Response builder
Another way to set a status code is to use a Response builder to build a response with the intended code.
In that case, your method's return type must be javax.ws.rs.core.Response. This is described in various other responses such as hisdrewness' accepted answer and looks like this :
#GET
#Path("myresource({id}")
public Response retrieveSomething(#PathParam("id") String id) {
...
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
}
...
}
2. Success, but not 200
Another case when you want to set the return status is when the operation was successful, but you want to return a success code different than 200, along with the content that you return in the body.
A frequent use case is when you create a new entity (POST request) and want to return info about this new entity or maybe the entity itself, together with a 201 Created status code.
One approach is to use the response object just like described above and set the body of the request yourself. However, by doing this you loose the ability to use the automatic serialization to XML or JSON provided by JAXB.
This is the original method returning an entity object that will be serialized to JSON by JAXB:
#Path("/")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
This will return a JSON representation of the newly created user, but the return status will be 200, not 201.
Now the problem is if I want to use the Response builder to set the return code, I have to return a Response object in my method. How do I still return the User object to be serialized?
a) Set the code on the servlet response
One approach to solve this is to obtain a servlet request object and set the response code manually ourselves, like demonstrated in Garett Wilson's answer :
#Path("/")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, #Context final HttpServletResponse response){
User newUser = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
The method still returns an entity object and the status code will be 201.
Note that to make it work, I had to flush the response. This is an unpleasant resurgence of low-level Servlet API code in our nice JAX_RS resource, and much worse, it causes the headers to be unmodifiable after this because they were already sent on the wire.
b) Use the response object with the entity
The best solution, in that case, is to use the Response object and set the entity to be serialized on this response object. It would be nice to make the Response object generic to indicate the type of the payload entity in that case, but is not the currently the case.
#Path("/")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){
User newUser = ...
return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}
In that case, we use the created method of the Response builder class in order to set the status code to 201. We pass the entity object (user) to the response via the entity() method.
The result is that the HTTP code is 401 as we wanted, and the body of the response is the exact same JSON as we had before when we just returned the User object. It also adds a location header.
The Response class has a number of builder method for different statuses (stati ?) such as :
Response.accepted()
Response.ok()
Response.noContent()
Response.notAcceptable()
NB: the hateoas object is a helper class that I developed to help generate resources URIs. You will need to come up with your own mechanism here ;)
That's about it.
I hope this lengthy response helps somebody :)
The answer by hisdrewness will work, but it modifies the whole approach to letting a provider such as Jackson+JAXB automatically convert your returned object to some output format such as JSON. Inspired by an Apache CXF post (which uses a CXF-specific class) I've found one way to set the response code that should work in any JAX-RS implementation: inject an HttpServletResponse context and manually set the response code. For example, here is how to set the response code to CREATED when appropriate.
#Path("/foos/{fooId}")
#PUT
#Consumes("application/json")
#Produces("application/json")
public Foo setFoo(#PathParam("fooID") final String fooID, final Foo foo, #Context final HttpServletResponse response)
{
//TODO store foo in persistent storage
if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
{
response.setStatus(Response.Status.CREATED.getStatusCode());
}
return foo; //TODO get latest foo from storage if needed
}
Improvement: After finding another related answer, I learned that one can inject the HttpServletResponse as a member variable, even for singleton service class (at least in RESTEasy)!! This is a much better approach than polluting the API with implementation details. It would look like this:
#Context //injected response proxy supporting multiple threads
private HttpServletResponse response;
#Path("/foos/{fooId}")
#PUT
#Consumes("application/json")
#Produces("application/json")
public Foo setFoo(#PathParam("fooID") final String fooID, final Foo foo)
{
//TODO store foo in persistent storage
if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
{
response.setStatus(Response.Status.CREATED.getStatusCode());
}
return foo; //TODO get latest foo from storage if needed
}
If you like to keep your resource layer clean of Response objects, then I recommend you use #NameBinding and binding to implementations of ContainerResponseFilter.
Here's the meat of the annotation:
package my.webservice.annotations.status;
import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Status {
int CREATED = 201;
int value();
}
Here's the meat of the filter:
package my.webservice.interceptors.status;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class StatusFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
if (containerResponseContext.getStatus() == 200) {
for (Annotation annotation : containerResponseContext.getEntityAnnotations()) {
if(annotation instanceof Status){
containerResponseContext.setStatus(((Status) annotation).value());
break;
}
}
}
}
}
And then the implementation on your resource simply becomes:
package my.webservice.resources;
import my.webservice.annotations.status.StatusCreated;
import javax.ws.rs.*;
#Path("/my-resource-path")
public class MyResource{
#POST
#Status(Status.CREATED)
public boolean create(){
return true;
}
}
I found it very useful to build also a json message with repeated code, like this:
#POST
#Consumes("application/json")
#Produces("application/json")
public Response authUser(JsonObject authData) {
String email = authData.getString("email");
String password = authData.getString("password");
JSONObject json = new JSONObject();
if (email.equalsIgnoreCase(user.getEmail()) && password.equalsIgnoreCase(user.getPassword())) {
json.put("status", "success");
json.put("code", Response.Status.OK.getStatusCode());
json.put("message", "User " + authData.getString("email") + " authenticated.");
return Response.ok(json.toString()).build();
} else {
json.put("status", "error");
json.put("code", Response.Status.NOT_FOUND.getStatusCode());
json.put("message", "User " + authData.getString("email") + " not found.");
return Response.status(Response.Status.NOT_FOUND).entity(json.toString()).build();
}
}
In case you want to change the status code because of an exception, with JAX-RS 2.0 you can implement an ExceptionMapper like this. This handles this kind of exception for the whole app.
#Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {
#Override
public Response toResponse(EJBAccessException exception) {
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
}
}
If your WS-RS needs raise an error why not just use the WebApplicationException?
#GET
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Path("{id}")
public MyEntity getFoo(#PathParam("id") long id, #QueryParam("lang")long idLanguage) {
if (idLanguage== 0){
// No URL parameter idLanguage was sent
ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST);
builder.entity("Missing idLanguage parameter on request");
Response response = builder.build();
throw new WebApplicationException(response);
}
... //other stuff to return my entity
return myEntity;
}
JAX-RS has support for standard/custom HTTP codes. See ResponseBuilder and ResponseStatus, for example:
http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status%28javax.ws.rs.core.Response.Status%29
Keep in mind that JSON information is more about the data associated with the resource/application. The HTTP codes are more about the status of the CRUD operation being requested. (at least that is how it's supposed to be in REST-ful systems)
Please look at the example here, it best illustrates the problem and how it is solved in the latest (2.3.1) version of Jersey.
https://jersey.java.net/documentation/latest/representations.html#d0e3586
It basically involves defining a custom Exception and keeping the return type as the entity. When there is an error, the exception is thrown, otherwise, you return the POJO.
I'm not using JAX-RS, but I've got a similar scenario where I use:
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
Also, notice that by default Jersey will override the response body in case of an http code 400 or more.
In order to get your specified entity as the response body, try to add the following init-param to your Jersey in your web.xml configuration file :
<init-param>
<!-- used to overwrite default 4xx state pages -->
<param-name>jersey.config.server.response.setStatusOverSendError</param-name>
<param-value>true</param-value>
</init-param>
The following code worked for me. Injecting the messageContext via annotated setter and setting the status code in my "add" method.
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import org.apache.cxf.jaxrs.ext.MessageContext;
public class FlightReservationService {
MessageContext messageContext;
private final Map<Long, FlightReservation> flightReservations = new HashMap<>();
#Context
public void setMessageContext(MessageContext messageContext) {
this.messageContext = messageContext;
}
#Override
public Collection<FlightReservation> list() {
return flightReservations.values();
}
#Path("/{id}")
#Produces("application/json")
#GET
public FlightReservation get(Long id) {
return flightReservations.get(id);
}
#Path("/")
#Consumes("application/json")
#Produces("application/json")
#POST
public void add(FlightReservation booking) {
messageContext.getHttpServletResponse().setStatus(Response.Status.CREATED.getStatusCode());
flightReservations.put(booking.getId(), booking);
}
#Path("/")
#Consumes("application/json")
#PUT
public void update(FlightReservation booking) {
flightReservations.remove(booking.getId());
flightReservations.put(booking.getId(), booking);
}
#Path("/{id}")
#DELETE
public void remove(Long id) {
flightReservations.remove(id);
}
}
Expanding on the answer of Nthalk with Microprofile OpenAPI you can align the return code with your documentation using #APIResponse annotation.
This allows tagging a JAX-RS method like
#GET
#APIResponse(responseCode = "204")
public Resource getResource(ResourceRequest request)
You can parse this standardized annotation with a ContainerResponseFilter
#Provider
public class StatusFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
if (responseContext.getStatus() == 200) {
for (final var annotation : responseContext.getEntityAnnotations()) {
if (annotation instanceof APIResponse response) {
final var rawCode = response.responseCode();
final var statusCode = Integer.parseInt(rawCode);
responseContext.setStatus(statusCode);
}
}
}
}
}
A caveat occurs when you put multiple annotations on your method like
#APIResponse(responseCode = "201", description = "first use case")
#APIResponse(responseCode = "204", description = "because you can")
public Resource getResource(ResourceRequest request)
I'm using jersey 2.0 with message body readers and writers. I had my method return type as a specific entity which was also used in the implementation of the message body writer and i was returning the same pojo, a SkuListDTO.
#GET
#Consumes({"application/xml", "application/json"})
#Produces({"application/xml", "application/json"})
#Path("/skuResync")
public SkuResultListDTO getSkuData()
....
return SkuResultListDTO;
all i changed was this, I left the writer implementation alone and it still worked.
public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
I'm building a spring boot microservice-based application and I'm having trouble getting my error messages to propagate from the services containing all my business logic back into the webapp. In my services I'm throwing exceptions that look like this:
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class Http400ServiceException extends Exception {
public Http400ServiceException() {
super("Some error message");
}
}
Everything behaves as expected, sending the response code as expected. So if my service sent a 403 exception, I'd get a 403 in the webapp. What I'm trying to do now is to get the error messages from the exceptions in my service.
The Problem
When I poke my services from a rest client in such a way as to produce a 403, the response (in JSON) looks like this:
{
"timestamp": 1459453512220
"status": 403
"error": "Forbidden"
"exception": "com.mysite.mypackage.exceptions.Http403ServiceException"
"message": "Username Rob must be between 5 and 16 characters long"
"path": "/createLogin"
}
However, for some reason I can't access the 'message' field from my webapp. I have some generic error handling code in the webapp that looks like this:
#Override
public RuntimeException handleException(Exception e) {
...
if (e instanceof HttpClientErrorException) {
HttpClientErrorException httpError = (HttpClientErrorException) e;
LOGGER.info(httpError.getResponseBodyAsString());
}
...
}
But when I look in my logs/run my app in debug, httpError.getResponseBodyAsString() is returning null. It's got the right response code, just no response body.
If anyone has any insights into what's going wrong, I'd really appreciate it.
So we parked this issue for a few months while we were working on other areas of the app. But just in case someone else sees this while trying to solve a similar problem, the approach I ended up taking was the following
Create an interface for all responses from all services to implement, and an exception type to indicate that the exception is because of user error:
public interface IModel {
boolean isError();
UserException getError();
}
In the controllers for each service, catch any exceptions and create some sort of IModel out of them:
#ResponseStatus(HttpStatus.OK)
#ExceptionHandler(UserException.class)
public IModel handleException(UserException exception) {
return exception.toModel();
}
In the component used to call the services, if the response has a UserException on it, throw it, otherwise return the response body:
public <T extends IModel> T makeCall(Object payload, Endpoint<T> endpoint) throws UserException {
...
ResponseEntity<T> response = restTemplate.postForEntity(endpoint.getEndpointUrl(), payload, endpoint.getReturnType());
if (response.getBody().isError()) {
throw response.getBody().getError();
}
...
return response.getBody();
}
In the webapp, handle the exceptions with the appropriate #ExceptionHandler e.g.
#ExceptionHandler(UserException.class)
public ModelAndView handleUserException(UserException e) {
ModelAndView modelAndView = new ModelAndView("viewName");
modelAndView.addObject("errorMessage", e.getMessage());
return modelAndView;
}
I kinda feel like there's a cleaner approach but this is the best I've been able to come up with so far. I'll update this if I find a better way of doing it though.
Goal: make a general implementation for PATCH requests using reflextion API.
Context: previuosly there was a POST request (JSON input: complete object, all fields). Now I want to replace it with a PATCH request (JSON input: partial object, only updated fields). So I need to replace #RequestBody Entity entity by something like #RequestBody Map<String, Object> entityFieldValues.
JSON input:
{
"active": false
}
What have I tried :
#RequestMapping( value = "/{entityId}", method = RequestMethod.PATCH )
public void patch( #PathVariable( "entityId" ) final Long entityId, #RequestBody Map<String, Object> fieldValues) {
// load entity from DB and update values using reflection (BeanUtils)
service.patch( entityId, fieldValues);
}
It's not working. When I test this controller using POSTMAN I am facing HTTP error 501 not implemented. For now, I am using following workaround :
#RequestMapping( value = "/{entityId}", method = RequestMethod.PATCH )
public void tooglePublished( #PathVariable( "entityId" ) final Long entityId, #RequestBody String body) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
// TODO : make it work using something like: #RequestBody Map<String, String> fieldValues
Map<String, Object> fieldValues = mapper.readValue(body,new TypeReference<Map<String, Object>>() {});
// load entity from DB and update values using reflection (BeanUtils)
service.patch( entityId, fieldValues);
}
Does anybody knows how I can make it work using Spring MVC annotations?
Spring 4.1.1.RELEASE
Jackson 1.9.13
Thanks
It doesn't make sense. 501 not implemented means that the web server doesn't support the method (PATCH) at all, so the second example would fail too. Maybe you chose the wrong method in Postman.
In order to use #RequestBody Map<String, Object> you would also have to send the correct Content-Type header.
Let's say I have a repository like:
public interface MyRepository extends PagingAndSortingRepository<MyEntity, String> {
#Query("....")
Page<MyEntity> findByCustomField(#Param("customField") String customField, Pageable pageable);
}
This works great. However, if the client sends a formed request (say, searching on a field that does not exist), then Spring returns the exception as JSON. Revealing the #Query, etc.
// This is OK
http://example.com/data-rest/search/findByCustomField?customField=ABC
// This is also OK because "secondField" is a valid column and is mapped via the Query
http://example.com/data-rest/search/findByCustomField?customField=ABC&sort=secondField
// This throws an exception and sends the exception to the client
http://example.com/data-rest/search/findByCustomField?customField=ABC&sort=blahblah
An example of the exception thrown and sent to client:
{
message:null,
cause: {
message: 'org.hibernate.QueryException: could not resolve property: blahblah...'
}
}
How can I handle those exceptions? Normally, I use the #ExceptionHandler for my MVC controllers but I'm not using a layer between the Data Rest API and the client. Should I?
Thanks.
You could use a global #ExceptionHandler with the #ControllerAdvice annotation. Basically, you define which Exception to handle with #ExceptionHandler within the class with #ControllerAdvice annotation, and then you implement what you want to do when that exception is thrown.
Like this:
#ControllerAdvice(basePackageClasses = RepositoryRestExceptionHandler.class)
public class GlobalExceptionHandler {
#ExceptionHandler({QueryException.class})
public ResponseEntity<Map<String, String>> yourExceptionHandler(QueryException e) {
Map<String, String> response = new HashMap<String, String>();
response.put("message", "Bad Request");
return new ResponseEntity<Map<String, String>>(response, HttpStatus.BAD_REQUEST); //Bad Request example
}
}
See also: https://web.archive.org/web/20170715202138/http://www.ekiras.com/2016/02/how-to-do-exception-handling-in-springboot-rest-application.html
You could use #ControllerAdvice and render the content your way. Here is tutorial if you need know how to work on ControllerAdvice, just remember to return HttpEntity