创建以JSON字符串作为主体的JAX-RS客户端发布请求

bakd9h0s  于 2023-01-14  发布在  其他
关注(0)|答案(3)|浏览(144)

我正在为一个供应商REST服务编写一个REST客户端。我使用jersey 2.x和JSON-P,下面是我添加的依赖项。

<dependencies>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-processing</artifactId>
    <version>2.26</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.core</groupId>
    <artifactId>jersey-client</artifactId>
    <version>2.26</version>
</dependency>
</dependencies>

我成功地为GET请求编写了代码,并收到了JSON输出。我将其保存到一个文件中,并使用JSON-P来解释和执行自己的逻辑,没有任何问题。
但现在我需要写一个POST请求。当我使用CURL如下我能做到这一点。并希望实现相同的使用Jeresey 2.x和JSON-P。

curl -v -H "Accept:application/json" -H "Content-Type:application/json" -u user:password -X POST --databinary @./InputParameters.json https://<IP>:<PORT>/Configuration/server

InputParameters.json contents
{
"ip": "10.197.70.16",
"partNumber": 202067,
"model": "IBM P7"
}

当我试图通过响应体作为字符串在JSON格式({"ip": "10.197.70.16", "partNumber": 202067, "model": "IBM P7"}),但没有工作。所以尝试作为JsonObject如下仍然没有工作。

JsonObject jsonObj = Json.createObjectBuilder()
            .add("ip", "10.197.70.16")
            .add("partNumber", 202067)
            .add("model", "IBM P7")
            .build();

response = invocationBuilder.post(Entity.json(jsonObj));

我知道核心java,基于那次经验,我跳进写这个程序,用GET而不是POST获得了成功。我怀疑我在POST上做了一些根本性的错误。

velaa5lx

velaa5lx1#

让我们先把你正在做的事情分解一下。首先是这个部分:

JsonObject jsonObj = Json.createObjectBuilder()
    .add("ip", "10.197.70.16")
    .add("partNumber", 202067)
    .add("model", "IBM P7")
    .build();

这将创建一个javax.json.JsonObject示例。JsonObject是JSON-P Java API的一部分,它的内容大致如下:表示JSON对象的类。JsonObject示例包含javax.json.JsonValue示例的层次结构,这些示例符合更具体的类型,如JsonArray、JsonString、其他JsonObject等。在这方面,它与the classes of the DOM API for representing XML documents没有什么不同(让我们希望Oracle将API文档保留在该URL一段时间)。但幸运的是,JSON比XML和DOM简单得多。
您的jsonObj示例将包含一个JsonString示例,其值“10.197.70.16“Map到名称“ip”,一个JsonNumber示例,其值202067(可能表示为BigDecimal)Map到名称“partNumber”,等等。
接下来,您的代码执行以下命令:

Entity.json(jsonObj)

javax.ws.rs.client.Entity.json(something)基本上表示您希望创建一个实体,该实体将为内容类型为application/json的JAX-RS客户端调用提供有效负载。换句话说,您为其创建的something在发送到API时必须转换为JSON表示。它应该期望JSON有效负载并知道如何处理它。(...)有一个泛型类型参数,方法签名是static <T> Entity<t> json(T entity),所以你要创建一个Entity<JsonObject>的示例,其有效载荷实体是jsonObj
当它被移交给javax.ws.rs.client.Invocation.Builder示例的post方法时(post方法实际上是在其父接口SyncInvoker中定义的),客户端实现开始工作。

response = invocationBuilder.post(Entity.json(jsonObj));

它接受提供的实体示例及其内容(我们的jsonObj),检查实体的所需输出是什么(这是application/json),并寻找一个提供者,该提供者可以将给定类型的对象转换为该输出。换句话说,必须找到一些组件,这些组件可以被赋予javax.json.JsonObject,并将其表示形式作为JSON写入OutputStream。处理这个问题的组件可以是一个javax.ws.rs.ext.MessageBodyWriter,它声称自己可以执行这个转换,并注册到JAX-RS运行时,各种库都提供了这样的提供程序,您也可以编写自己的提供程序,这使得JAX-RS可以扩展以处理各种场景。处理非标准的输入和输出,或者让您调整它的行为。当多个提供程序能够处理给定的实体类型并生成所需的输出时,有一些规则来决定由哪一个来承担这个工作。2注意这取决于你的类路径。3有一些方法可以明确地强制执行。
客户机通过其配置使用适当的URL、查询参数、HTTP方法有效负载是通过将实体以所需格式写入OutputStream来创建的。在您的示例中,这将导致到服务器的POST。调用完成后,您将收到一个javax.ws.rs.core.Response,可以使用它来确定HTTP结果代码并检索响应负载,Response的readEntity(Class<T> entityType)方法的工作原理与将实体转换为有效负载的过程相反,它搜索一个MessageBodyReader,该MessageBodyReader可以根据response.getMediaType()返回的值解释响应流,并可以从中创建类entityType的示例。
那么,在解释了所有这些之后,您的方法究竟出了什么问题呢?嗯,问题是您的JAX-RS运行时可用的默认实现可能没有专门针对类型为JsonObject的输入和预期输出application/json的编写器。如果服务器预期JSON,这看起来非常合乎逻辑,您应该能够提供一个JsonObject作为有效负载。但是如果JAX-RS实现找不到处理该类的方法,那么它最多只能使用一些默认的方法,在这种情况下,它可能会尝试将对象解释为POJO,并以默认的方式将其序列化为JSON,这可能会导致如下的奇怪情况:

{
    "valueMap": {
        "ip": {
            "value": "10.197.70.16"
        },
        "partNumber": {
            "num": 202067,
            "integral": TRUE
        },
        ...
    }
}

这就是JsonObject示例的字面解释,具体取决于它使用的实现以及JAX-RS使用什么将其转换为JSON输出。当然,对象可能根本无法序列化为JSON,要么是因为找不到合适的MessageBodyWriter,要么是在创建输出时遇到错误。
第一个解决方案非常简单,只需将JsonObject转换为String,并将其作为实体提供即可:

StringWriter stringWriter = new StringWriter();
try (JsonWriter jsonWriter = Json.createWriter(stringWriter);) {
    jsonWriter.writeObject(jsonObject);
} // some error handling would be needed here
String jsonPayload = stringWriter.toString();
response = invocationBuilder.post(Entity.json(jsonPayload));

看起来你已经尝试过了。一个可能的问题是,当使用String作为输出,application/json作为所需的内容类型时,使用的MessageBodyWriter只需要以合适的编码(可能是UTF-8)输出String的字节。有些可能不会这样做。你可以尝试Entity.entity(jsonPayload, MediaType.TEXT_PLAIN_TYPE.withCharset("UTF-8")),但服务器可能会拒绝调用。
其他可能的解决方案包括

  • 为String对象编写自己的MessageBodyWriter,并在其上添加@javax.ws.rs.Produces(MediaType.APPLICATION_JSON)注解。

  • 更好的方法是编写这样一个接受JsonObject示例的类。

  • 为JSON结构创建POJO类,并让它们用于从示例生成JSON或将JSON响应反序列化到示例。

  • 查找一个扩展库,其中包含处理javax.json类的合适提供程序。

将com.owlike:genson依赖项添加到项目中正是最后一个建议的应用。Genson库提供Java对象和JSON之间的双向转换以及数据绑定。它的部分代码库专用于JAX-RS提供程序,其中一个类适合接受JsonObject作为输入。

guz6ccqo

guz6ccqo2#

添加以下依赖项后问题得到解决。此时我不确定它的作用。
感谢Swamy(TCS)的支持来解决这个问题。

<dependency>
 <groupId>com.owlike</groupId>
 <artifactId>genson</artifactId>
 <version>1.4</version>
</dependency>
hc8w905p

hc8w905p3#

使用genson的示例

String serialize = new Genson().serialize(
        Json.createObjectBuilder()
        .add("ip", "10.197.70.16")
        .add("partNumber", 202067)
        .add("model", "IBM P7")
        .build()
    );
response = invocationBuilder.post(Entity.json(serialize));

相关问题