Retrofit 2 - POST 请求变成了 GET?
Retrofit 2 - POST request became GET?
我的 POST
请求一直作为 GET
发送并被 API 端点拒绝
我的服务class
@FormUrlEncoded
@POST("api/users/")
Call<List<User>> getUsers(@FieldMap HashMap<String, String> parameters);
请求代码
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.build();
mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
mResponse = response.body();
mResponseObserver.onFinish(mResponse);
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
mResponseObserver.onFailure();
}
});
但是当它以 GET
请求形式到达服务器时服务器拒绝了它!?使用调试器检查后,我看到:
rawResponse.request.method = GET
这里是 watch window 的屏幕截图,显示了改造的请求对象:
如您所见,请求方法是GET
。但奇怪的部分是在 tag
中,它显示了一个带有 POST
方法的请求对象?
我是不是错过了什么?
更新
我添加了日志拦截器,这是日志:
D/OkHttp: --> POST http://***/api/users/ http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 56
D/OkHttp: --> END POST
D/OkHttp: <-- 200 OK https://***/api/users/ (388ms)
D/OkHttp: Date: Thu, 01 Sep 2016 11:50:23 GMT
D/OkHttp: Server: Apache
D/OkHttp: X-Powered-By: PHP/5.4.34
D/OkHttp: Cache-Control: max-age=2592000
D/OkHttp: Expires: Sat, 01 Oct 2016 11:50:23 GMT
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: Content-Type: application/json; charset=UTF-8
D/OkHttp: Set-Cookie: SESSION_DEFAULT=***; expires=Sun, 04-Sep-2016 11:50:24 GMT; path=/; HttpOnly
D/OkHttp: Set-Cookie: COOKIE[***]=***; path=/; httponly
D/OkHttp: Connection: close
D/OkHttp: <-- END HTTP
看起来请求是 POST
。但是,服务器仍然返回错误消息,说请求方法是 GET
嗯,我再深入一点。
你能试试吗
@POST("/api/users/")
"api"前加斜杠?
另外,用 Fiddler 或 smth.
仔细检查请求会很有帮助
我通过日志检查了您的程序,并确认 POST
请求已发送。
我建议你这样检查:
class TestClass {
private void testRequest() {
HashMap<String, String> mParameters = new HashMap<>();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging); // <-- this is the important line!
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.client(httpClient.build())
.build();
MyService mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
}
});
}
interface MyService {
@FormUrlEncoded
@POST("api/users/")
Call<List<User>> getUsers(@FieldMap HashMap<String, String> parameters);
}
class User {
}
class Constants {
public static final String API_ENDPOINT_TEST_URL = "http://.........";
}
}
这里是请求日志:
D/OkHttp: --> POST http://......... http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 0
D/OkHttp: --> END POST
这是回复:
D/OkHttp: <-- 200 OK http://.............../ (167ms)
D/OkHttp: Server: nginx
D/OkHttp: Date: Thu, 01 Sep 2016 11:30:32 GMT
D/OkHttp: Content-Type: text/html; charset=UTF-8
D/OkHttp: Connection: close
....
D/OkHttp: <-- END HTTP
我遇到了类似的问题。就我而言,我收到了对 301 的响应,重定向和方法更改 POST -> GET.
首先,检查您的 BASE_URL,它的格式必须是 "h ttps://www.YourSite.com/" www - 非常重要。如果一切就绪,问题依然存在,那么您可以按如下所述进行更改:
从 okhttp wiki and based on this image 开始,您需要更改如下内容:
...
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
final RequestBody requestBody = new FormBody.Builder().build();
httpClient.addNetworkInterceptor(new Interceptor() {
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl.Builder builder = request.url().newBuilder();
HttpUrl url = builder.build();
request = request.newBuilder()
.url(url)
.post(requestBody)
.build();
return chain.proceed(request);
}
})
httpClient.addInterceptor(logging);
...
实际上,问题是 2 个因素的组合:
- 使用了错误的请求协议(http 而不是 https)
- 服务器在错误的协议上响应了一条奇怪的消息:"GET is not supported"。
总之,感谢@nshmura 的小助手。
这是部分正确的 http 客户端行为。根据the HTTP spec
If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
有时无法更改后端 API。因此,这里有一个解决方法,您可以将拦截器添加到拦截器中,以使用正确的方法手动重定向请求。
fun forceRedirectMethod(chain: Interceptor.Chain, originResponse: Response): Response {
/* TODO
one more request is made; disable followRedirect and make it manually?
*/
if (originResponse.priorResponse()?.code() == 302) {
val priorResponse: Response = originResponse.priorResponse() as Response
val redirectRequest = priorResponse.request()
val builder = originResponse.request().newBuilder()
.method(redirectRequest.method(), redirectRequest.body())
val newRequest = builder.build()
return chain.proceed(newRequest)
} else {
return originResponse
}
}
我的 POST
请求一直作为 GET
发送并被 API 端点拒绝
我的服务class
@FormUrlEncoded
@POST("api/users/")
Call<List<User>> getUsers(@FieldMap HashMap<String, String> parameters);
请求代码
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.build();
mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
mResponse = response.body();
mResponseObserver.onFinish(mResponse);
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
mResponseObserver.onFailure();
}
});
但是当它以 GET
请求形式到达服务器时服务器拒绝了它!?使用调试器检查后,我看到:
rawResponse.request.method = GET
这里是 watch window 的屏幕截图,显示了改造的请求对象:
如您所见,请求方法是GET
。但奇怪的部分是在 tag
中,它显示了一个带有 POST
方法的请求对象?
我是不是错过了什么?
更新
我添加了日志拦截器,这是日志:
D/OkHttp: --> POST http://***/api/users/ http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 56
D/OkHttp: --> END POST
D/OkHttp: <-- 200 OK https://***/api/users/ (388ms)
D/OkHttp: Date: Thu, 01 Sep 2016 11:50:23 GMT
D/OkHttp: Server: Apache
D/OkHttp: X-Powered-By: PHP/5.4.34
D/OkHttp: Cache-Control: max-age=2592000
D/OkHttp: Expires: Sat, 01 Oct 2016 11:50:23 GMT
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: Content-Type: application/json; charset=UTF-8
D/OkHttp: Set-Cookie: SESSION_DEFAULT=***; expires=Sun, 04-Sep-2016 11:50:24 GMT; path=/; HttpOnly
D/OkHttp: Set-Cookie: COOKIE[***]=***; path=/; httponly
D/OkHttp: Connection: close
D/OkHttp: <-- END HTTP
看起来请求是 POST
。但是,服务器仍然返回错误消息,说请求方法是 GET
嗯,我再深入一点。
你能试试吗
@POST("/api/users/")
"api"前加斜杠?
另外,用 Fiddler 或 smth.
仔细检查请求会很有帮助我通过日志检查了您的程序,并确认 POST
请求已发送。
我建议你这样检查:
class TestClass {
private void testRequest() {
HashMap<String, String> mParameters = new HashMap<>();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging); // <-- this is the important line!
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.client(httpClient.build())
.build();
MyService mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
}
});
}
interface MyService {
@FormUrlEncoded
@POST("api/users/")
Call<List<User>> getUsers(@FieldMap HashMap<String, String> parameters);
}
class User {
}
class Constants {
public static final String API_ENDPOINT_TEST_URL = "http://.........";
}
}
这里是请求日志:
D/OkHttp: --> POST http://......... http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 0
D/OkHttp: --> END POST
这是回复:
D/OkHttp: <-- 200 OK http://.............../ (167ms)
D/OkHttp: Server: nginx
D/OkHttp: Date: Thu, 01 Sep 2016 11:30:32 GMT
D/OkHttp: Content-Type: text/html; charset=UTF-8
D/OkHttp: Connection: close
....
D/OkHttp: <-- END HTTP
我遇到了类似的问题。就我而言,我收到了对 301 的响应,重定向和方法更改 POST -> GET.
首先,检查您的 BASE_URL,它的格式必须是 "h ttps://www.YourSite.com/" www - 非常重要。如果一切就绪,问题依然存在,那么您可以按如下所述进行更改:
从 okhttp wiki and based on this image 开始,您需要更改如下内容:
...
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
final RequestBody requestBody = new FormBody.Builder().build();
httpClient.addNetworkInterceptor(new Interceptor() {
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl.Builder builder = request.url().newBuilder();
HttpUrl url = builder.build();
request = request.newBuilder()
.url(url)
.post(requestBody)
.build();
return chain.proceed(request);
}
})
httpClient.addInterceptor(logging);
...
实际上,问题是 2 个因素的组合:
- 使用了错误的请求协议(http 而不是 https)
- 服务器在错误的协议上响应了一条奇怪的消息:"GET is not supported"。
总之,感谢@nshmura 的小助手。
这是部分正确的 http 客户端行为。根据the HTTP spec
If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
有时无法更改后端 API。因此,这里有一个解决方法,您可以将拦截器添加到拦截器中,以使用正确的方法手动重定向请求。
fun forceRedirectMethod(chain: Interceptor.Chain, originResponse: Response): Response {
/* TODO
one more request is made; disable followRedirect and make it manually?
*/
if (originResponse.priorResponse()?.code() == 302) {
val priorResponse: Response = originResponse.priorResponse() as Response
val redirectRequest = priorResponse.request()
val builder = originResponse.request().newBuilder()
.method(redirectRequest.method(), redirectRequest.body())
val newRequest = builder.build()
return chain.proceed(newRequest)
} else {
return originResponse
}
}