/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package ar.com.pucaratech.rest.exceptionmappers;
import ar.com.pucaratech.dtos.ResponseComprobantes;
import java.util.Arrays;
import java.util.List;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Variant;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.pac4j.core.exception.TechnicalException;
/**
*
* @author Seba
*/
@Provider
public class ErrorExceptionMapper extends Throwable implements ExceptionMapper<Exception> {
@Context
private javax.inject.Provider<Request> request;
@Override
public Response toResponse(Exception exception) {
Response.ResponseBuilder response = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
if (exception instanceof TechnicalException) {
response = Response.status(Response.Status.UNAUTHORIZED);
} else if (exception instanceof BadRequestException) {
response = Response.status(Response.Status.BAD_REQUEST);
} else if (exception instanceof NotAuthorizedException) {
response = Response.status(Response.Status.UNAUTHORIZED);
} else if (exception instanceof NotFoundException) {
response = Response.status(Response.Status.NOT_FOUND);
} else {
response = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
}
// Entity
final List<Variant> variants = Variant.mediaTypes(
MediaType.APPLICATION_JSON_TYPE,
MediaType.APPLICATION_XML_TYPE
).build();
final Variant variant = request.get().selectVariant(variants);
if (variant != null) {
response.type(variant.getMediaType());
} else {
/*
* default media type which will be used only when none media type from {@value variants} is in
* accept header of original request.
*/
response.type(MediaType.TEXT_PLAIN_TYPE);
}
ResponseComprobantes rc = new ResponseComprobantes();
rc.setResultado(ResponseComprobantes.RESULTADO_ERROR);
if (exception instanceof TechnicalException && exception.getMessage()
.equals("Cannot decrypt / verify JWT")) {
rc.setErrores(Arrays.asList("Token inválido. No se pudo verificar el token"));
} else {
rc.setErrores(Arrays.asList(exception.getMessage()));
}
response.entity(
new GenericEntity<ResponseComprobantes>(
rc,
new GenericType<ResponseComprobantes>() {
}.getType()
)
);
return response.build();
}
}
For the record, I tried ContainerResponseFilter to customize 401 Unauthorized and 403 Forbidden and works like a charm. Thanks Victor and leleuj for the help.
package <your package goes here>;
import java.io.IOException;
import java.util.List;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;
import org.apache.http.HttpStatus;
import <your package goes here>.ErrorResult;
@Provider
public class AuthenticationErrorResponseFilter
implements ContainerResponseFilter {
/** Intercept outgoing 403s, and add an entity to the response
* in an appropriate MediaType.
*/
@Override
public void filter(final ContainerRequestContext requestContext,
final ContainerResponseContext responseContext)
throws IOException {
if (responseContext.getStatus() == HttpStatus.SC_FORBIDDEN) {
MediaType requestMediaType = requestContext.getMediaType();
List<MediaType> acceptableMediaTypes =
requestContext.getAcceptableMediaTypes();
MediaType responseMediaType;
if (acceptableMediaTypes == null
|| acceptableMediaTypes.isEmpty()
|| acceptableMediaTypes.contains(requestMediaType)) {
responseMediaType = requestMediaType;
} else {
responseMediaType = acceptableMediaTypes.get(0);
}
responseContext.setEntity(new ErrorResult("Forbidden"),
null, responseMediaType);
}
}
}
package <your package goes here>;
import javax.xml.bind.annotation.XmlRootElement;
/** Class for representing an error result for an API call. */
@XmlRootElement(name = "error")
public class ErrorResult {
/** The text of the error message. */
private String message;
/** Default constructor.
*/
public ErrorResult() {
}
/** Constructor that takes the text of the error message as a parameter.
* @param aMessage The text of the error message.
*/
public ErrorResult(final String aMessage) {
message = aMessage;
}
/** Set the text of error message.
* @param aMessage The text of the error message.
*/
public void setMessage(final String aMessage) {
message = aMessage;
}
/** Get the text of the error message.
* @return The text of the error message.
*/
public String getMessage() {
return message;
}
}
Are you sure you need to bother with the media type like you do? I wouldn't have bothered with it personally, I would have just called setEntity and let Jersey handle things from there… not sure it is the best though ^^
public void filter(final ContainerRequestContext requestContext,
final ContainerResponseContext responseContext)
throws IOException {
if (responseContext.getStatus() == HttpStatus.SC_FORBIDDEN) {
responseContext.setEntity(new ErrorResult("Forbidden"));
}
}
On Monday, March 27, 2017 at 6:38:37 PM UTC+11, victo...@gmail.com wrote:Are you sure you need to bother with the media type like you do? I wouldn't have bothered with it personally, I would have just called setEntity and let Jersey handle things from there… not sure it is the best though ^^
Oh!
I must have done something wrong the first time, because it wasn't working.
(Maybe I clicked the wrong button in Swagger-UI?)
But you have encouraged me to try again ... and it works perfectly.
package <your package goes here>;
import org.apache.http.HttpStatus;
import org.pac4j.core.http.HttpActionAdapter;
import org.pac4j.jax.rs.pac4j.JaxRsContext;
import <your package goes here>.ErrorResult;
/** An HttpActionAdapter based on JaxRsHttpActionAdapter, but which
* returns an entity if authentication fails.
*/
public class AuthHttpActionAdapter
implements HttpActionAdapter<Object, JaxRsContext> {
/** Singleton instance of this class. */
public static final AuthHttpActionAdapter INSTANCE =
new AuthHttpActionAdapter();
/** For an authentication failure, insert an error response. */
@Override
public Object adapt(final int code, final JaxRsContext context) {
if (code == HttpStatus.SC_FORBIDDEN) {
context.getRequestContext().abortWith(
context.getAbortBuilder().
entity(new ErrorResult("Forbidden too")).
build());
} else {
context.getRequestContext().abortWith(
context.getAbortBuilder().build());
}
return null;
}
}
configInstance.setHttpActionAdapter(
AuthHttpActionAdapter.INSTANCE);
Actually, that's quite clever I think to work at that level.
It means it will only be applied to pac4j answers, so it's not always the best fit, but if it is your case, it is better to work at that place yes!