I have created a REST service and I was wondering what the best practice was for sending meaningful messages to a GET request. Basically my GET request returns a specific object, something like this;
#GET
#Path("/examsple")
#Produces(MediaType.APPLICATION_JSON)
public List<SomeObject> retrieveSomeObjs() {
List<SomeObject> result = new ArrayList<>();
try {
result = ... Get SomeObjects ...;
} catch (Exception e) {
... Deal with exception ...
}
return result;
}
That works great except when there is an error the response just sends back an empty List! What would be more useful would be a message that explains what the problem is. However I cant send back a String message because the return type is List!
My current solution is to change the return type to a Map and then I can return the object wrapped in the Map along with any messages. However its a little messy on the client side and I was wondering if there was either an inbuilt solution or an 'accepted' solution for this.
If the client has made an error then use HTTP Response codes. If an item is not found then your response would be a 404 Not Found. If the user does not have permissions to access an object then return a 403 Forbidden. Currently you are responding with a 200 OK saying everything is OK when it's not.
If it's an error at the server side you don't really want to be sending that information to your clients. Catch the error on the server and do something meaningful with it (like log it) so you can change the code so it doesn't happen again.
You could return an HTTP error status code in the header and a JSON response body with an object describing the exception.
As already mentioned some common error codes for GET requests include:
301 Moved Permanently - If the resource has been moved
400 Bad Request - If the client request is unaccaptable, i.e. if the client sends none-sense parameters in the request
401 Unauthorized - If the client did not provide any valid credentials
403 Forbidden - If the client is authorized but not allowed to perform the request (you can also return a 404 in this case to conceal that this resource exists at all)
404 Not Found - If the requested resource could not be found
I usually create a POJO to represent these error messages and then return it using a Jersey Response object.
For example the error object could look like this:
public class ApiError {
private String status;
private String code;
private String message;
private String developerMessage;
// Getters and Setters here
}
To return it you can do the following (i.e. in your catch block or your custom ExceptionMapper):
ApiError error = new ApiError("409", "409-1", message, developerMessage);
return Response.status(Response.Status.CONFLICT).entity(error).build();
This way you can provide nicely formatted JSON/XML error messages containing custom error codes and further information for the developer. The error entities will get serialized according to your #Produces annotation.
Related
I have created a statuscallback url for Twilio SMS.
But am getting this exception
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: It is invalid to call isReady() when the response has not been put into non-blocking mode (through reference chain: org.apache.catalina.connector.ResponseFacade["outputStream"]->org.apache.catalina.connector.CoyoteOutputStream["ready"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: It is invalid to call isReady() when the response has not been put into non-blocking mode (through reference chain: org.apache.catalina.connector.ResponseFacade["outputStream"]->org.apache.catalina.connector.CoyoteOutputStream["ready"])
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:292)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:231)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:174)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
org.springframework.web.servlet.mvc.method
My code is as below
#RequestMapping(value = "/getstatus", method = RequestMethod.POST)
public Object getTwilioStatusCallback( HttpServletRequest httpRequest, final HttpServletResponse httpServletResponse){
// System.out.println(httpRequest.getParameter("MessageSid"));
httpServletResponse.setStatus(httpServletResponse.SC_NO_CONTENT);
return httpServletResponse;
}
Two things. First it looks like you are doing a synchronous call, which is a nono in web development. Second, your parameter as MessageSid.. Twilio is notorious for not having the correct reference in their documents. Try messageSid as a parameter. I had the same problem in Node.
You can also look at the entire request body and see how it shows up and get the correct parameter from there, which is what I had to do.
Hopefully this helps.
I am using Jersey Rest implementation. There are one Rest Services Called HelloWorld. See the below code.
Please consider this code as reference not as compiled code.
#Path("helloWorld")
public class HelloWorld{
#Path("test")
#Produces(...)
#Consum(...)
#GET
public Response test(Person person){
System.out.println(person);
}
}
I am using Jersey client to sent the request.
Here My question is apart from POST method is there any way to send the object to GET method directly. Instead of QueryString.
Please let me if there is any way to do so.
Thanks
So the problem shouldn't be with the server. I did a few tests on different servers (not weblogic as I don't use it) and all of them seem to have no problems accepting a body in the GET request. The problem seem to be with the client. To test I used the following code
ClientBuilder.newClient()
.target("http://localhost:8080/api/get-body")
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.request()
.method(HttpMethod.GET, Entity.text("Hello World"));
The SUPPRESS_HTTP_COMPLIANCE_VALIDATION allows us to pass a body to the request. If we didn't use this, then we would get an error.
The problem with this code, is that even though we set this override property, the client completely overrides the GET method and automatically makes it a POST method, so I would get back a 405 Method Not Allowed.
The solution I came up with is to just allow the client to set a header, e.g. X-GET-BODY-OVERRIDE, and then use a #PreMatching filter on the server side to check for this header. If the header is present, then just change the method to a GET
#Provider
#PreMatching
public class GetWithBodyFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext request) throws IOException {
String getOverride = request.getHeaderString("X-GET-BODY-OVERRIDE");
if (getOverride != null && "true".equalsIgnoreCase(getOverride)) {
request.setMethod(HttpMethod.GET);
}
}
}
Then just register the filter with the server side. On the client, you would simply need to add the header
ClientBuilder.newClient()
.target("http://localhost:8080/api/get-body")
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.request()
.header("X-GET-BODY-OVERRIDE", "True")
.method(HttpMethod.GET, Entity.text("Hello World"));
This solution is good because it takes into account more than just the Jersey client, in regards with being able to send a body in the GET request.
Is it a good idea to always use Response as a return type for all your REST services.
For example, let's say I have a method that gets all customers
First way:
public List <customer> getAllCustomers(){
// select all customers from database
}
Second Way:
public Response getAllCustomers(){
// user Response.ResponseBuilder.entity(customer)
}
enter code here
Which is a better approach. Is is advisable for all methods to return Response as return type (as in the Second way).
I would create your own response object. This will let you pass information to the client about the operation, number of records updated, error messages, etc. Also your service will behave consistently to your clients.
I would say you do not have to return any response data, but you should set your HTTP response codes appropriately. Like setting a 204 (No Content) response code for a simple acknowledgement of an operation.
My Wildfly resteasy service is working fine, or was until I made a code change. No big deal, now I'm getting a deserialization error: "Problem deserializing 'setterless' property ..."
My question is whether there is anyway to get an error message in the client. I'm getting a Status of 400, and I can test that, but I'd like to get any message if possible. Any ideas?
If I get an error in the user code, I can set an error message in the header, but since there is a deserialization problem, the server is throwing a error before getting to any user code.
You can use an ExceptionMapper to handle the response returned to the client. JAX-RS has an exception hierarchy that will map to different responses and status codes. 400 in JAX-RS is a BadRequestException. So you could do something like
#Provider
public class BadRequestExceptionMapper
implements ExceptionMapper<BadRequestException> {
#Override
public Response toResponse(BadRequestException e) {
Response response = Response.status(Response.Status.BAD_REQUEST)
.entity("Sorry I forgot to implement a Setter").build();
return response;
}
}
This isn't a very great example, because BadRequestException is thrown for many other reasons, than just forgetting a setter (or deserialization), but it demonstrates how you can intercept the response after the exception is thrown.
See RestEasy Exception Handling
Jersey User Guider has a better explanation
See Exception Hierarchy
I'm building a very simple REST API using Jersey, and I've got a warning in my log files that I'm not sure about.
WARNING: A servlet POST request, to
the URI
http://myserver/mycontext/myapi/users/12345?action=delete,
contains form parameters in the
request body but the request body has
been consumed by the servlet or a
servlet filter accessing the request
parameters. Only resource methods
using #FormParam will work as
expected. Resource methods consuming
the request body by other means will
not work as expected.
My webapp only has the Jersey servlet defined, mapped to /myapi/*
How can I stop these warnings?
For me the warning was showing for POST application/x-www-form-urlencoded. And I am using Spring Boot which has an HiddenHttpMethodFilter that does a getParameter before anything else... So I ended up doing this nasty override:
#Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if ("POST".equals(request.getMethod())
&& request.getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
};
}
This message is meant to warn developers about the fact that the request entity body has been consumed, thus any other attempts to read the message body will fail.
It is safe to ignore the message or filter it out from the logs:
java.util.logging.Logger jerseyLogger =
java.util.logging.Logger.getLogger(WebComponent.class.getName());
jerseyLogger.setFilter(new Filter() {
#Override
public boolean isLoggable(LogRecord record) {
boolean isLoggable = true;
if (record.getMessage().contains("Only resource methods using #FormParam")) {
isLoggable = false;
}
return isLoggable;
}
});
The following thread describes the warning you are receiving. It sounds as though you might have a filter defined in your web.xml that is processing the request before Jersey does.
Finally got rid of this by making sure I had Content-Type: application/json in my request headers (obviously, on the client side)
I just had my ajax-function in JQuery set to contentType: "application/x-www-form-urlencoded; charset=UTF-8" because with a prior solution (without Jersey) I had some encoding problems. When I removed that the message was gone and everything worked fine.
This warning is the only thing the WebComponent logs, so just turn logging up to ERROR level or turn off logging for this component in your logback.xml or wherever you have logging configured. You don't need to write a custom filter to ignore this specific message since there are no other messages logged from this component.
Source code snippet from org.glassfish.jersey.servlet.WebComponent version 2.14:
if(!form.asMap().isEmpty()) {
containerRequest.setProperty("jersey.config.server.representation.decoded.form", form);
if(LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, LocalizationMessages.FORM_PARAM_CONSUMED(containerRequest.getRequestUri()));
}
}
The localized message that is used for this warning message is:
form.param.consumed=A servlet request to the URI {0} contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
Turn logging off for the WebComponent in your logback.xml like so:
<logger name="org.glassfish.jersey.servlet.WebComponent" level="OFF" additivity="false"/>
Right.
So I've been suffering this issue, and I've been trying to solve it on different ways, but I did't want to change my web.xml settings, just because if I was testing my application with Postman it worked perfect, but when it was being integrated with the webapp it fails with the mentioned issue (A servlet request to the URI {MY_URI} contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.)
So as #clijk mentioned, you only have to set your headers as:
"Content-Type":"application/json"
"charset":"UTF-8"
and voilá, the warning it's gone.
Thanks
In my case I've fixed this error when I've changed the Object Date to String in the method.
Error:
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
public List<MyObject> myMethod(#FormParam("StartDate") Date date) throws Exception {
Fixed
#POST
#Path("/myPath")
#Produces(MediaType.APPLICATION_JSON)
public List<MyObject> myMethod(#FormParam("StartDate") String date) throws Exception {
Put this to your resource signature. Or find this string in your project someone already use this if #PUT or #POST is used. This should help
import javax.ws.rs.Consumes;
#Consumes(MediaType.APPLICATION_JSON)