找不到媒体类型的球衣客户端 MessageBodyWriter=application/x-www-form-urlencoded

jersey-client MessageBodyWriter not found for media type=application/x-www-form-urlencoded

使用 org.glassfish.jersey.core.jersey-客户端版本 2.16,似乎无法发出简单的 POST 请求:

MessageBodyProviderNotFoundException:找不到媒体类型的 MessageBodyWriter=application/x-www-form-urlencoded

客户端

String response = ClientBuilder.newClient().target("http://0.0.0.0:8080")
        .path("/get")
        .request(MediaType.APPLICATION_JSON_TYPE)
        .post(Entity.entity(getForm(), MediaType.APPLICATION_FORM_URLENCODED_TYPE), String.class);

private Form getForm()
{
    return new Form() {{ this.param("whatever", "whatever"); }};
}

服务器

@Path("/get")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String postForToken(@FormParam("whatever") final String whatever) {...}

根据 this post 注册 MultiPartFeature 没有任何区别。

config.register(MultiPartFeature.class);

好的,所以线索在完整的异常消息中(上面没有发布)。

org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/x-www-form-urlencoded, type=class com.blah.AcceptanceTest, genericType=class com.blah.AcceptanceTest.

$1 表示它正在尝试解析某种内部 class - 这使我找到了 Form 的匿名子 class。将上面的内容更改为以下内容可以正常工作:

private Form getForm()
{
    final Form form = new Form();
    form.param("whatever", "whatever");
    return form;
}

Jersey 使用 Entity Providers 序列化和反序列化 Java 个对象——它使用 MessageBodyWriter 序列化和 MessageBodyReader 反序列化。一些实体提供程序是开箱即用的,包括 FormProvider,它处理媒体类型 application/x-www-form-urlencodedForm 对象的读取和写入。

Jersey 文档的

Section 8.3 概述了在决定使用哪个注册提供者时使用的算法。第 5 步是这样说的:

  1. Iterate through the sorted MessageBodyWriter providers and, utilizing the isWriteable method of each until you find a MessageBodyWriter that returns true.

问题是 FormProvider 中的 implementation of isWriteable() 检查对象的运行时类型是否 完全 等于 Form.class -- 子classes,包括匿名classes,不合格,所以方法returns falseFormProvider 不用于序列化。然后,没有找到其他匹配的提供者,结果是 MessageBodyProviderNotFoundException.

简单的解决方案:

简单的解决方案是只使用实际的 Form class。

硬解:

对于任何真正想要使用 Form 的子classes 的人来说,困难的解决方案是重新实现 FormProvider(标记为 final,因此您可以不只是 subclass 它),用下面的代码代替 isWriteable() 方法:

public boolean isWriteable(...) {
    return Form.class.isAssignableFrom(type);
}

然后您必须在使用前向 Client 注册您的自定义 FormProvider:

Client client = ClientBuilder.newClient().register(MyFormProvider.class);