接受List作为消耗多部分内容类型的Jersey Web服务的参数

时间:2012-06-13 17:43:18

标签: java web-services jersey multipart rest-assured

我有一个现有的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 {

这是正常工作的,当List中传递的多个p3参数由Jersey正确生成并传递给方法时。

我现在需要制作一个可接受多部分请求的此方法的替代版本,以便也可以将文件与现有参数一起上传。所以我创建了一个非常相似的方法签名来使用多部分请求,如下所示。

@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,但得到相同的结果。

在此先感谢,任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:3)

在介绍了更多的球衣代码后,我得出的结论是,在使用多部分时,我的方法上不能有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。

答案 1 :(得分:0)

我遇到了另一种解决方案,它可能比手动处理列表的逗号分隔版本等更好(更简单)。过时发布以帮助其他人找到该帖子。

从以下位置更改multipart参数:

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

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

然后在P3中有您的列表,您可以在其中使用FormDataBodyPart获得每个getValue()元素的参数值。

来源:receiving-arrays-from-form-elements-with-jersey