Retrofit中@Body的使用

關於Retrofit的參數標籤@Field、@Path、@Query等等的使用網上已經有很多文章了,可是關於@Body的使用不僅少而且還都是略微的帶過,這裏就說下Body的用法。

首先@Body的作用是把對象轉換成需要的字符串發送到服務器,明白這一點就能對應實際的需要了,比如服務端需要的是關於某一個自定義對象的JSON數據格式,當然服務端若也是Java開發的,那這段JSON能通過工具能自動生成想要的對象,額,其他語言的也能;說了些題外話,主要想說明對象轉字符串,當然轉化就需要有個轉換器——Converter,這個Converter是一個接口,由Retrofit提供,我們定義一個類WrapperConverter implements Converter,如下:

public class WrapperConverter implements Converter {
    private static final String CHARSET_DEFAULT = "utf-8";
    private Gson gson;

    @Override
    public Object fromBody(TypedInput body, Type type) throws ConversionException {
		...
		return obj;
    }

	public Gson getGson(){
        if (gson==null)gson = new Gson();
        return gson;
    }
	
    @Override
    public TypedOutput toBody(Object o) {
        if (o instanceof List) {
            try {
                String postNames = o.toString();
                postNames = postNames.substring(1, postNames.length() - 1);
                return new ArrayTypedOutPut(postNames.getBytes(CHARSET_DEFAULT), CHARSET_DEFAULT);
            } catch (UnsupportedEncodingException e) {
                throw new AssertionError(e);
            }
        } else {
            try {
                return new JsonTypedOutPut(getGson().toJson(o).getBytes(CHARSET_DEFAULT), CHARSET_DEFAULT);
            } catch (UnsupportedEncodingException e) {
                throw new AssertionError(e);
            }
        }
    }

    private static class JsonTypedOutPut implements TypedOutput {
        private final byte[] jsonBytes;
        private final String mimeType;

        public JsonTypedOutPut(byte[] jsonBytes, String encode) {
            this.jsonBytes = jsonBytes;
            this.mimeType = "application/json; charset=" + encode;
        }

        @Override
        public String fileName() {
            return null;
        }

        @Override
        public String mimeType() {
            return mimeType;
        }

        @Override
        public long length() {
            return jsonBytes.length;
        }

        @Override
        public void writeTo(OutputStream out) throws IOException {
            out.write(jsonBytes);
        }
    }

    private static class ArrayTypedOutPut implements TypedOutput {

        private final byte[] arrayBytes;
        private final String mimeType;

        public ArrayTypedOutPut(byte[] arrayBytes, String encode) {
            this.arrayBytes = arrayBytes;
            this.mimeType = "application/text; charset=" + encode;
        }

        @Override
        public String fileName() {
            return null;
        }

        @Override
        public String mimeType() {
            return mimeType;
        }

        @Override
        public long length() {
            return arrayBytes.length;
        }

        @Override
        public void writeTo(OutputStream out) throws IOException {
            out.write(arrayBytes);
        }
    }
}

fromBody是處理接收數據的,就比如像上面說的從JSON轉換成Object,這個不是這裏要說的重點;看toBody函數,這個函數裏面就是處理對象轉換字符串(當然不僅限於字符串),代碼中的toBody可以把元素爲String的List轉成逗號分隔的字符串或者將Object轉成JSON;好了,基礎工作做完了,看下怎麼在interface裏面使用:

@POST("add")
void GG(@Body List ggs, Callback<String> callback)
@PUT("add")
void GG(@Body List mms, Callback<String> callback)
注意:@Body不能與@FormUrlEncoded共用,否則報錯;而@Field偏偏是提交表單使用的,需要@FormUrlEncoded,這很明白了,根據數學的某個公理得出@Body和@Field也是不能同時使用的。

在Retrofit的定義中,POST、PUT、、PATCH請求允許使用Body,如果我們的DELETE請求想使用Body是不被通過的,怎麼辦呢,用我們自定義的DELETE註釋。下面是一源碼,可以參考:

@Documented
@Target(METHOD)
@Retention(RUNTIME)
@RestMethod(value = "DELETE", hasBody = true)
public @interface DELETE {
    String value();
}
這是自定義的,Retrofit提供的DELETE沒有hasBody = true,也就說它內部對DELETE的處理默認是不接收Body的,按上面做就行了


以上的處理都是基於Retrofit1.9的,2.0也有對應的轉化器部分,自行解決就好

若有不恰當的地方,歡迎拍磚


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章