ฉันจะจัดการกับตัวตอบสนองที่ว่างเปล่าด้วย Retrofit 2 ได้อย่างไร


126

เมื่อเร็ว ๆ นี้ฉันเริ่มใช้ Retrofit 2 และฉันประสบปัญหาในการแยกวิเคราะห์เนื้อหาตอบสนองที่ว่างเปล่า ฉันมีเซิร์ฟเวอร์ที่ตอบสนองด้วยโค้ด http เท่านั้นโดยไม่มีเนื้อหาใด ๆ ในเนื้อหาตอบกลับ

ฉันจะจัดการเฉพาะข้อมูลเมตาเกี่ยวกับการตอบสนองของเซิร์ฟเวอร์ (ส่วนหัวรหัสสถานะ ฯลฯ ) ได้อย่างไร

คำตอบ:


217

แก้ไข:

ดังที่ Jake Wharton ชี้ให้เห็นว่า

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

เป็นวิธีที่ดีที่สุดเมื่อเทียบกับคำตอบเดิมของฉัน -

คุณสามารถส่งคืน a 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
    }
});

58
ดียิ่งขึ้น: ใช้Voidซึ่งไม่เพียง แต่มีความหมายที่ดีกว่า แต่ยังมีประสิทธิภาพมากกว่า (เล็กน้อย) ในกรณีที่ว่างเปล่าและมีประสิทธิภาพมากกว่าอย่างมากในกรณีที่ไม่ว่างเปล่า (เมื่อคุณไม่สนใจร่างกาย)
Jake Wharton

1
@JakeWharton นั่นเป็นพฤติกรรมที่ยอดเยี่ยม ขอบคุณสำหรับคำชี้แนะ อัปเดตคำตอบแล้ว
iagreen

2
คำตอบที่ดี เหตุผลหนึ่งที่ไม่ใช้ Void คือถ้าคุณมีทรัพยากรที่ส่งคืนเฉพาะเนื้อหาเมื่อคำขอไม่สำเร็จและคุณต้องการแปลง errorBody ResponseBody เป็นประเภทเฉพาะหรือทั่วไป

7
ข้อเสนอแนะ @JakeWharton Voidที่ดีในการใช้งาน การใช้Unitโค้ด Kotlin จะให้ประโยชน์เหมือนกันกับVoidใน Java for Retrofit หรือไม่?
Akshay Chordiya

6
@ akshay-chordiya ฉันเพิ่งตรวจสอบUnitใน Kotlin ไม่ทำงานVoidอย่างไรก็ตาม ฉันคิดว่ามีการตรวจสอบรหัสแข็งอยู่ที่ไหนสักแห่ง
user3363866

41

หากคุณใช้ 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 */);

คุณจะมีเพียงonCompleteและonError. หากรหัสตอบกลับสำเร็จรหัสนั้นจะเริ่มทำงานonCompleteอีกครั้งจะเริ่มonErrorทำงาน


1
onError Throwableในกรณีนี้จะมีข้อโต้แย้งอะไรบ้าง? ฉันพบว่าตัวนี้สะอาดกว่า แต่เรามักจะต้องดูรหัสตอบสนองและเนื้อหาสำหรับความล้มเหลว
big_m

24

หากคุณใช้ rxjava ให้ใช้สิ่งต่อไปนี้

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

นั่นคือสิ่งที่ฉันพบ! ขอบคุณ!
Sirelon

ฉันใช้ ResposeBody กับ RxJava2 และ Retrofit2 สำหรับคำขอ PUT REST มันใช้งานได้ดี
Moti Bartov

1
เรามี endpoint API ที่ส่งคืน body ว่างเมื่อสำเร็จและ json body เมื่อเกิดข้อผิดพลาด หากใช้การตอบกลับ <Void> ฉันจะจัดการกับกรณีข้อผิดพลาดได้อย่างไร
KeNVin Favo

คุณใช้คลาสตอบกลับใดที่นี่ ชุดติดตั้งเพิ่มหรือ OKHttps?
Matthias

1
ไม่ใช่ตัวเลือกที่ดีหากคุณจัดการข้อผิดพลาดในข้อยกเว้น .. คุณจะไม่มีข้อยกเว้นกับแนวทางนี้ แต่เป็น JSON เพื่อตอบสนองข้อผิดพลาด
Ovi Trif

0

นี่คือวิธีที่ฉันใช้กับ Rx2 และ Retrofit2 ด้วยคำขอ 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");
        }
    });

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.