You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:39:52