在泽西servlet方法中使用MULTIPART_FORM_DATA和application / x-www-form-urlencoded媒体类型

时间:2016-03-16 13:05:25

标签: jersey jax-rs media-type

我在jersey servlet中有一个方法,它使用MULTIPART_FORM_DATA和application / x-www-form-urlencoded媒体类型,在我的请求中,我发送了一些参数以及文件输入流中的文件。

这是我的方法

@POST
@Path("/upload")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_FORM_URLENCODED})
@Produces(MediaType.TEXT_PLAIN)
public String uploadFile(MultivaluedMap<String,String> requestParamsPost,@FormDataParam("file") InputStream fis,
@FormDataParam("file") FormDataContentDisposition fdcd){

//some code goes here

}

但我的问题是当我在web.xml中进行servlet映射后启动服务器时,我遇到了一些严重的异常

SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.package.ImportService.uploadFile(java.lang.String,java.lang.String,java.lang.String) at parameter at index 0
SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.package.ImportService.uploadFile(java.lang.String,java.lang.String,java.lang.String) at parameter at index 1
SEVERE: Missing dependency for method public javax.ws.rs.core.Response com.package.ImportService.uploadFile(java.lang.String,java.lang.String,java.lang.String) at parameter at index 2

在单个端点上以一种方法使用两种媒体类型是否可行? 发送文件每个请求都需要参数吗?

1 个答案:

答案 0 :(得分:0)

错误的原因是MultivaluedMap参数。泽西岛不知道该怎么做。每个方法只能有一个实体类型。在您的方法中,您尝试在请求中接受两种不同的体型。你不能这样做。我甚至不知道你打算如何从客户那里发送它。

application/x-www-form-urlencoded数据需要成为多部分正文的一部分。所以你可以做到

@Consumes({MediaType.MULTIPART_FORM_DATA})
public String uploadFile(@FormDataMultiPart("form-data") MultivaluedMap<String,String> form,
                         @FormDataParam("file") InputStream fis,
                         @FormDataParam("file") FormDataContentDisposition fdcd){

那会有用。唯一的问题是,您需要确保客户端设置Content-Type部分的form-data。如果他们不这样做,那么默认的Content-Type for that part will be text / plain and Jersey will not be able to parse it to a MultivaluedMap`。

您可以做的只是使用FormDataBodyPart作为方法参数,然后显式设置媒体类型。然后,您可以将其提取到MultivaluedMap。这样,客户端不需要为该部分设置Content-Type。有些客户甚至不允许设置单独的零件类型。

以下是使用Jersey Test Framework

的示例
import java.util.logging.Logger;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;

import org.junit.Test;
import static junit.framework.Assert.assertEquals;

public class MultipartTest extends JerseyTest {

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

        @POST
        @Consumes(MediaType.MULTIPART_FORM_DATA)
        public Response post(@FormDataParam("form-data") FormDataBodyPart bodyPart, 
                             @FormDataParam("data") String data) {
            bodyPart.setMediaType(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
            MultivaluedMap<String, String> formData = bodyPart.getEntityAs(MultivaluedMap.class);
            StringBuilder sb = new StringBuilder();
            sb.append(data).append(";").append(formData.getFirst("key"));

            return Response.ok(sb.toString()).build();
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(MultiPartResource.class)
                .register(MultiPartFeature.class)
                .register(new LoggingFilter(Logger.getAnonymousLogger(), true));
    }

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

    @Test
    public void doit() {
        FormDataMultiPart multiPart = new FormDataMultiPart();
        multiPart.field("data", "hello");
        multiPart.field("form-data", "key=world");
        final Response response = target("test")
                .request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA));
        assertEquals("hello;world", response.readEntity(String.class));
    }
}

如果查看日志记录,您会看到请求为

--Boundary_1_323823279_1458137333706
Content-Type: text/plain
Content-Disposition: form-data; name="data"

hello
--Boundary_1_323823279_1458137333706
Content-Type: text/plain
Content-Disposition: form-data; name="form-data"

key=world
--Boundary_1_323823279_1458137333706--

您可以看到Content-Type正文部分form-datatext/plain,这是默认设置,但在服务器端,我们在Jersey解析它之前明确设置它

public Response post(@FormDataParam("form-data") FormDataBodyPart bodyPart, 
                     @FormDataParam("data") String data) {
    bodyPart.setMediaType(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
    MultivaluedMap<String, String> formData = bodyPart.getEntityAs(MultivaluedMap.class);