Sometimes JAR-RS clients are sending wrong syntactical request body. The server should response with HTTP status 400 (Bad Request), but it responses with HTTP status 500 (Internal Server Error).
Code:
JAX-B model class:
#XmlRootElement(namespace = "http://www.test.com/test")
#XmlAccessorType(value = XmlAccessType.FIELD)
public class TestModel {
#XmlElement
private String id;
}
JAX-RS resource class:
#Path("test")
public class TestResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void create(TestModel testModel) {
// some code
}
}
CXF configuration:
<jaxrs:server address="/rest" id="test" staticSubresourceResolution="true">
<jaxrs:serviceBeans>
<ref bean="testResource" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
Example:
Request body:
{"id2": "test"}
The id2 is wrong, so client should get a HTTP status 400, but it gets HTTP status 500.
Server log:
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "id2" (class test.TestModel), not marked as ignorable (one known property: "id"])
at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1#6f30793d; line: 1, column: 10] (through reference chain: test.TestModel["id2"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:839)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1045)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1352)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1330)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:264)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1470)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:912)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:811)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1343)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1294)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:826)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:789)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:212)
... 68 more
Is there a way to configure Jackson and/or CXF to return HTTP status 400 for wrong syntactical request body without schema validation or bean validation?
The problem is that exceptions that aren't mapped to response (by way of ExceptionMappers), translate to a general server error response, as the runtime has no idea what to do with the exception.
The jackson-jaxrs-provider module has ExceptionMappers to handle the Jackson base exception class JsonMappingException and JsonParseException. The mappers are JsonMappingExceptionMapper and JsonParseExceptionMapper, respectively. These mappers will map the exception to a 400 response along with the exception message as the response body. If you do not like this response body, you can just write your own mapper.
Related
I have a REST service, where i need to send appropriate status codes with the custom json response, which explains the reason for the error.
I have #Controller class which exposes a service /test and while hitting the service, I am throwing a custom exception (extends Exception class)
I have a #ControllerAdvice class which handles the exception. However, whenever i get error i don't get the json response, instead the stack trace is printed.
Checked the blogs and tried the solutions. However could not get it resolved.
#ControllerAdvice(basePackages = "com.test")
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(value= {MyException.class})
#ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
ResponseEntity<MyResponse> commonServiceException(MyException ex) {
MyResponse errors = new MyResponse();
errors.setTimeStamp(LocalDateTime.now());
errors.setErrorCode(ex.getErrorCode());
errors.setErrorMessage(ex.getMessage());
errors.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
errors.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
SERVICE
#Controller
....
public class test{
..
#GET
#Path("/test")
//#ExceptionHandler(value= {MyException.class})
#Produces(MediaType.APPLICATION_JSON)
public String test() throws MyException{
throw new MyException("123", "message");
}
context.xml
<context:component-scan base-package=" com.test">
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
Expected : JSON Response
Actual : Type Exception Report
Message Service currently unavailable. Error Code : Application Error. Error
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
com.test.MyException: Service currently unavailable. Error Code : Application Error..............................
This was because I was mixing up Jersey and Spring Framework. After doing necessary spring servlet configurations in web.xml and using #RequestMapping the issue is resolved.
I am getting 400 Http response when i am passing the invalid json format,
I would like to return the custom json message instead of this , can any one advise how to do in Spring 4.1 ?
Handling Execption using ControllerAdvice,but it is not working.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler({org.springframework.http.converter.HttpMessageNotReadableException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public String resolveException() {
return "error";
}
}
spring-config.xml is given below
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- Renders JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Given below Json request and response from WebSphere application server (7.0).
Request 1: Empty json request : {}
Response Status Code: 400 Bad Request
Response Message : Json request contains invalid data:null
Request 2:Invalid format of Json Request : {"data":,"name":"java"}
Response Status Code: 400 Bad Request
Response or Exception message :
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unexpected character (',' (code 44)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: com.ibm.ws.webcontainer.srt.http.HttpInputStream#8f308f3; line: 5, column: 57]
Similar question like below link
Using Spring MVC, accepting POST requests with bad JSON leads to a default 400 error code server page being returned
You can attempt to map the exception this way. This code will return a 400 status, but you can change the return the same way as is the link you posted
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleJsonMappingException(JsonMappingException ex) {}
Finally i have handle the exception via Servlet Filter with HttpServletRequestWrapper.
Step 1: Add the filter
Step 2: Get the request body from Customize HttpServletRequestWrapper class
Step 3: Convert request body json string to java object using JSON API
Step 4: Chain the request/response
Step 5: Catch exception / and update the HttpServlet Response
Using below reference.
Filter Example
HttpServletRequestWrapper Example
String to Json Object
With the help of this approach i can handle 400/405/415 Http Errors.
You may try this, in your pom.xml add dependency:
<!-- Need this for json to/from object -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
this will convert your java objects to JSON automatically when you return them. like you can write a class for response:
public class Response {
private int responseCode;
private String responseMessage;
//as many fields as you like
public Response (int responseCode, String responseMessage) {
this.responseCode = responseCode;
this.responseMessage = responseMessage;
} }
then you can return any java objects and they will be received as JSON,
#RequestMapping(value="/someMethod", method=RequestMethod.POST)
public #ResponseBody Response someMethod(#RequestBody Parameters param) {
return new Response(404, "your error message");
}
I have a question for the developers of Spring Web MVC.
In a nutshell: previously it was possible to send a request body in an HTTP DELETE message, but now it is not possible anymore. Why?
In detail:
We are using spring-webmvc-4.2.4.RELEASE.
#RestController
public class Controller {
#RequestMapping(value = "/{pathVariable}/deleteAnything", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteAnything(#PathVariable String pathVariable,
#Valid #RequestBody Set<Pojo> pojoSet) {
...
We send
DELETE /anything/deleteAnything HTTP/1.1
Content-Type: application/json
Host: example.com
[ {
"any field" : "Any value"
} ]
and get the exception
m.m.a.RequestResponseBodyMethodProcessor : Read [java.util.Set<packagename.Pojo>] as "application/json;charset=UTF-8" with [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#333825a3]
.w.s.m.m.a.ServletInvocableHandlerMethod : Error resolving argument [1] [type=java.util.Set]
HandlerMethod details:
Controller [packagename.Controller]
Method [public org.springframework.http.ResponseEntity<?> packagename.Controller.deleteAnything(java.lang.String,java.util.Set<packagename.Pojo>)]
org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.http.ResponseEntity<?> packagename.Controller.deleteAnything(java.lang.String,java.util.Set<packagename.Pojo>)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:151)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:125)
...
It seems that the request body has been removed.
If we use HTTP POST instead of HTTP DELETE everywhere, it works fine.
Previously it worked fine (sorry that I cannot specify previously because our dependencies are very complicated. If it helps you, I can post an old build.gradle).
Why is it not possible anymore?
You probably should redesign your API, as payloads within DELETE requests should be ignored.
From https://www.rfc-editor.org/rfc/rfc7231#section-4.3.5:
A payload within a DELETE request message has no defined semantics.
From https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3:
If the request method does not include defined semantics for an
entity-body, then the message-body SHOULD be ignored when handling the
request.
It seems to be a problem with zuul. Without zuul it works. Spring has nothing to do with it.
How can i return response status 405 with empty entity in java REST?
#POST
#Path("/path")
public Response createNullEntity() {
return Response.created(null).status(405).entity(null).build();
}
It returns status code 405, but the entity is not null, it is the http page for the error 405.
When you return an error status, Jersey delegates the response to your container's error processing via sendError. When sendError is called, the container will serve up an error page. This process is outlined in the Java Servlet Specification ยง10.9 Error Handling.
I suspect what you are seeing is your container's default error page for a 405 response. You could probably resolve your issue by specifying a custom error page (which could be empty). Alternatively, Jersey won't use sendError if you provide an entity in your response. You could give it an empty string like this:
#POST
#Path("/path")
public Response createNullEntity() {
return Response.status(405).entity("").build();
}
The above results in Content-Length of 0
I'm trying to handle errors coming from my backend. The handleMessage() is called if an error occurs but the content is an instance of XmlMessage. I would like to change it to my own response - just set the response code and add some message.
I haven't found any proper documentation which could tell me how to do this...
These axamples are for REST but I'd like to manage this thing in SOAP too.
interceptor
public class ErrorHandlerInterceptor extends AbstractPhaseInterceptor<Message> {
public ErrorHandlerInterceptor() {
super(Phase.POST_LOGICAL);
}
#Override
public void handleMessage(Message message) throws Fault {
Response response = Response
.status(Response.Status.BAD_REQUEST)
.entity("HOW TO GET A MESSAGE FROM AN EXCEPTION IN HERE???")
.build();
message.getExchange().put(Response.class, response);
}
}
context.xml
<bean id="errorHandlerInterceptor"
class="cz.cvut.fit.wst.server.interceptor.ErrorHandlerInterceptor" />
<jaxrs:server address="/rest/">
<jaxrs:serviceBeans>
<ref bean="restService" />
</jaxrs:serviceBeans>
<jaxrs:outFaultInterceptors>
<ref bean="errorHandlerInterceptor" />
</jaxrs:outFaultInterceptors>
</jaxrs:server>
If you're using JAX-RS, why not setup an exception mapper, and then use that mapper to handle the response.
A simple example:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class MyExceptionMapper implements
ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException e) {
return Response.status(Status.NOT_FOUND).build();
}
}
Then you would need to register the provider in the jaxrs serve by adding:
<jaxrs:providers>
<bean class="com.blah.blah.blah.blah.MyExceptionMapper"/>
</jaxrs:providers>
in the server config in the context. With that you have full access to the exception, and can get whatever you want from it.
And here's the other piece of your puzzle. You're already using JAX-RS, so why not use JAX-WS as well?
This thread and this blog post cover mapping Exceptions into SOAP faults. Short and sweet:
The JAX-WS 2.0 specification demands that the exception annotated with #WebFault must have two constructors and one method [getter to obtain the fault information]:
WrapperException(String message, FaultBean faultInfo)
WrapperException(String message, FaultBean faultInfo, Throwable cause)
FaultBean getFaultInfo()
The WrapperException is replaced by the name of the exception, and FaultBean is replaced by the class name that implements the fault bean. The fault bean is a Java bean that contains the information of the fault and is used by the Web service client to know the cause for the fault.
And there's your mapping. Simply specify implementations of the above signatures in the context of #WebFault and your SOAP API should map these happily. Obviously, the links contain more details.