8

I would like to call my Webservice with this pattern :

/resource/1,2,3

And in my Class I want to bind my parameters to a List of Object

@Path("/resource")
public class AppWS {

    @GET
    @Path("/{params}")
    public Response get(@PathParam("params") List<MyObject> params) {
        return Response.status(200).entity("output").build();
    }
}

With a simple Object:

public class MyObject {
    Integer value;
    public MyObject(Integer value) {
        this.value = value;
    }
}

nb: If it possible I don't want to create an MyObjectList which extends List (and have a constructor which split my string)

How may I proceed ?

Guillaume
  • 694
  • 1
  • 6
  • 15
  • Your link is broken. Could you just say the name of the pattern or explain it instead? – gla3dr Jan 23 '15 at 21:45
  • Hi gla3dr, thx for the response. It's not a real link, it's just an example of uri that can access to the resource. In fact, I want to use commas for multi values in the path (I edit my post) – Guillaume Jan 23 '15 at 22:47
  • Oh I see. I misinterpreted what you meant, my bad! The way you have it now is much more clear. – gla3dr Jan 23 '15 at 22:48
  • Np! It's me. I've clarified my post – Guillaume Jan 23 '15 at 22:50
  • Hi. Does it really have to be `/resource/1,2,3`. Can it not be something like `resource?1,2,3`? QueryParam definitively accepts more than one argument, although you would not write it like that (it would usually be something like `resource?id=1,id=2,id=3`). Hope it helps. – lrnzcig Jan 24 '15 at 10:59
  • Good question! Surely `resource/1,2,3` will be received as List on server, but all params are in the same element. (i.e. expected `list.get(0)` = 1, `list.get(1)` = 2, `list.get(2)` = 3, but actually `list.get(0)` = 1,2,3.) And Jin Kown's answer contains workaround of it. Your question and responded answer save my time, thank you! – tkhm Dec 13 '17 at 12:47
  • is this a good rest api design practice to have path params as a list? – theprogrammer Feb 06 '20 at 15:23

1 Answers1

11

I'm not sure the way of 1,2,3.

If you insist,

private Response get(List<MyObject> params) {
}

@GET
@Path("/{params}")
public Response get(@PathParam("params") String params) {

    return get(Stream.of(params.split(","))
        .map(MyObject::new)
        .collect(Collectors.toList()));
}

If you really insist,

annotation,

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface MyObjectsParam {

}

converter,

public class MyObjectsParamDateConverter
    implements ParamConverter<List<MyObject>> {

    @Override
    public List<MyObject> fromString(final String value) {
        return Stream.of(params.split(","))
          .map(MyObject::new)
          .collect(Collectors.toList())
    }

    @Override
    public String toString(final List<MyObject> value) {
        return value.stream()
            .map(o -> o.getValue())
            .map(Object::toString)
            .collect(Collectors.joining(","));
    }
}

provider,

@Provider
public class MyObjectsParamConverterProvider
    implements ParamConverterProvider {

    @Override
    @SuppressWarnings("unchecked")
    default ParamConverter getConverter(final Class<S> rawType,
                                        final Type genericType,
                                        final Annotation[] annotations) {
        for (final Annotation annotation : annotations) {
            if (MyObjectsParam.class.isInstance(annotation)) {
                return new MyObjectsParamDateConverter();
            }
        }

        return null;
    }
}

Now you can use it like this.

@GET
@Path("/{params}") // still '1,2,3'
public Response get(
    @PathParam("params")
    @MyObjectsParam // IN ACTION!!!
    List<MyObject> params) {

}
Agnibha
  • 613
  • 1
  • 11
  • 20
Jin Kwon
  • 20,295
  • 14
  • 115
  • 184