使用Retrofit调用AWS Appsync GraphQL接口返回400错误如何解决?
问题根因排查
你当前的代码存在两个核心错误,是导致400错误的直接原因:
- Content-Type和请求Body格式不匹配:如果设置
Content-Type: application/graphql,请求Body应该直接是GraphQL查询字符串,不需要外层包裹{"query": ...}的JSON结构;如果要传JSON结构的请求体,Content-Type应该改为application/json。AWS AppSync本身同时兼容两种请求格式,但你的Android代码把两种格式混用,导致服务端解析失败。 - Retrofit String类型Body的序列化错误:你配置了
GsonConverterFactory,当你传入String类型的对象作为@Body时,Gson会自动给字符串添加双引号、转义内部特殊字符,最终发出的请求体不是你预期的结构,而是被二次转义的字符串。
方案1:使用application/json格式请求(推荐,和NodeJS逻辑完全对齐)
1. 修改GraphqlService接口
public interface GraphqlService { @Headers({ "Content-Type: application/json", "x-api-key: SOME_API_KEY" }) @POST("/graphql") // 改用RequestBody避免Gson二次序列化 Call<String> getLockers(@Body RequestBody body); }
2. 修改请求发起逻辑
retrofit = new Retrofit.Builder() .baseUrl("https://amazon.endpoint.com") .addConverterFactory(GsonConverterFactory.create()) .build(); GraphqlService service = retrofit.create(GraphqlService.class); try { JSONObject paramObject = new JSONObject(); String query = "query { listData { dataName } }"; paramObject.put("query", query); // 直接构建JSON类型的RequestBody,跳过Gson二次处理 RequestBody body = RequestBody.create( paramObject.toString(), MediaType.parse("application/json; charset=utf-8") ); service.getLockers(body) .enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { System.out.println(response); } @Override public void onFailure(Call<String> call, Throwable t) { System.out.println(t); } }); } catch (JSONException e) { e.printStackTrace(); }
可选优化
可以封装请求实体类避免每次手动拼接JSON:
public class GraphqlRequest { private String query; // 有查询变量的话可以再加个Map<String, Object> variables字段 public GraphqlRequest(String query) { this.query = query; } // 按需添加get、set方法 }
接口定义改为Call<String> getLockers(@Body GraphqlRequest request);,调用时直接传入实体类对象即可,Gson会自动序列化为正确的JSON结构,代码更简洁。
方案2:使用application/graphql格式请求
如果要保持Content-Type为application/graphql,直接传查询字符串即可,不需要外层JSON包裹:
1. 修改GraphqlService接口
public interface GraphqlService { @Headers({ "Content-Type: application/graphql", "x-api-key: SOME_API_KEY" }) @POST("/graphql") Call<String> getLockers(@Body RequestBody body); }
2. 修改请求发起逻辑
// 直接传查询字符串作为Body RequestBody body = RequestBody.create( "query { listData { dataName } }", MediaType.parse("application/graphql; charset=utf-8") ); // 其余请求逻辑和方案1一致
调试建议
如果修改后依然报错,可以接入OkHttp的LoggingInterceptor拦截器,设置日志级别为BODY,打印完整的请求头、请求体内容,和curl的请求内容做对比,就能快速定位差异点。
内容的提问来源于stack exchange,提问作者Trayson Keli'i
相关产品推荐
相关产品推荐

