I'm learning REST web service. I need some clarification on handling custom exception in Jersey. By using WebApplicationException and ExceptionMapper you can throw custom Exception. My question is in which scenario we have go for WebApplicationException and ExceptionMapper. What are the differences between them?
I've gone through with some tutorial, I found that ExceptionMapper is like Generic.
#Provider
class MyWrapper implements ExceptionMapper<MyPojoClass>
{
}
One more difference is that,
Because of #Provider Jersey will catch this exception only by default, if any other exception occurs also.
Is there any other differences?
MyExceptionMapper.java
package com.katte.galaxe.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
public class MyExceptionMapper implements ExceptionMapper<MyCustomException>
{
#Override
public Response toResponse(MyCustomException errMsg) {
ErrorMessage msg = new ErrorMessage(errMsg.getMessage(),1100);
return Response.status(Status.NOT_FOUND)
.type(MediaType.APPLICATION_XML)
.entity(msg)
.build();
}
}
MyWebApplicationException.java
package com.katte.galaxe.exception;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
public class MyWebApplicationException extends WebApplicationException{
private static final long serialVersionUID = -5788907118191114665L;
public MyWebApplicationException(ErrorMessage msg) {
super(Response.status(Status.NOT_FOUND)
.entity(msg)
.type(MediaType.APPLICATION_XML)
.build());
}
}
WebApplicationException and ExceptionMapper are not meant to be compared as they serve different purpose. Instead they are meant to be used together for a better response strategy.
Imagine a case wherein an exception is encountered while serving a request. Let's say you encountered a SQLException while accessing a database. In this case one can make use of ExceptionMapper to map SQLException with an appropriate 5xx response instead of propagating the original exception to the end user.
WebApplicationException
Runtime exception for applications.
This exception may be thrown by a resource method, provider or StreamingOutput implementation if a specific HTTP error response needs to be produced. Only effective if thrown prior to the response being committed.
ExceptionMapper
Contract for a provider that maps Java exceptions to Response.
Related
How to implement multipart/form-data request (file upload) handler with JAX-RS without vendor specific libraries? So far I haven't found other way than to inject the HttpServletRequest and use the Servlet API to access the form data.
Yet HttpServletRequest#getParts() returns an empty list even the request is well formed (confirmed with Wireshark). I read I have to enable multipart configuration for the Jersey Servlet in the web.xml. However, I'm using #ApplicationPath annotation to automatically configure JAX-RS. So what is the correct way to handle multipart requests?
This code may inspire you
1) JAXRS App setup
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
#ApplicationPath("demo")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("com.mycompany.demo").register(MultiPartFeature.class); // <= here!
}
}
2) JAXRS service
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadImage(
#FormDataParam("file") InputStream data,
#FormDataParam("file") FormDataContentDisposition fileInfo) {
...
}
I am building REST API with Spring Boot and I use Spring Security. I started here but found some other tutorials and blog posts with this issue and managed to get it work after implementing custom stuff. This and this SO posts answer some of my questions, but I have one more:
Is there any extension that implements some of the things like REST AuthenticationEntryPoint that returns 401 instead of redirect, or JWT generating and verifying or I should just implement same things for every REST service?
Thank you for your answers.
I also use Springboot but for the security I rely on Apache Shiro project which fundamentally, depending how you store the users accounts (mines are in a MongoDb instance),
takes care of the login - currentUser.login(token);
If fails throws an exception so you can handle the response
If succeed inject the authentication cookie in the response
For any other request, decode the cookie and inject the user with the proper authorizations
In few words Shiro does not redirect the HTTPRequest because it just care for the security leaving further decision, redirect in your case, to your controller logic.
You can add it to your project with a simple Maven dependence.
#brownies.....
try this one....
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
#Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
#Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.cors().and().exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).and().authorizeRequests()......
add above RESTAuthenticationEntryPoint and config in your security configuration class then it will return 401 if auth fails.
I get the exception:
Warning: StandardWrapperValve[org.netbeans.rest.application.config.ApplicationConfig]: Servlet.service() for servlet org.netbeans.rest.application.config.ApplicationConfig threw exception
javax.ejb.AccessLocalException: Client not authorized for this invocation
This is perfectly normal, as it is not authorized for this methodcall.
Onfortunately, as this EJB is a REST Service as well, it throws a "500 - Bad Request" http status. Instead I would like to have a "401 - Unauthorized".
Should I not use EJB Security or should I catch this AccessLocalException in the ApplicationConfig or should I use Jersey to implement REST Security?
Roles are defined in the web.xml and annotations are put upon the EJB Bean.
You can define an ExceptionMapper, that maps a General Exception onto a HTTP Response.
import javax.ejb.EJBAccessException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
public class EJBAccessExceptionMapper implements
ExceptionMapper<EJBAccessException>
{
#Override
public Response toResponse(EJBAccessException exception)
{
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
I am working on an application for which I required to build the response in case an invalid JSON payload is sent in the request.
Currently the exception message received at the server end is Unexpected character ('"' (code 34)): was expecting comma to separate OBJECT entries
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#310db662; line: 13, column: 7]
This is how I'm trying to achieve it ...
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintDeclarationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#Provider
public class JsonParseExceptionMapper implements ExceptionMapper<JsonProcessingException> {
private static Logger logger = LogManager.getLogger(JsonParseExceptionMapper.class.getName());
#Override
public Response toResponse(final JsonProcessingException ex) {
System.out.println("inside json parse exception mappper");
logger.error(messages.toString());
return Response.status(Response.Status.BAD_REQUEST).entity(messages).type(MediaType.APPLICATION_JSON).build();
}
}
I also have a similar ExceptionMapper which catches the ConstraintViolationException which is working fine.
I also tried a few different exceptions as suggested over the internet, to name a few
JsonProcessingException
BadRequestException
JsonParseException
InvalidFormatException
ConstraintDeclarationException
But None of the above exceptions seem to work in this case. Does anyone have any idea as to which exception should be used in conjunction with the ExceptionMapper to catch such cases.
Also, I guess it is possible to have more than one ExceptionMapper implementations running parallel to each other. Please correct if I'm wrong.
Edit: This is what I mean by an invalid JSON Payload, look out for the missing comma before checksum
{
"payload": {
"name":"rachel",
"employeeid":"23443"
}
"checksum":""
}
Jersey v2.22.1
Spring v4.2.4-RELEASE
For some reason I'm not able to comment on any of the posts !!! Even the edit window has issues, I can't see any icon !!
I tried using RuntimeException, Exception and IOException as well but the ExceptionMapper didn't work for them too. I guess it takes a specific time of exception like the ConstraintViolationException which is in use with another ExceptionMapper and that one is working fine.
some part of web.xml
jersey.config.beanValidation.enableOutputValidationErrorEntity.server
true
jersey.config.server.disableMoxyJson
true
Initially I had registered JacksonFeature.class for Json Parsing which uses its own ExceptionMapper for JsonParseException.
I changed it to JacksonJaxbJsonProvider.class.
I'm now able to use my custom JsonParseExceptionMapper.
Had to change certain dependencies !!
I'm trying to test a resource with Resteasy using an embedded Netty instance as described in the Resteasy Docs.
Injecting path parameters and query parameters works like a charm but then I tried to test a resource that injects HttpServletRequest and HttpServletResponse from the context like this:
#GET
#Path("/")
public void example(#Context HttpServletResponse response,
#Context HttpServletRequest request) { ... }
Resteasy cannot find HttpServletRequestin the context and throws the following exception:
5105 [r #1] DEB o.j.resteasy.core.SynchronousDispatcher - PathInfo: /auth
5201 [r #1] ERR c.s.f.v.s.r.e.ApplicationExceptionMapper - Unhandled application exception: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
org.jboss.resteasy.spi.LoggableFailure: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
I tried putting mock versions of request and response in the context as suggested in RESTEasy Mock vs. Exception Mapper vs. Context but it does not work either as the contextual data is a ThreadLocal and Netty spawns a new thread for each request.
Any ideas on how to solve this?
What worked in my case was injecting a org.jboss.seam.mock.HttpServletRequest, since I am using seam in my application. You should try some mock framework like spring.test or mockito.
Here is how my code looks like:
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.seam.mock.MockHttpServletRequest;
import org.jboss.seam.mock.DBUnitSeamTest;
public class Test extends DBUnitSeamTest{
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
POJOResourceFactory noDefaults = new POJOResourceFactory(ClasstoBeTested.class); dispatcher.getRegistry().addResourceFactory(noDefaults);
MockHttpRequest request = MockHttpRequest.get("/serviceToBeTested/1961");
MockHttpResponse response = new MockHttpResponse();
HttpServletRequest servletRequest = new MockHttpServletRequest(getSession());
ResteasyProviderFactory.getContextDataMap().put(HttpServletRequest.class, servletRequest);
dispatcher.invoke(request, response);
Assert.assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Assert.assertTrue(response.getContentAsString().contains("1961"));
}
}
I just got hit by this again on another project and decided to investigate once more.
The issue is that in a mock request with Netty, there is no HttpServletRequest available. If you look into the sources of NettyJaxrsServerand related classes, Reasteasy uses its own abstraction for http requests that do not implement HttpServletRequest.
If I change my implementation to use these abstractions, I can access request and response in my resource.
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
#GET
#Path("/")
public void example(#Context HttpResponse response,
#Context HttpRequest request) { ... }
This is not perfect, because it makes my resources depend on Resteasy interfaces but I decided to go with it for now to support mock testing of multipart data.