使用Retrofit调用WCF POST接口遭遇400错误:Bad Request
Retrofit POST请求返回400 Bad Request问题排查与解决
兄弟,我看了你这个问题,Postman和Volley都能正常请求,唯独Retrofit一直返回400 Bad Request,研究两天没解决太闹心了!我帮你瞅了下代码,核心问题大概率是请求格式不匹配——你说要以JSON数据调用API,但你的Retrofit代码用的是表单格式发送请求,这就和接口期望的格式对不上,自然返回400了。
先给你分析下问题所在:你的ApiInterface用了@FormUrlEncoded和@Field注解,这会让Retrofit发送application/x-www-form-urlencoded格式的表单数据,但如果你的后端接口期望的是application/json格式的请求体,那肯定会报错。而Postman和Volley你应该是按JSON格式发的,所以能正常请求。
下面是具体的解决方案,一步步来:
1. 创建请求参数的POJO类
我们需要把请求参数封装成一个对象,方便Retrofit转成JSON:
public class UserLoginRequest { @SerializedName("Mobile_no") @Expose private String mobileNo; @SerializedName("Password") @Expose private String password; @SerializedName("RegID") @Expose private String regID; // 构造方法 public UserLoginRequest(String mobileNo, String password, String regID) { this.mobileNo = mobileNo; this.password = password; this.regID = regID; } // Getter和Setter方法 public String getMobileNo() { return mobileNo; } public void setMobileNo(String mobileNo) { this.mobileNo = mobileNo; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRegID() { return regID; } public void setRegID(String regID) { this.regID = regID; } }
2. 修改ApiInterface接口
把原来的表单注解换成@Body,让Retrofit发送JSON请求体:
public interface ApiInterface { @POST("UserLogin") Call<SignUpResponse> registration(@Body UserLoginRequest loginRequest); }
3. 调整MainActivity的调用代码
现在需要传入封装好的请求对象,而不是单个参数:
// 封装请求参数 UserLoginRequest loginRequest = new UserLoginRequest("xxxNumberhere", "rd", "222"); Api.getClient().registration(loginRequest).enqueue(new Callback<SignUpResponse>() { @Override public void onResponse(Call<SignUpResponse> call, Response<SignUpResponse> response) { progressDialog.dismiss(); if (response.isSuccessful()) { signUpResponsesData = response.body(); Toast.makeText(getApplicationContext(), signUpResponsesData.getUserLoginResult().getMessage(), Toast.LENGTH_SHORT).show(); } else { // 打印错误响应内容,方便排查问题 try { Log.e("Retrofit Error", "Response Code: " + response.code() + ", Error Body: " + response.errorBody().string()); } catch (IOException e) { e.printStackTrace(); } Toast.makeText(getApplicationContext(), "请求失败,状态码:" + response.code(), Toast.LENGTH_SHORT).show(); } } @Override public void onFailure(Call<SignUpResponse> call, Throwable t) { Log.d("Retrofit Failure", t.getMessage()); progressDialog.dismiss(); Toast.makeText(getApplicationContext(), "网络请求失败:" + t.getMessage(), Toast.LENGTH_SHORT).show(); } });
4. 可选:添加日志拦截器排查请求细节
如果修改后还是有问题,建议添加OkHttp的日志拦截器,查看Retrofit发送的实际请求内容,和Postman的请求对比,就能快速找到差异:
首先添加依赖到你的build.gradle:
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
然后修改你的Api类,配置日志拦截器:
public class Api { private static Retrofit retrofit = null; public static ApiInterface getClient() { // 创建日志拦截器,打印完整请求响应 HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); // 配置OkHttpClient OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging) .build(); if (retrofit == null) { retrofit = new Retrofit.Builder() .baseUrl("xxxx/Service1.svc/") .client(client) // 添加自定义OkHttpClient .addConverterFactory(GsonConverterFactory.create()) .build(); } ApiInterface api = retrofit.create(ApiInterface.class); return api; } }
这样你就能在Logcat里看到完整的请求头、请求体、响应头和响应体,和Postman里的请求做对比,任何参数、格式的差异都能一目了然。
额外排查点
- 确认你的
baseUrl是否正确,拼接后的完整请求地址要和Postman里的完全一致(比如末尾的斜杠是否正确) - 检查请求参数的键名(比如
Mobile_no、Password)是否和后端接口要求的完全一致,大小写、下划线都不能错 - 确保你的GsonConverter配置正确,Retrofit能正常把对象转成JSON
内容的提问来源于stack exchange,提问作者Rajesh Naddy
相关产品推荐
相关产品推荐

