我如何处理空响应体与翻新2?

最近我开始使用 Requfit 2,并且遇到了解析空响应主体的问题。我有一个服务器,它只响应 http 代码,没有任何内容在响应体内。

如何只处理有关服务器响应的元信息(头、状态代码等) ?

90900 次浏览

编辑:

正如杰克 · 沃顿指出的,

@GET("/path/to/get")
Call<Void> getMyData(/* your args here */);

是对抗我最初的反应的最好方法

您只需返回一个 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

表示没有任何值但仅指示完成或异常的延迟计算。该类遵循与 Reactive-Streams: onSubscribe (onError | onComplete)类似的事件模式?

Http://reactivex.io/rxjava/2.x/javadoc/io/reactivex/completable.html

公认的答案是:

@GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);

如果端点返回失败响应代码,它仍然在 onNext中,您必须自己检查响应代码。

但是,如果使用 Completable

@GET("/path/to/get")
Completable getMyData(/* your args here */);

你将只有 onCompleteonError。 如果响应代码是成功的,它将启动 onComplete,否则它将启动 onError

对于 kotlin,使用返回类型 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()
})
}