mdzh
mdzh

Reputation: 1050

How to forbid slash '/' usage in entity attribute using jaxb and jaxrs

I have the following entity:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = EntityConstants.PARTNER)
public class FilePartner
{
    @XmlAttribute(name = EntityConstants.IDENTIFIER, required = true)
    @XmlJavaTypeAdapter(RestResourceIdJaxbAdapter.class)
    private String identifier;
    ...
}

Here is the jaxb adapter:

public class RestResourceIdJaxbAdapter extends XmlAdapter<String, String>
{
    @Override
    public String unmarshal(String v) throws Exception
    {
        if (v != null && v.contains("/"))
        {
            // throw new ApiException(Status.BAD_REQUEST, RestErrorMessages.BAD_REQUEST_SUFFIX, "Identifier must not contain slashes");

            return v.replaceAll("/", "");
        }

        return v;
    }

    @Override
    public String marshal(String v) throws Exception
    {
        return v;
    }
}

I have a jaxrs service that accepts POST requests with body FilePartner:

@POST
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response createPartner(FilePartner partner,
                                  @Context UriInfo uriInfo,
                                  @HeaderParam(HttpHeaders.ACCEPT) String acceptHeader)
        throws ApiException
    {
        ...
    }

What I want to achieve is to forbid the usage of slashes '/' in the identifier attribute of the FilePartner entity. Today I am doing this using some jaxb adapter which simply strips all slashes from the id when unmarshalling. Instead, what I would like is to return an appropriate BAD_REQUEST exception to the user.

I tried throwing exception in the unmarshal method of the jaxb adapter but seems that jaxrs is swallowing it and simply setting my identifier to null. If we want to override this behavior I think I must create a new @Provider and register a special ValidationEventHandler in the unmarshaller that the javax.ws.rs.ext.MessageBodyReader creates. Unfortunately, this is impossible unless I define an explicit dependency to a JAX-RS implementation which I want to avoid.

Are there any other options to restrict the usage of slashes in the identifier attribute, without defining an explicit dependency to jersey/resteasy and without handling the restriction in the @POST method of the service?

Upvotes: 0

Views: 160

Answers (1)

Optional
Optional

Reputation: 4507

To your rescue comes ReaderInterceptor

Don't do any special handling using @XmlJavaTypeAdapter. Register a ReaderInterceptor with your Application class (if in jersey2) or in web.xml if earlier.

import java.io.IOException;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
javax.ws.rs.ext.ReaderInterceptor

@Provider
public class ValidationInterceptor implements ReaderInterceptor {
    public ValidationInterceptor() {
        super();
    }

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext) throws IOException,
                                                                                           WebApplicationException {
        Object o = readerInterceptorContext.proceed();
        if (o instanceof FilePartner&& ((FilePartner) o).getIndentifier().contains("/")) {
            throw new WebApplicationException(Response.status(400)
                                                      .entity("Identifier must not contain a slash")
                                                      .build());

        }
        return o;
    }
}

And register the interceptor to your Application in override of public Set<Class<?>> getClasses() method something like classes.add(ValidationInterceptor.class);

Hope that helps.

Upvotes: 0

Related Questions