java - 尝试在 Java 中使用 HttpClient 发送 HTTP Post GraphQL 查询时出现 HTTP 400

标签 java spring-boot graphql apache-httpclient-4.x graphql-java

对外部 GraphQL 服务器进行后查询,该服务器在 Postman Pro 设置中作为预请求脚本运行。

我正在尝试使用 HttpClient 访问外部 GraphQL 服务器。

Maven 依赖项 pom.xml:

<!-- Apache Commons -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.6</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2.5</version>
</dependency>

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.6</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.2.4</version>
</dependency>

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.1</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient-cache</artifactId>
    <version>4.2.5</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.2.5</version>
</dependency>

<!-- Google Code -->
<dependency>
    <groupId>com.googlecode.json-simple</groupId>
    <artifactId>json-simple</artifactId>
    <version>1.1.1</version>
</dependency>

对预请求脚本进行硬编码,如下所示:

GetDataAsRequest.java

public class GetDataAsRequest {
    private static final String GET_DATA_REQUEST_TEMPLATE = 
    "query {" 
        + "    viewer {     "
        + "        data(id: \"10045701-2017-41a4-33f4-bf8481cf01dc\") {" + 
        "            id"
        + "            createdDate" 
        + "            lastModifiedDate" 
        + "            abbreviation"
        + "            cityStateRegion" 
        + "        }"
        + "    }"
        + "}";
    }

    public static String generateQuery() {
        return String.format(GET_DATA_REQUEST_TEMPLATE);
    }
}

我的 Controller :

public class MyController {

    private static final String EXTERNAL_API_URL = "https://sample-api.com"

    public static void main (String args []) {

        // Generate Access Token
        String accessToken = OAuth2Client.generateAccessToken();
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost postRequest = new HttpPost(EXTERNAL_API_URL);
        StringEntity postBody = new StringEntity(GetDataAsRequest.generateQuery());
        postBody.setContentType("application/json");
        postRequest.addHeader("Authorization", "Bearer " + accessToken);
        postRequest.setEntity(postBody);
        HttpResponse response = httpClient.execute(postRequest);

        if (response.getStatusLine().getStatusCode() != 201) {
            throw new RuntimeException("Failed : HTTP error code : " + response.getStatusLine().getStatusCode());
        }

        BufferedReader br = new BufferedReader(new InputStreamReader((response.getEntity().getContent())));

        String output;
        System.out.println("Output from Server .... \n");
        while ((output = br.readLine()) != null) {
            System.out.println(output);
        }
        httpClient.getConnectionManager().shutdown();
        } 
        catch (MalformedURLException e) {
            e.printStackTrace();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }

尽管当我将相同的 OAuthToken 和 HTTP post 查询从 stdin(控制台)复制到 Postman 调用中时,它可以工作,但当尝试在 Java 程序中使用 HttpClient 模拟相同的调用时,我不断收到 HTTP 400:

03:25:30.690 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Get connection for route {s}->https://https://sample-api.com
03:25:30.751 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnectionOperator - Connecting to https://sample-api.com:443
03:25:30.943 [main] DEBUG org.apache.http.client.protocol.RequestAddCookies - CookieSpec selected: best-match
03:25:30.950 [main] DEBUG org.apache.http.client.protocol.RequestAuthCache - Auth cache not set in the context
03:25:30.950 [main] DEBUG org.apache.http.client.protocol.RequestTargetAuthentication - Target auth state: UNCHALLENGED
03:25:30.951 [main] DEBUG org.apache.http.client.protocol.RequestProxyAuthentication - Proxy auth state: UNCHALLENGED
03:25:30.951 [main] DEBUG org.apache.http.impl.client.DefaultHttpClient - Attempt 1 to execute request
03:25:30.951 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Sending request: POST /v1/oauth/token HTTP/1.1
03:25:30.951 [main] DEBUG org.apache.http.wire - >> "POST /v1/oauth/token HTTP/1.1[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.wire - >> "Content-Length: 119[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.wire - >> "Content-Type: application/x-www-form-urlencoded; charset=UTF-8[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.wire - >> "Host: https://sample-api.com[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.wire - >> "Connection: Keep-Alive[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.wire - >> "User-Agent: Apache-HttpClient/4.2.5 (java 1.5)[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.wire - >> "[\r][\n]"
03:25:30.952 [main] DEBUG org.apache.http.headers - >> POST /v1/oauth/token HTTP/1.1
03:25:30.952 [main] DEBUG org.apache.http.headers - >> Content-Length: 119
03:25:30.952 [main] DEBUG org.apache.http.headers - >> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
03:25:30.952 [main] DEBUG org.apache.http.headers - >> Host: https://sample-api.com
03:25:30.952 [main] DEBUG org.apache.http.headers - >> Connection: Keep-Alive
03:25:30.953 [main] DEBUG org.apache.http.headers - >> User-Agent: Apache-HttpClient/4.2.5 (java 1.5)
03:25:30.953 [main] DEBUG org.apache.http.wire - >> "grant_type=client_credentials&client_id=fghuXeULFBdW4B1dmRY0MhROMRQnlumk&client_secret=MM2banXEq2R1GhRvIQ2d2AKRx0SORvb4"
03:25:31.132 [main] DEBUG org.apache.http.wire - << "HTTP/1.1 200 OK[\r][\n]"
03:25:31.134 [main] DEBUG org.apache.http.wire - << "X-Application-Context: application:prod[\r][\n]"
03:25:31.134 [main] DEBUG org.apache.http.wire - << "Content-Type: application/json[\r][\n]"
03:25:31.134 [main] DEBUG org.apache.http.wire - << "Cache-Control: no-store[\r][\n]"
03:25:31.134 [main] DEBUG org.apache.http.wire - << "Pragma: no-cache[\r][\n]"
03:25:31.134 [main] DEBUG org.apache.http.wire - << "Accept-Ranges: bytes[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "X-Cache-Hits: 0[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "X-Timer: S1525774921.421988,VS0,VE41[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "Date: Tue, 08 May 2018 10:22:01 GMT[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "Content-Length: 325[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "Connection: keep-alive[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "X-Sample-APP: sso[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "Access-Control-Allow-Headers: Authorization,Content-Type[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "Access-Control-Allow-Methods: PUT,POST,OPTIONS,GET,PATCH,DELETE[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "Access-Control-Allow-Origin: *[\r][\n]"
03:25:31.135 [main] DEBUG org.apache.http.wire - << "[\r][\n]"
03:25:31.136 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Receiving response: HTTP/1.1 200 OK
03:25:31.136 [main] DEBUG org.apache.http.headers - << HTTP/1.1 200 OK
03:25:31.136 [main] DEBUG org.apache.http.headers - << X-Application-Context: application:prod
03:25:31.136 [main] DEBUG org.apache.http.headers - << Content-Type: application/json
03:25:31.136 [main] DEBUG org.apache.http.headers - << Cache-Control: no-store
03:25:31.136 [main] DEBUG org.apache.http.headers - << Pragma: no-cache
03:25:31.138 [main] DEBUG org.apache.http.headers - << Accept-Ranges: bytes
03:25:31.138 [main] DEBUG org.apache.http.headers - << X-Served-By: cache-sample-las9320
03:25:31.138 [main] DEBUG org.apache.http.headers - << X-Cache-Hits: 0
03:25:31.138 [main] DEBUG org.apache.http.headers - << X-Timer: S1525774921.421988,VS0,VE41
03:25:31.138 [main] DEBUG org.apache.http.headers - << Date: Tue, 08 May 2018 10:22:01 GMT
03:25:31.138 [main] DEBUG org.apache.http.headers - << Content-Length: 325
03:25:31.138 [main] DEBUG org.apache.http.headers - << Connection: keep-alive
03:25:31.138 [main] DEBUG org.apache.http.headers - << X-Sample-APP: sso
03:25:31.138 [main] DEBUG org.apache.http.headers - << Access-Control-Allow-Headers: Authorization,Content-Type
03:25:31.138 [main] DEBUG org.apache.http.headers - << Access-Control-Allow-Methods: PUT,POST,OPTIONS,GET,PATCH,DELETE
03:25:31.138 [main] DEBUG org.apache.http.headers - << Access-Control-Allow-Origin: *
03:25:31.141 [main] DEBUG org.apache.http.impl.client.DefaultHttpClient - Connection can be kept alive indefinitely
03:25:31.147 [main] DEBUG org.apache.http.wire - << "{"access_token":"fkMmGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6bnVsbCwiZGV2aWNlSWQiOm51bGwsImNsaWVudElkIjoiaUxIdVhlVUxGQmRXNEIxZG1SWTBNaFJPTVJRbmxmZUsiLCJhZElkIjpudWxsLCJleHAiOjE1MjU3Nzg1MjEsImlhdCI6MTUyNTc3NDkyMX0.U3gacgf7a8hlwCuCn6yGXc0z6E5Zrwgii3B_77xFlyo","token_type":"Bearer","expires_in":3600,"refresh_token":null,"scope":null}"
03:25:31.147 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Releasing connection org.apache.http.impl.conn.ManagedClientConnectionImpl@2ddc8ecb
03:25:31.147 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Connection can be kept alive indefinitely
Exception in thread "main" java.lang.RuntimeException: Failed : HTTP error code : 400

这是否会返回 HTTP 400,因为我的请求正文在某种程度上是错误的?

是否有更简单的方法通过 GraphQL 的 Java 库获取 JSON 响应:

<!-- GraphQL -->
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
    <version>8.0</version>
</dependency>

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>5.0.0</version>
</dependency>

互联网上有很多示例展示了如何设置和外部 GraphQL 服务器,我需要的是学习如何使用 GraphQL Java 库作为客户端库来解析并从外部服务器获取正确的 GraphQL 响应...

这从 Postman Pro 和curl 正确返回,但不使用 HttpClient。

我可能做错了什么?

最佳答案

你控制服务器吗?如果是这样,为什么不在那里放置一个断点并检查失败的原因呢?如果您不控制服务器,我建议您仔细检查您在此处发送的内容与使用 Postman 发送的内容之间的差异。

有一件事看起来非常可疑。您正在发送一个声称具有 JSON 正文的 POST,但您发送的字符串是原始 GraphQL 查询,而不是 JSON。

我可以告诉您将请求正文转换为 GraphQL over HTTP Spec 定义的 JSON 对象。 ,但在不知道您的服务器是否实际实现此规范的情况下,这只是一个猜测。

规范定义的 POST 请求的正确正文是一个包含 3 个字段的 JSON 对象:queryoperationNamevariablesquery 字段应包含查询string(因此首先将查询字符串化),operationName 包含要执行的操作的名称,以防有多个操作(与您的情况无关),variables 包含一个 JSON 对象,其中变量作为键值对(目前不相关)。

在您的情况下,正文应该如下所示:

{
    query: "{
        viewer {
            data(id: \"10045701-2017-41a4-33f4-bf8481cf01dc\") {
                id
                createdDate
                lastModifiedDate
                abbreviation
                cityStateRegion
            }
        }
    }"
}

要将这个字符串放入 Java 代码中,所有这些都需要额外的转义级别,但我希望您的 IDE 能够处理这一点。另请注意换行符很重要。如果您由于任何原因丢失了它们,则必须在所选字段之间添加逗号以使查询保持有效。

关于java - 尝试在 Java 中使用 HttpClient 发送 HTTP Post GraphQL 查询时出现 HTTP 400,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50231767/

相关文章:

java - Spring Boot webservice JDBC偶尔无法获得连接

Java : Trying to print average temperature for each of 3 weeks using instantiable classes and arrays

java - 具有一个 namespace 和本地部分的多个 SOAP 端点

java - 如何在 Java 中实现 3Sum?

java - 无法更改 spring-boot 过滤器映射

graphql - KeystoneJS 关系,如何使用 ids 数组进行连接

java - 在枚举结构上循环时重构和删除 case 语句

java - 如何编写更新嵌入文档的查询

javascript - GraphQL 交叉引用引发错误 : field type must be Output Type but got: [object Object]

javascript - GraphQL 解析时出错 "ç": Cannot parse the unexpected character "\u00E7"