Jersey多部分表单数据默认值?

5

我有一个接受多部分表单数据类型的方法。其中一个参数是一个file表单数据参数,如下所示:

@FormDataParam("file") InputStream inputStream,
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader

我希望有时可以在不提供表单数据参数file的情况下访问此端点,但是现在当我省略它时,该方法会立即返回400错误请求。是否有一种方法可以设置它,以便我可以省略它?或者是否有一种方法可以为此设置默认值(例如null)?任何帮助都将不胜感激。我的方法声明如下:

@POST
@Path("/publish")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response publish(@Auth Key key,
                        @QueryParam("email") String email,
                        @HeaderParam("password") String password,
                        @QueryParam("type") PublishType type,
                        @QueryParam("message") String message,
                        @FormDataParam("file") InputStream inputStream,
                        @FormDataParam("file") FormDataContentDisposition
                              contentDispositionHeader,
                        @FormDataParam("title") @DefaultValue("") String videoTitle) {
    // code here
}

最后,我想创建一个终端,用户可以将文本发布到数据库中,并可选择包含图像或某种类型的媒体。如果有其他方法可以实现这一点,请告诉我。
谢谢!

我无法重现这个问题。您使用的是哪个Jersey版本? - Paul Samsotha
@peeskillet 我相信我正在使用2.23.2版本。但我认为你的示例中的区别可能在于你仍然在请求中发布表单数据。对于我的情况,当我尝试仅使用查询参数和标头参数命中端点时,它会返回400。 - Rohan
如果是POST请求,将电子邮件和其他内容作为查询参数并没有太多意义。为什么不将它们添加为表单字段呢?这就是multipart的用途所在。 - Paul Samsotha
@peeskillet 我试图保持与资源中其他方法的一致性,那些东西都是查询参数。我想尽可能保持一致的API。 - Rohan
2个回答

3

虽然不太美观,但如果您不想发布任何正文,我能想到的唯一方法就是使用Jersey的ContainerRequest,您可以将其注入到方法中。然后,如果有任何正文,请将多部分作为FormDataMultiPart获取并手动遍历各个部分。

@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String post(@Context ContainerRequest request) {
    final String contentLength = request.getHeaderString(HttpHeaders.CONTENT_LENGTH);
    if (contentLength != null && Integer.parseInt(contentLength) != 0) {
        FormDataMultiPart multiPart = request.readEntity(FormDataMultiPart.class);
        FormDataBodyPart part = multiPart.getField("test");
        String result = part.getValueAs(String.class);
        return result;
    }
    return "no body";
}

以下是完整的测试。
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.media.multipart.*;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import javax.ws.rs.*;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import java.util.logging.Logger;

import static org.junit.Assert.assertEquals;

/**
 * Example with default value for multipart field.
 *
 * Dependencies for JUnit test.
 *
 *   <dependency>
 *     <groupId>org.glassfish.jersey.test-framework.providers</groupId>
 *     <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
 *     <scope>test</scope>
 *     <version>${jersey2.version}</version>
 *   </dependency>
 *   <dependency>
 *     <groupId>org.glassfish.jersey.media</groupId>
 *     <artifactId>jersey-media-multipart</artifactId>
 *     <version>${jersey2.version}</version>
 *  </dependency>
 */
public class MultiPartMissingTest extends JerseyTest {

    @Path("test")
    public static class TestResource {

        @POST
        @Produces(MediaType.TEXT_PLAIN)
        @Consumes(MediaType.MULTIPART_FORM_DATA)
        public String post(@Context ContainerRequest request) {
            final String contentLength = request.getHeaderString(HttpHeaders.CONTENT_LENGTH);
            if (contentLength != null && Integer.parseInt(contentLength) != 0) {
                FormDataMultiPart multiPart = request.readEntity(FormDataMultiPart.class);
                FormDataBodyPart part = multiPart.getField("test");
                String result = part.getValueAs(String.class);
                return result;
            }
            return "no body";
        }
    }


    @Override
    public ResourceConfig configure() {
        return new ResourceConfig()
                .register(TestResource.class)
                .register(MultiPartFeature.class)
                .register(new LoggingFilter(Logger.getAnonymousLogger(), true))
                .register(new ExceptionMapper<Throwable>() {
                    @Override
                    public Response toResponse(Throwable t) {
                        t.printStackTrace();
                        return Response.serverError().entity(t.getMessage()).build();
                    }
                });
    }

    @Override
    public void configureClient(ClientConfig config) {
        config.register(MultiPartFeature.class);
    }

    @Test
    public void testWithBody() {
        final MultiPart multiPart = new FormDataMultiPart()
                .field("test", "testing");

        final Response response = target("test")
                .request()
                .post(Entity.entity(multiPart, multiPart.getMediaType()));

        assertEquals(200, response.getStatus());
        assertEquals("testing", response.readEntity(String.class));
    }

    @Test
    public void withoutBody() {
        final Response response = target("test")
                .request()
                .post(null);

        assertEquals(200, response.getStatus());
        assertEquals("no body", response.readEntity(String.class));
    }
}

这回答了我的问题,但你说得对,它不太美观!我可能需要重新考虑我的设计。不过还是谢谢你,这非常有帮助。 - Rohan

1
您可以简单地说明文件参数不是必需的。请参见下文:
@POST
@Path("/publish")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response publish(
        @Multipart("key") Key key,
        @Multipart(value = "file", required = false) Attachment file) {}

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