Принятие списка в качестве параметра для веб-службы Джерси, которая использует тип содержимого, состоящий из нескольких частей.

У меня был существующий метод веб-службы Джерси, который принимает ряд параметров через метод Http POST, который предназначен для обработки данных стандартной формы, типа содержимого application/x-www-form-urlencoded; одним из этих параметров был список строк. Ниже приведен пример сигнатуры метода, которая у меня есть.

@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createItem(
        @FormParam("p1") long p1,
        @FormParam("p2") String p2,
        @FormParam("p3") List<String> p3,
        @FormParam("p4") String p4,
        @Context UriInfo uriInfo
) throws SQLException {

Это работало правильно, и когда несколько параметров p3 передаются в список, он правильно генерируется Джерси и передается в метод.

Теперь мне нужно было сделать альтернативную версию этого метода, которая принимала бы запрос из нескольких частей, чтобы вместе с существующими параметрами можно было загрузить и файл. Поэтому я создал очень похожую сигнатуру метода для обработки запросов, состоящих из нескольких частей, пример показан ниже.

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createItemWithFile(
        @FormDataParam("p1") long p1,
        @FormDataParam("p2") String p2,
        @FormDataParam("p3") List<String> p3,
        @FormDataParam("p4") String p4,
        @FormDataParam("file") InputStream inputStream,
        @Context UriInfo uriInfo
) throws SQLException {

Я изменил аннотации FormParam на FormDataParam, так как считаю, что это необходимо при использовании данных, состоящих из нескольких частей. Я пытался вызвать этот метод из теста JUnit, используя RESTAssured для выполнения вызова (так же, как это было сделано для исходного метода), но я получаю следующую ошибку.

java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205)
at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)

Поместив некоторые точки останова в код Джерси, в некоторых точках, указанных в трассировке стека, кажется, что он определил правильный метод для вызова, но в списке параметров, которые он пытается ему передать, p3 опущен .

Есть ли что-то другое, что нужно сделать, чтобы поддерживать прием списка в качестве входных данных при работе с данными, состоящими из нескольких частей? Учитывая, что это необязательный параметр, я ожидал, что в любом случае его можно будет опустить, это относится к исходному методу.

Код RESTAAssured в тесте, используемом для вызова метода, выглядит следующим образом.

Response response = given()                    
                .header("my_header", "xyz")
                .param("p1", "8000040")
                .param("p2", "sample string") 
                .param("p3", "first_value")
                .param("p4", "abcde")
                .multiPart("file", myFile1, inputStream)
                .expect()

Я также пытался использовать formParam в тестовом коде RESTAssured вместо param, но получил тот же результат.

Заранее спасибо, буду признательна за любую помощь.


person Rob    schedule 13.06.2012    source источник
comment
какую версию RESTAssured вы используете? Если › 1.5, то добавь log() сразу после given() и увидишь свой запрос.   -  person Alex Stybaev    schedule 14.06.2012
comment
Я использую RESTAssured версии 1.6. добавив log(), оказывается, что значение для p3 передается в запросе.   -  person Rob    schedule 18.06.2012
comment
@rob, ты нашел какое-нибудь решение для этого?   -  person Ishan Liyanage    schedule 02.06.2014


Ответы (2)


Пройдя еще немного кода трикотажа, я пришел к выводу, что у меня не может быть параметра типа List в моем методе при использовании multi-part. В какой-то момент процесса Джерси перебирает каждый параметр в методе, находя Injectable для чтения значения для каждого параметра (извините, вероятно, не очень хорошее объяснение, но я отлаживал столько, сколько мне нужно), в классе com.sun .jersey.multipart.impl.FormDataMultiPartDispatchProvider в методе getInjectables — это следующий код:

 private List<Injectable> getInjectables(AbstractResourceMethod method) {
    List<Injectable> list = new ArrayList<Injectable>(method.getParameters().size());
    for (int i = 0; i < method.getParameters().size(); i++) {
        Parameter p = method.getParameters().get(i);
        if (Parameter.Source.ENTITY == p.getSource()) {
            if (FormDataMultiPart.class.isAssignableFrom(p.getParameterClass())) {
                list.add(new FormDataMultiPartInjectable());
            } else {
                list.add(null);
            }
        } else if (p.getAnnotation().annotationType() == FormDataParam.class) {
            if (Collection.class == p.getParameterClass() || List.class == p.getParameterClass()) {
                Class c = ReflectionHelper.getGenericClass(p.getParameterType());
                if (FormDataBodyPart.class == c) {
                    list.add(new ListFormDataBodyPartMultiPartInjectable(p.getSourceName()));
                } else if (FormDataContentDisposition.class == c) {
                    list.add(new ListFormDataContentDispositionMultiPartInjectable(p.getSourceName()));
                }
            } else if (FormDataBodyPart.class == p.getParameterClass()) {
                list.add(new FormDataBodyPartMultiPartInjectable(p.getSourceName()));
            } else if (FormDataContentDisposition.class == p.getParameterClass()) {
                list.add(new FormDataContentDispositionMultiPartInjectable(p.getSourceName()));
            } else {
                list.add(new FormDataMultiPartParamInjectable(p));
            }
        } else {
            Injectable injectable = getInjectableProviderContext().getInjectable(p, ComponentScope.PerRequest);
            list.add(injectable);
        }
    }
    return list;
}

Таким образом, когда он увидит, что тип параметра является списком или коллекцией, он будет игнорировать его, если общий тип будет любым, кроме FormDataBodyPart или FormDataContentDisposition.

Чтобы обойти эту проблему, я только что изменил свой метод, чтобы принять строку с разделителями-запятыми для p3 вместо списка.

person Rob    schedule 18.06.2012

Я наткнулся на другое решение, которое может быть лучше (и проще), чем вручную обрабатывать версию списка, разделенную запятыми, и т. д. Опубликовано с опозданием, чтобы помочь другим, которые найдут этот пост.

Измените составной параметр с:

@FormDataParam("p3") List<String> p3,

to

@FormDataParam("p3") List<FormDataBodyPart> p3,

а затем у вас есть список в P3, где вы можете получить значение параметра для каждого элемента FormDataBodyPart, используя getValue().

Источник: получение массивов из -form-elements-with-Джерси

person Paul Jowett    schedule 22.01.2019