Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Enum case insensitive Jersey Query Param Binding

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) {
            //... 
}
like image 550
richersoon Avatar asked Oct 26 '15 23:10

richersoon


People also ask

How do I pass Enum in query param?

In an HTTP Get request, we can pass additional parameters in the query string. These parameters are typically a string or an integer data type. Since an Enum is a Value-Type, we can parse Enum into a string or an integer. That helps us to use the Enum in a query string parameter without any drama.

Is Enum in Java case sensitive?

The Java enum that you define is case-sensitive and can only be created from exactly matched string.

Are Enum values case sensitive?

Enum values must be GraphQL Names: they may contain only letters, numbers, and underscores. Enums are case sensitive; by convention they are in all upper-case.

Can enums have parameters?

You can have methods on enums which take parameters. Java enums are not union types like in some other languages.


1 Answers

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

like image 181
Paul Samsotha Avatar answered Oct 09 '22 11:10

Paul Samsotha