最近我开始使用 Requfit 2,并且遇到了解析空响应主体的问题。我有一个服务器,它只响应 http 代码,没有任何内容在响应体内。
如何只处理有关服务器响应的元信息(头、状态代码等) ?
编辑:
正如杰克 · 沃顿指出的,
@GET("/path/to/get") Call<Void> getMyData(/* your args here */);
是对抗我最初的反应的最好方法
您只需返回一个 ResponseBody,它将绕过响应的解析。
ResponseBody
@GET("/path/to/get") Call<ResponseBody> getMyData(/* your args here */);
然后在你的电话里,
Call<ResponseBody> dataCall = myApi.getMyData(); dataCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Response<ResponseBody> response) { // use response.code, response.headers, etc. } @Override public void onFailure(Throwable t) { // handle failure } });
如果您正在使用 rxjava,请使用以下内容:
@GET("/path/to/get") Observable<Response<Void>> getMyData(/* your args here */);
下面是我如何在 Rx2和 Revifit2中使用它,以及 PUT REST 请求: 我的请求有一个 json 主体,但是只有带有空主体的 http 响应代码。
Api 客户端:
public class ApiClient { public static final String TAG = ApiClient.class.getSimpleName(); private DevicesEndpoint apiEndpointInterface; public DevicesEndpoint getApiService() { Gson gson = new GsonBuilder() .setLenient() .create(); OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder(); HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); okHttpClientBuilder.addInterceptor(logging); OkHttpClient okHttpClient = okHttpClientBuilder.build(); apiEndpointInterface = new Retrofit.Builder() .baseUrl(ApiContract.DEVICES_REST_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(DevicesEndpoint.class); return apiEndpointInterface; }
界面:
public interface DevicesEndpoint { @Headers("Content-Type: application/json") @PUT(ApiContract.DEVICES_ENDPOINT) Observable<ResponseBody> sendDeviceDetails(@Body Device device); }
然后使用它:
private void sendDeviceId(Device device){ ApiClient client = new ApiClient(); DevicesEndpoint apiService = client.getApiService(); Observable<ResponseBody> call = apiService.sendDeviceDetails(device); Log.i(TAG, "sendDeviceId: about to send device ID"); call.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() { @Override public void onSubscribe(Disposable disposable) { } @Override public void onNext(ResponseBody body) { Log.i(TAG, "onNext"); } @Override public void onError(Throwable t) { Log.e(TAG, "onError: ", t); } @Override public void onComplete() { Log.i(TAG, "onCompleted: sent device ID done"); } }); }
如果使用 RxJava,那么在本例中最好使用 Completable
Completable
表示没有任何值但仅指示完成或异常的延迟计算。该类遵循与 Reactive-Streams: onSubscribe (onError | onComplete)类似的事件模式?
Http://reactivex.io/rxjava/2.x/javadoc/io/reactivex/completable.html
公认的答案是:
如果端点返回失败响应代码,它仍然在 onNext中,您必须自己检查响应代码。
onNext
但是,如果使用 Completable。
@GET("/path/to/get") Completable getMyData(/* your args here */);
你将只有 onComplete和 onError。 如果响应代码是成功的,它将启动 onComplete,否则它将启动 onError。
onComplete
onError
对于 kotlin,使用返回类型 Call<Void>仍然抛出 IllegalArgumentException: Unable to create converter for retrofit2.Call<java.lang.Void>
Call<Void>
IllegalArgumentException: Unable to create converter for retrofit2.Call<java.lang.Void>
使用 Response 而不是 Call 解决了这个问题
@DELETE("user/data") suspend fun deleteUserData(): Response<Void>
你可以试试这个
Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl) .addConverterFactory(new NullOnEmptyConverterFactory()) .client(okHttpClient).build();
class NullOnEmptyConverterFactory extends Converter.Factory { @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations); return (Converter<ResponseBody, Object>) body -> { if (body.source().exhausted()) return null; return delegate.convert(body); }; } }
下面是一个例子,Kotlin 在 MVVM 中使用 service、 Repository 和 ViewModel:
服务范围:
@POST("/logout") suspend fun logout(@Header("Authorization") token: String):Response<Unit>
储存库:
//logout private val mLogoutResponse = MutableLiveData<String>() val logoutResponse: LiveData<String> get() { return mLogoutResponse } suspend fun logout(token: String) { try { val result=quizzerProfileApi.logout(token) if(result.code()!=0) { mLogoutResponse.postValue(result.code().toString()) } } catch (e: Exception) { Log.d("ProfileRepository", "logout: Error: $e") } }
视图模型:
fun logout(token: String) { viewModelScope.launch { repository.logout(token) } } val logoutResponseCd: LiveData<String> get() = repository.logoutResponse
活动内容:
private fun logout() { myViewModel.logout(token) myViewModel.logoutResponseCd.observe(this, Observer { if(it!="0"){ Log.d(TAG, "logout: code= $it") finish() } else Toast.makeText(this, "Error logging out: $it", Toast.LENGTH_SHORT).show() }) }