Vicky Vicky - 3 months ago 17
Java Question

Implementing strong params using Dropwizard

I would like to validate the request in a way that only specific request fields should be accepted and return 400 for all the non-accepted fields using Dropwizard and Jersey.

Is there anyway to do this?

EDIT:

Example - Lets assume the request contains the following JSON:
{
name: Test,
age: 30,
id: 52
}

and the field id is not accepted in the request by the server.

Expected: The server should return 400 stating that the field id is not allowed in the request.

Answer

Second try to answer this question after question was edited.

You can build your JSON bean and ignore unknown values like ID with Jackson annotation 'JsonIgnoreProperties'.

You have following resource:

@Post
public Response hello(Person person) {
        return Response
                .status(Status.OK)
                .entity(String.format("Hello %s. We know that you are %s years old, but we do not know if you send more informations to us like id or sth. else.", person.getName(), person.getAge()))
                .build();
}

@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private final String name;
    private final int age;

    @JsonCreator
    public Person(@JsonProperty String name,@JsonProperty int age) {
        this.name= name;
        this.age= age;
    }

    @JsonProperty
    public String getName() {
        return name;
    }

    @JsonProperty
    public String getage() {
        return age;
    }
}



OldAnswer: Why not validate them? Below 3 example of how to do this.

Option 1:

@Path("smooth")
@GET
public Response smooth(
    @DefaultValue("2") @QueryParam("step") int step,
    @DefaultValue("true") @QueryParam("min-m") boolean hasMin,
    @DefaultValue("true") @QueryParam("max-m") boolean hasMax,
    @DefaultValue("true") @QueryParam("last-m") boolean hasLast,
    @DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
    @DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
    @DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) {
    validateMaxColor(maxColor);
}

public void validateMaxColor(ColorParam  maxColorParam){
    if(!maxColorParam.isInRange()){
        throw new WebapplicationException("maxColorParam out of range");
    }
}

Option 2:Use a specific param class

@Path("smooth")
@GET
public Response smooth(
    @DefaultValue("2") @QueryParam("step") int step,
    @DefaultValue("true") @QueryParam("min-m") boolean hasMin,
    @DefaultValue("true") @QueryParam("max-m") boolean hasMax,
    @DefaultValue("true") @QueryParam("last-m") boolean hasLast,
    @DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
    @DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
    @DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) {
    ....
}


public class ColorParam extends Color {

    public ColorParam(String color) {
        super(getRGB(color));
        if(getRGB(color).isGreen()){
            throw new ParseException("ohh nooooo ... green is not allowed here");
        }
    }

    private static int getRGB(String s) {
        if (s.charAt(0) == '#') {
            try {
                Color c = Color.decode("0x" + s.substring(1));
                return c.getRGB();
            } catch (NumberFormatException e) {
                throw new WebApplicationException(400);
            }
        } else {
            try {
                Field f = Color.class.getField(s);
                return ((Color)f.get(null)).getRGB();
            } catch (Exception e) {
                throw new WebApplicationException(400);
            }
        }
    }
}

Here it is important that you use a String constructor.

Option 3: For Dropwizard Users; Use AbstractParam and validate the incoming string

See Dropwizard documentation about Params:

Parameters

The annotated methods on a resource class can accept parameters which are mapped to from aspects of the incoming request. The *Param annotations determine which part of the request the data is mapped, and the parameter type determines how the data is mapped.

For example:

A @PathParam("user")-annotated String takes the raw value from the user variable in the matched URI template and passes it into the

method as a String. A @QueryParam("count")-annotated IntParam parameter takes the first count value from the request’s query string and passes it as a String to IntParam‘s constructor. IntParam (and all other io.dropwizard.jersey.params.* classes) parses the string as an Integer, returning a 400 Bad Request if the value is malformed. A @FormParam("name")-annotated Set parameter takes all the name values from a posted form and passes them to the method as a set of strings. A *Param–annotated NonEmptyStringParam will interpret empty strings as absent strings, which is useful in cases where the endpoint treats empty strings and absent strings as interchangeable.

What’s noteworthy here is that you can actually encapsulate the vast majority of your validation logic using specialized parameter objects. See AbstractParam for details.

Your ressource should get sth like this:

@Path("long")
@GET
public Response long(
    @QueryParam("long") LongParam longValue) {
    //here your longValue is in wanted Range
    //doubles or floats will return a HTTP 400
}


/**
 * A parameter encapsulating long values. All non-decimal values will return a {@code 400 Bad
 * Request} response.
 */
public class LongParam extends AbstractParam<Long> {
    public LongParam(String input) {
        super(input);
    }

    @Override
    protected String errorMessage(Exception e) {
        return "Parameter is not a number.";
    }

    @Override
    protected Long parse(String input) {
        return Long.valueOf(input);
    }
}

With this you can easily validate your incoming string and throw an exception if string can not be parsed or is not valid range (e.g. if you want a HexValue and your string is #ZZ8000, then you can check that hex string is not allowed to contain any 'Z').

In the end: Please upvote if this solved your problem.