Java: Enum case insensitive Jersey Query Param Bin

2019-04-06 14:05发布

问题:

I tried to override/implement all the attribute in JSR311 but the Jersey binding seems case sensitive:

  1. Be a primitive type
  2. Have a constructor that accepts a single String argument
  3. Have a static method named valueOf or fromString that accepts a single String argument (see, for example, Integer.valueOf(String))
  4. Be List, Set or SortedSet, where T satisfies 2 or 3 above. The resulting collection is read-only.

How can I make Jersey binding for enum case insensitive?

EDIT:

Here's the code:

The enum:

public enum Color {

    GREEN,

    BLUE;

    public Color fromString(String param) {
        String toUpper = param.toUpperCase();
        try {
            return valueOf(toUpper);
        } catch (Exception e) {
            return null;
        }
    }
}

Bean Param:

 public class FooQueryParam {
        @QueryParam(value = "color")
        private Color color;

        public Color getColor() {
            return color;
        }

        public void setStatus(Color Color) {
            this.color = color;
        }
    }

The resource:

public Response get(@BeanParam FooQueryParam fooQueryParam) {
            //... 
}

回答1:

If you're doing it right it shouldn't be a problem. For example in case 3, using a fromString

public static enum Color {

    RED, GREEN, BLUE;

    public static Color fromString(String param) {
        String toUpper = param.toUpperCase();
        try {
            return valueOf(toUpper);
        } catch (Exception e) {
            // default behavior is to send 404 on error.
            // do something else if you want
            throw new WebApplicationException(400);
        }
    }
}

Every enum already has a static valueOf method which tries to match the enum value exactly, so we override this behavior by defining the fromString. We first call toUpperCase() then call valueOf as it's looking for upper case. If anything fails, like a wrong enum value, we send a 400. You can send something else or stick with the normal 404. Up to you.

I already did this, it is not working

Here is a complete test case.

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class EnumTest extends JerseyTest {

    public static enum Color {

        RED, GREEN, BLUE;

        public static Color fromString(String param) {
            String toUpper = param.toUpperCase();
            try {
                return valueOf(toUpper);
            } catch (Exception e) {
                // default behavior is to send 404 on error.
                // do something else if you want
                throw new WebApplicationException(400);
            }
        }
    }

    @Path("enum")
    public static class ColorResource {

        @GET
        public String color(@QueryParam("color") Color color) {
            return color.toString();
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(ColorResource.class);
    }

    @Test
    public void doit() {
        Response response = target("enum").queryParam("color", "blue").request().get();
        assertEquals(200, response.getStatus());
        assertEquals("BLUE", response.readEntity(String.class));
        response.close();

        response = target("enum").queryParam("color", "gReEn").request().get();
        assertEquals(200, response.getStatus());
        assertEquals("GREEN", response.readEntity(String.class));
        response.close();

        response = target("enum").queryParam("color", "RED").request().get();
        assertEquals(200, response.getStatus());
        assertEquals("RED", response.readEntity(String.class));
        response.close();

        response = target("enum").queryParam("color", "orange").request().get();
        assertEquals(400, response.getStatus());
        response.close();
    }
}

Use this dependency

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>2.19</version>
    <scope>test</scope>
</dependency>

UPDATE

Test using @BeanParam

import javax.ws.rs.BeanParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class EnumTest extends JerseyTest {

    public static enum Color {

        RED, GREEN, BLUE;

        public static Color fromString(String param) {
            String toUpper = param.toUpperCase();
            System.out.println("--- toUpper " + toUpper + "---");
            try {
                return valueOf(toUpper);
            } catch (Exception e) {
                System.out.println(" --- ERROR ---");
                // default behavior is to send 404 on error.
                // do something else if you want
                throw new WebApplicationException(400);
            }
        }
    }

    public static class FooQueryParam  {
        @QueryParam("color") 
        private Color color;
        public Color getColor() { return color; }
        public void setColor(Color color) { this.color = color; }
    }

    @Path("enum")
    public static class ColorResource {

        @GET
        public String color(@BeanParam FooQueryParam foo) {
            return foo.getColor().toString();
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(ColorResource.class);
    }

    @Test
    public void doit() {
        Response response = target("enum").queryParam("color", "blue").request().get();
        assertEquals(200, response.getStatus());
        assertEquals("BLUE", response.readEntity(String.class));
        response.close();

        response = target("enum").queryParam("color", "gReEn").request().get();
        assertEquals(200, response.getStatus());
        assertEquals("GREEN", response.readEntity(String.class));
        response.close();

        response = target("enum").queryParam("color", "RED").request().get();
        assertEquals(200, response.getStatus());
        assertEquals("RED", response.readEntity(String.class));
        response.close();

        response = target("enum").queryParam("color", "orange").request().get();
        assertEquals(400, response.getStatus());
        response.close();
    }
}

The only thing that fails is the error case where I am sending a bad color. It seems with @BeanParam the behavior is different. Instead of the expected 400, there is a 500. The other case sentiviity issues are fine