Android中用Retrofit调用Klaviyo API出现400错误求助
Retrofit调用Klaviyo批量订阅接口返回400错误排查
我在Postman中调用Klaviyo的POST接口 https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs/ 能正常返回200状态码,请求信息如下:
Header : revision:2024-07-15 content-type:application/json accept:application/json Authorization:Klaviyo-API-Key android89459832432sdskjha request body raw type : {"data":{"type":"profile-subscription-bulk-create-job","attributes":{"profiles":{"data":[{"type":"profile","attributes":{"email":"anilkt87@gmail.com"}}]}},"relationships":{"list":{"data":{"type":"list","id":"YXOSus"}}}}}
但在Android中用Retrofit实现相同请求时,返回400错误,日志显示 Failed to create subscription: okhttp3.ResponseBody$Companion$asResponseBody$1@a3bd973。我的Android代码如下:
订阅创建方法
public void createSubscription() { klaviyoService = KalvioRetrofitClient.getKlaviyoService(); String data = "{\"data\":{\"type\":\"profile-subscription-bulk-create-job\",\"attributes\":{\"profiles\":{\"data\":[{\"type\":\"profile\",\"attributes\":{\"email\":\"anilkt87@gmail.com\"}}]}},\"relationships\":{\"list\":{\"data\":{\"type\":\"list\",\"id\":\"YXOSus\"}}}}}"; JSONObject jsonObject = null; try { jsonObject = new JSONObject(data); } catch (JSONException e) { throw new RuntimeException(e); } Call<Response<String>> call = klaviyoService.createSubscription(jsonObject); call.enqueue(new Callback<Response<String>>() { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { Log.d("Klaviyo", "Subscription status: " + response); } else { Log.e("Klaviyo", "Failed to create subscription: " + response.errorBody()); } } @Override public void onFailure(Call<Response<String>> call, Throwable t) { Log.e("Klaviyo", "Error creating subscription", t); } }); }
Retrofit客户端配置
public class KalvioRetrofitClient { private static Retrofit retrofit = null; public static Retrofit getClient(String baseUrl) { if (retrofit == null) { // Set up logging interceptor (optional, for debugging) HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @NonNull @Override public Response intercept(@NonNull Chain chain) throws IOException { Request request = chain.request().newBuilder().addHeader("Content-Type", "application/json") .addHeader("accept", "application/json").addHeader("revision", "2024-07-15").addHeader("Authorization", "Klaviyo-API-Key android89459832432sdskjha").build(); return chain.proceed(request); } }) .build(); retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; } public static KlaviyoService getKlaviyoService() { return getClient("https://a.klaviyo.com/").create(KlaviyoService.class); } }
Retrofit接口定义
public interface KlaviyoService { @POST("api/profile-subscription-bulk-create-jobs") Call<Response<String>> createSubscription(@Body JSONObject klaviyoRequest); }
我确认API密钥和列表ID都是正确的,请求内容和Postman一致,想知道问题出在哪里,求解决办法。
问题排查与解决方案
1. 核心问题:JSONObject 与Gson转换器的冲突
你在Retrofit接口中使用 @Body JSONObject,但同时配置了 GsonConverterFactory。Gson转换器无法正确序列化原生 JSONObject,会导致请求体格式异常,这是400错误的主要原因。
2. 解决步骤
方案一:改用POJO类作为请求体(推荐)
创建对应Klaviyo请求结构的POJO类,让Gson自动序列化,避免手动拼接JSON的错误:
// 顶层请求类 public class SubscriptionBulkCreateRequest { private Data data; public SubscriptionBulkCreateRequest(Data data) { this.data = data; } // Getter & Setter public static class Data { private String type; private Attributes attributes; private Relationships relationships; public Data(String type, Attributes attributes, Relationships relationships) { this.type = type; this.attributes = attributes; this.relationships = relationships; } // Getter & Setter public static class Attributes { private ProfileData profiles; public Attributes(ProfileData profiles) { this.profiles = profiles; } // Getter & Setter public static class ProfileData { private List<Profile> data; public ProfileData(List<Profile> data) { this.data = data; } // Getter & Setter public static class Profile { private String type; private ProfileAttributes attributes; public Profile(String type, ProfileAttributes attributes) { this.type = type; this.attributes = attributes; } // Getter & Setter public static class ProfileAttributes { private String email; public ProfileAttributes(String email) { this.email = email; } // Getter & Setter } } } } public static class Relationships { private ListData list; public Relationships(ListData list) { this.list = list; } // Getter & Setter public static class ListData { private DataItem data; public ListData(DataItem data) { this.data = data; } // Getter & Setter public static class DataItem { private String type; private String id; public DataItem(String type, String id) { this.type = type; this.id = id; } // Getter & Setter } } } } }
修改接口和调用代码:
// KlaviyoService接口修改 public interface KlaviyoService { @POST("api/profile-subscription-bulk-create-jobs") Call<Response<String>> createSubscription(@Body SubscriptionBulkCreateRequest request); } // 创建订阅方法修改 public void createSubscription() { klaviyoService = KalvioRetrofitClient.getKlaviyoService(); // 构建请求体 SubscriptionBulkCreateRequest.ProfileAttributes profileAttr = new SubscriptionBulkCreateRequest.ProfileAttributes("anilkt87@gmail.com"); SubscriptionBulkCreateRequest.Profile profile = new SubscriptionBulkCreateRequest.Profile("profile", profileAttr); SubscriptionBulkCreateRequest.ProfileData profileData = new SubscriptionBulkCreateRequest.ProfileData(Collections.singletonList(profile)); SubscriptionBulkCreateRequest.Attributes attributes = new SubscriptionBulkCreateRequest.Attributes(profileData); SubscriptionBulkCreateRequest.DataItem listDataItem = new SubscriptionBulkCreateRequest.DataItem("list", "YXOSus"); SubscriptionBulkCreateRequest.ListData listData = new SubscriptionBulkCreateRequest.ListData(listDataItem); SubscriptionBulkCreateRequest.Relationships relationships = new SubscriptionBulkCreateRequest.Relationships(listData); SubscriptionBulkCreateRequest.Data data = new SubscriptionBulkCreateRequest.Data("profile-subscription-bulk-create-job", attributes, relationships); SubscriptionBulkCreateRequest request = new SubscriptionBulkCreateRequest(data); Call<Response<String>> call = klaviyoService.createSubscription(request); call.enqueue(new Callback<Response<String>>() { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { Log.d("Klaviyo", "Subscription status: " + response); } else { // 打印详细错误信息 try { String errorBody = response.errorBody().string(); Log.e("Klaviyo", "Failed to create subscription: " + errorBody); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<Response<String>> call, Throwable t) { Log.e("Klaviyo", "Error creating subscription", t); } }); }
方案二:改用 String 作为请求体
如果不想创建POJO类,可以直接传递JSON字符串:
// KlaviyoService接口修改 public interface KlaviyoService { @POST("api/profile-subscription-bulk-create-jobs") @Headers("Content-Type: application/json") Call<Response<String>> createSubscription(@Body String requestBody); } // 创建订阅方法修改 public void createSubscription() { klaviyoService = KalvioRetrofitClient.getKlaviyoService(); String data = "{\"data\":{\"type\":\"profile-subscription-bulk-create-job\",\"attributes\":{\"profiles\":{\"data\":[{\"type\":\"profile\",\"attributes\":{\"email\":\"anilkt87@gmail.com\"}}]}},\"relationships\":{\"list\":{\"data\":{\"type\":\"list\",\"id\":\"YXOSus\"}}}}}"; Call<Response<String>> call = klaviyoService.createSubscription(data); call.enqueue(new Callback<Response<String>>() { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { Log.d("Klaviyo", "Subscription status: " + response); } else { try { String errorBody = response.errorBody().string(); Log.e("Klaviyo", "Failed to create subscription: " + errorBody); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<Response<String>> call, Throwable t) { Log.e("Klaviyo", "Error creating subscription", t); } }); }
3. 额外调试建议
- 开启OkHttp日志拦截器,查看实际发送的请求头和请求体,与Postman对比:
OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(logging) // 启用之前定义的日志拦截器 .addInterceptor(...) // 你的Header拦截器 .build(); - 打印错误响应的具体内容,调用
response.errorBody().string()获取Klaviyo返回的详细错误原因。
内容的提问来源于stack exchange,提问作者MARSH
相关产品推荐
相关产品推荐

