将列表作为参数传递给一个Jersey webservice,该webservice消耗多部分内容类型。

3

我有一个已经存在的Jersey webservice方法,通过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参数时,Jersey会正确生成List并将其传递到方法中。

现在我需要创建该方法的另一种版本,以便接受多部分请求,这样文件就可以与现有参数一起上传。因此,我创建了一个非常类似的方法签名来处理多部分请求,示例如下。

@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,因为在处理多部分数据时需要这样做。我一直在试图使用RESTAssured从JUnit测试中调用此方法进行调用(与原始方法相同),但我收到以下错误。

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)

将一些断点放入Jersey代码中,在堆栈跟踪中标识出的一些点,它似乎已经确定了要调用的正确方法,但在试图传递给它的参数列表中省略了p3。
处理多部分数据时,是否需要执行其他不同的操作来支持接受List作为输入?考虑到这是可选参数,我认为无论如何应该可以省略它,这也是原始方法的情况。
测试中用于调用该方法的RESTAssured代码如下。
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()

我也尝试在RESTAssured测试代码中使用formParam替换param,但结果相同。

非常感谢,任何帮助都将不胜感激。


1
你正在使用哪个版本的RESTAssured?如果版本号大于1.5,则在given()之后添加log(),以查看你的请求。 - Alex Stybaev
我正在使用RESTAssured版本1.6。添加了log()后,似乎p3的值被传递到请求中。 - Rob
@rob,你找到解决方案了吗? - Ishan Liyanage
2个回答

3

在深入了解了一些jersey代码后,我的结论是:当使用multi-part时,我不能在方法中拥有一个List类型的参数。 在整个过程中,Jersey会循环遍历方法中的每个参数,找到一个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;
}

当参数类型为List或Collection时,如果其泛型类型不是FormDataBodyPart或FormDataContentDisposition,它会忽略它。

为了解决这个问题,我只需将我的方法更改为接受逗号分隔的字符串作为p3,而不是一个List。


0
我发现另一种解决方法可能比手动处理逗号分隔列表等更好(并且更简单)。发布此贴以帮助其他人。

将多部分参数从以下内容更改:

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

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

然后您可以在P3中获取每个FormDataBodyPart元素的参数值,使用getValue()

来源:使用Jersey从表单元素接收数组


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接