目錄介紹
- 1.關於Retrofit基本介紹
- 2.最簡單使用【配合Rx使用】
- 3.註解的種類
- 請求方法註解
- 請求頭註解
- 標記註解
- 參數註解
- 其它註解
- 4.Retrofit相關請求參數
- @Query()【備註:get請求/ 接上參數 】
- @QueryMap()【備註:get請求/ 接上參數 】
- @Path()【備註:get請求/ 替換url中某個字段】
- @Body()【備註:post請求/ 指定一個對象做爲HTTP請求體】
- @Field()【備註:post請求/ 用於傳送表單數據】
- @FieldMap()【備註:post請求/ 用於傳送表單數據】
- @Header/@Headers()【備註: 添加請求頭部 】
- @Part()做用於方法的參數,用於定義Multipart請求的每和part
- @PartMap()做用於方法的參數
- 使用時注意事項
- 5.Retrofit與RxJava結合
- 使Rxjava與retrofit結合條件
- 能夠看到 Observable觀察者
- 能夠看到訂閱者
- 6.OkHttpClient
- 攔截器說明
- 日誌攔截器
- 請求頭攔截器
- 統一請求攔截器
- 緩存攔截器
- 自定義CookieJar
- 7.踩坑經驗
- 8.Form表單提交與multipart/form-data
- 8.1 form表單經常使用屬性
- 8.2 瀏覽器提交表單時,會執行以下步驟
- 8.3 提交方式
- 8.4 POST請求
- 8.5 enctype指定的content-type
- 9.content-type介紹
- 9.1 application/x-www-form-urlencoded
- 9.2 application/json
- 9.3 text/xml
- 9.4 multipart/form-data
- 10.Retrofit源碼深刻分析
- 10.1 設計模式分析[建造者模式]
- 10.2 如何理解動態代理模式
- 10.3 如何攔截方法,解析註解
- 10.4 如何構建Retrofit的Call
- 10.5 如何執行網絡異步請求enqueue方法
- N.關於其餘
1.關於Retrofit基本介紹
- Retrofit是Square 公司開發的一款正對Android 網絡請求的框架。底層基於OkHttp 實現,OkHttp 已經獲得了google 官方的承認。
- Retrofit是由Square公司出品的針對於Android和Java的類型安全的Http客戶端,若是看源碼會發現其實本質上是OkHttp的封裝,使用面向接口的方式進行網絡請求,利用動態生成的代理類封裝了網絡接口請求的底層,其將請求返回JavaBean,對網絡認證REST API進行了很友好的支持。使用Retrofit將會極大的提升咱們應用的網絡體驗。
- RxJava + Retrofit + okHttp組合,流行的網絡請求框架
- Retrofit 負責請求的數據和請求的結果,使用接口的方式呈現,OkHttp 負責請求的過程,RxJava 負責異步,各類線程之間的切換。
- RxJava 在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫)。這就是 RxJava ,歸納得很是精準。總之就是讓異步操做變得很是簡單。
- 爲何要使用Retrofit?
- 優勢
- 請求的方法參數註解能夠定製
- 支持同步、異步和RxJava
- 超級解耦
- 能夠配置不一樣的反序列化工具來解析數據,如json、xml等
- 其餘說明
- 在處理HTTP請求的時候,由於不一樣場景或者邊界狀況等比較難處理。你須要考慮網絡狀態,須要在請求失敗後重試,須要處理HTTPS等問題,二這些事情讓你很苦惱,而Retrofit能夠將你從這些頭疼的事情中解放出來。
- 效率高,其次Retrofit強大且配置靈活,第三和OkHttp無縫銜接,第四Jack Wharton主導的(你懂的)。
2.最簡單使用
public interface DouBookApi {
/**
* 根據tag獲取圖書
* @param tag 搜索關鍵字
* @param count 一次請求的數目 最多100
* https://api.douban.com/v2/book/search?tag=文學&start=0&count=30
*/
@GET("v2/book/search")
Observable<DouBookBean> getBook(@Query("tag") String tag,
@Query("start") int start,
@Query("count") int count);
}
public class DouBookModel {
private static DouBookModel bookModel;
private DouBookApi mApiService;
public DouBookModel(Context context) {
mApiService = RetrofitWrapper
.getInstance(ConstantALiYunApi.API_DOUBAN) //baseUrl地址
.create(DouBookApi.class);
}
public static DouBookModel getInstance(Context context){
if(bookModel == null) {
bookModel = new DouBookModel(context);
}
return bookModel;
}
public Observable<DouBookBean> getHotMovie(String tag, int start , int count) {
Observable<DouBookBean> book = mApiService.getBook(tag, start, count);
return book;
}
}
public class RetrofitWrapper {
private static RetrofitWrapper instance;
private Retrofit mRetrofit;
public RetrofitWrapper(String url) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//打印日誌
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(logging).build();
OkHttpClient client = builder.addInterceptor(new LogInterceptor("HTTP")).build();
//解析json
Gson gson = new GsonBuilder()
.setLenient()
.create();
mRetrofit = new Retrofit
.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(client)
.build();
}
public static RetrofitWrapper getInstance(String url){
//synchronized 避免同時調用多個接口,致使線程併發
synchronized (RetrofitWrapper.class){
instance = new RetrofitWrapper(url);
}
return instance;
}
public <T> T create(final Class<T> service) {
return mRetrofit.create(service);
}
}
DouBookModel model = DouBookModel.getInstance(activity);
model.getHotMovie(mType,start,count)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<DouBookBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(DouBookBean bookBean) {
}
});
3.註解的種類
@GET get請求
@POST post請求
@PUT put請求
@DELETE delete請求
@PATCH patch請求,該請求是對put請求的補充,用於更新局部資源
@HEAD head請求
@OPTIONS option請求
@HTTP 通用註解,能夠替換以上全部的註解,其擁有三個屬性:method,path,hasBody
@Headers 用於添加固定請求頭,能夠同時添加多個。經過該註解添加的請求頭不會相互覆蓋,而是共同存在
@Header 做爲方法的參數傳入,用於添加不固定值的Header,該註解會更新已有的請求頭
@FormUrlEncoded
表示請求發送編碼表單數據,每一個鍵值對須要使用@Field註解
用於修飾Fiedl註解 和FileldMap註解
使用該註解,表示請求正文將使用表單網址編碼。字段應該聲明爲參數,並用@Field 註解和 @FieldMap 註解,使用@FormUrlEncoded 註解的請求將具備"application/x-www-form-urlencoded" MIME類型。字段名稱和值將先進行UTF-8進行編碼,再根據RFC-3986進行URI編碼。
@Multipart
做用於方法
表示請求發送multipart數據,使用該註解,表示請求體是多部分的,每一個部分做爲一個參數,且用Part註解聲明。
@Streaming
做用於方法
未使用@Straming 註解,默認會把數據所有載入內存,以後經過流獲取數據也是讀取內存中數據,因此返回數據較大時,須要使用該註解。
處理返回Response的方法的響應體,用於下載大文件
提醒:若是是下載大文件必須加上@Streaming 不然會報OOM
@Streaming
@GET
Call<ResponseBody> downloadFileWithDynamicUrlAsync(@Url String fileUrl);
參數註解:@Query 、@QueryMap、@Body、@Field、@FieldMap、@Part、@PartMap
@Path、@Url
4.Retrofit相關請求參數
- @Query()【備註:get請求/ 接上參數 】
@Query:做用於方法參數,用於添加查詢參數,即請求參數
用於在url後拼接上參數,例如:
@GET("book/search")
Call<Book> getSearchBook(@Query("q") String name);//name由調用者傳入
至關於
@GET("book/search?q=name")
Call<Book> getSearchBook();
用於Get中指定參數
- @QueryMap()【備註:get請求/ 接上參數 】
@QueryMap:做用於方法的參數。以map的形式添加查詢參數,即請求參數,參數的鍵和值都經過String.valueOf()轉換爲String格式。默認map的值進行URL編碼,map中的每一項發鍵和值都不能爲空,不然跑出IllegalArgumentException異常。
固然若是入參比較多,就能夠把它們都放在Map中,例如:
@GET("book/search")
Call<Book> getSearchBook(@QueryMap Map<String, String> options);
- @Path()【備註:get請求/ 替換url中某個字段】
/**
* http://api.zhuishushenqi.com/ranking/582ed5fc93b7e855163e707d
* @return
*/
@GET("/ranking/{rankingId}")
Observable<SubHomeTopBean> getRanking(@Path("rankingId") String rankingId);
@GET("group/{id}/users")
Call<Book> groupList(@Path("id") int groupId);
* 像這種請求接口,在group和user之間有個不肯定的id值須要傳入,就能夠這種方法。咱們把待定的值字段用{}括起來,固然 {}裏的名字不必定就是id,能夠任取,但需和@Path後括號裏的名字同樣。若是在user後面還須要傳入參數的話,就能夠用Query拼接上,好比:
@GET("group/{id}/users")
Call<Book> groupList(@Path("id") int groupId, @Query("sort") String sort);
* 當咱們調用這個方法時,假設咱們groupId傳入1,sort傳入「2」,那麼它拼接成的url就是group/1/users?sort=2,固然最後請求的話還會加上前面的baseUrl
- @Body()【備註:post請求/ 指定一個對象做爲HTTP請求體】
使用@Body 註解定義的參數不能爲null 。當你發送一個post或put請求,可是又不想做爲請求參數或表單的方式發送請求時,使用該註解定義的參數能夠直接傳入一個實體類,retrofit會經過convert把該實體序列化並將序列化的結果直接做爲請求體發送出去。
能夠指定一個對象做爲HTTP請求體,好比:
@POST("users/new")
Call<User> createUser(@Body User user);
它會把咱們傳入的User實體類轉換爲用於傳輸的HTTP請求體,進行網絡請求。
多用於post請求發送非表單數據,好比想要以post方式傳遞json格式數據
- @Field()【備註:post請求/ 用於傳送表單數據】
用於傳送表單數據:
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
注意開頭必須多加上@FormUrlEncoded這句註釋,否則會報錯。表單天然是有多組鍵值對組成,這裏的first_name就是鍵,而具體傳入的first就是值啦
多用於post請求中表單字段,Filed和FieldMap須要FormUrlEncoded結合使用
- @FieldMap()【備註:post請求/ 用於傳送表單數據】
@FormUrlEncoded
@POST("user/login")
Call<User> login(@FieldMap Map<String,String> map);
- @Header/@Headers()【備註: 添加請求頭部 】
用於動態添加請求頭部:
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
表示將頭部Authorization屬性設置爲你傳入的authorization;固然你還能夠用@Headers表示,做用是同樣的好比:
@Headers("Cache-Control: max-age=640000")
@GET("user")
Call<User> getUser()
固然你能夠多個設置:
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("user")
Call<User> getUser()
- @Part()做用於方法的參數,用於定義Multipart請求的每和part
使用該註解定義的參數,參數值能夠爲空,爲空時,則忽略。使用該註解定義的參數類型有以下3中方式可選:
1 okhttp2.MulitpartBody.Part,內容將被直接使用。省略part中的名稱,即@Part MultipartBody.Part part
2 若是類型是RequestBody,那麼該值直接與其內容類型一塊兒使用。在註釋中提供part名稱(例如,@Part("foo") RequestBody foo)
3 其它對象類型將經過使用轉換器轉換爲適當的格式。在註釋中提供part名稱(例如,@Part("foo") Image photo)。
@Multipart
@POST("/")
Call<ResponseBody> example(
@Part("description") String description,
@Part(value = "image", encoding = "8-bit") RequestBody image);
以map的方式定義Multipart請求的每一個part map中每一項的鍵和值都不能爲空,不然拋出IllegalArgumentException異常。
使用@PartMap 註解定義的參數類型有一下兩種:
1 若是類型是RequestBody,那麼該值將直接與其內容類型與其使用。
2 其它對象類型將經過使用轉換器轉換爲適當的格式。
使用時注意事項
- 一、Map用來組合複雜的參數,而且對於FieldMap,HeaderMap,PartMap,QueryMap這四種做用方法的註解,其參數類型必須爲Map實例,且key的類型必須爲String類型,不然拋出異常。
- 二、Query、QueryMap與Field、FieldMap功能同樣,生成的數據形式同樣;Query、QueryMap的數據體如今Url上;Field、FieldMap的數據是請求體
- 三、{佔位符}和PATH儘可能只用在URL的path部分,url的參數使用Query、QueryMap代替,保證接口的簡潔
- 四、Query、Field、Part支持數據和實現了iterable接口的類型,如List、Set等,方便向後臺傳遞數組,代碼以下:
- 五、以上部分註解真正的實如今ParameterHandler類中,每一個註解的真正實現都是ParameterHandler類中的一個final類型的內部類,每一個內部類都對各個註解的使用要求作了限制,好比參數是否可空、鍵和值是否可空等。
- 六、@FormUrlEncoded 註解和@Multipart 註解不能同時使用,不然會拋出methodError(「Only one encoding annotation is allowed.」),可在ServiceMethod類中parseMethodAnnotation()方法中找到不能同時使用的具體緣由。
- 七、@Path 與@Url 註解不能同時使用,不然會拋出parameterError(p, "@Path parameters may not be used with @Url."),可在ServcieMethod類中parseParameterAnnotation()方法中找到不能同時使用的具體代碼。其實緣由也是很好理解:Path註解用於替換url中的參數,這就要求在使用path註解時,必須已經存在請求路徑。否則無法替換路徑中指定的參數。而@Url 註解是在參數中指定了請求路徑的,這時候情定請求路徑已經晚,path註解找不到請求路徑,更別提更換請求路徑了中的參數了。
- 八、使用@Body 註解的參數不能使用form 或multi-part編碼,即若是爲方法使用了FormUrlEncoded或Multipart註解,則方法的參數中不能使用@Body 註解,不然會拋出異常parameterError(p, 「@Body parameters cannot be used with form or multi-part encoding.」)
5.Retrofit與RxJava結合
使Rxjava與retrofit結合條件
- 在Retrofit對象創建的時候添加一句代碼addCallAdapterFactory(RxJavaCallAdapterFactory.create())
完整代碼
mRetrofit = new Retrofit
.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(client)
.build();
public Observable<DouBookBean> getHotMovie(String tag, int start , int count) {
Observable<DouBookBean> book = mApiService.getBook(tag, start, count);
return book;
}
- 能夠看到訂閱者
- RxAndroid其實就是對RxJava的擴展。好比上面這個Android主線程在RxJava中就沒有,所以要使用的話就必須得引用RxAndroid
DouBookModel model = DouBookModel.getInstance(activity);
model.getHotMovie(mType,start,count)
.subscribeOn(Schedulers.io()) //請求數據的事件發生在io線程
.observeOn(AndroidSchedulers.mainThread()) //請求完成後在主線程更顯UI
.subscribe(new Observer<DouBookBean>() { //訂閱
@Override
public void onCompleted() {
//全部事件都完成,能夠作些操做。。
}
@Override
public void onError(Throwable e) {
e.printStackTrace(); //請求過程當中發生錯誤
}
@Override
public void onNext(DouBookBean bookBean) {
//這裏的book就是咱們請求接口返回的實體類
}
});
6.OkHttpClient
- 攔截器說明
- addNetworkInterceptor添加的是網絡攔截器Network,Interfacetor它會在request和response時分別被調用一次;
- addInterceptor添加的是應用攔截器Application Interceptor他只會在response被調用一次。
- 日誌攔截器
- 一種是使用HttpLoggingInterceptor,須要使用到依賴
compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'
/**
* 建立日誌攔截器
* @return
*/
public static HttpLoggingInterceptor getHttpLoggingInterceptor() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.e("OkHttp", "log = " + message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return loggingInterceptor;
}
- 另外一種是建立自定義日誌攔截器
- 請求頭攔截器
/**
* 請求頭攔截器
* 使用addHeader()不會覆蓋以前設置的header,若使用header()則會覆蓋以前的header
* @return
*/
public static Interceptor getRequestHeader() {
Interceptor headerInterceptor = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request.Builder builder = originalRequest.newBuilder();
builder.addHeader("version", "1");
builder.addHeader("time", System.currentTimeMillis() + "");
Request.Builder requestBuilder = builder.method(originalRequest.method(), originalRequest.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
};
return headerInterceptor;
}
使用addInterceptor()方法添加到OkHttpClient中
個人理解是,請求頭攔截器是爲了讓服務端能更好的識別該請求,服務器那邊經過請求頭判斷該請求是否爲有效請求等...
- 統一請求攔截器
- 使用addInterceptor()方法添加到OkHttpClient中,統一請求攔截器的功能跟請求頭攔截器相相似
/**
* 統一請求攔截器
* 統一的請求參數
*/
public static Interceptor commonParamsInterceptor() {
Interceptor commonParams = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originRequest = chain.request();
Request request;
HttpUrl httpUrl = originRequest.url().newBuilder()
.addQueryParameter("paltform", "android")
.addQueryParameter("version", "1.0.0")
.build();
request = originRequest.newBuilder()
.url(httpUrl)
.build();
return chain.proceed(request);
}
};
return commonParams;
}
- 緩存攔截器
- 使用okhttp緩存的話,先要建立Cache,而後在建立緩存攔截器
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//添加緩存攔截器
//建立Cache
File httpCacheDirectory = new File("OkHttpCache");
Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
builder.cache(cache);
//設置緩存
builder.addNetworkInterceptor(InterceptorUtils.getCacheInterceptor());
builder.addInterceptor(InterceptorUtils.getCacheInterceptor());
/**
* 在無網絡的狀況下讀取緩存,有網絡的狀況下根據緩存的過時時間從新請求
* @return
*/
public static Interceptor getCacheInterceptor() {
Interceptor commonParams = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetworkUtils.isConnected()) {
//無網絡下強制使用緩存,不管緩存是否過時,此時該請求實際上不會被髮送出去。
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetworkUtils.isConnected()) {
//有網絡狀況下,根據請求接口的設置,配置緩存。
// 這樣在下次請求時,根據緩存決定是否真正發出請求。
String cacheControl = request.cacheControl().toString();
//固然若是你想在有網絡的狀況下都直接走網絡,那麼只須要
//將其超時時間這是爲0便可:String cacheControl="Cache-Control:public,max-age=0"
int maxAge = 60 * 60;
// read from cache for 1 minute
return response.newBuilder()
.header("Cache-Control", cacheControl)
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader("Pragma") .build();
} else { //無網絡
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
return response.newBuilder()
.header("Cache-Control", "public,only-if-cached,max-stale=360000")
.header("Cache-Control", "public,only-if-cached,max-stale=" + maxStale)
.removeHeader("Pragma") .build();
}
}
};
return commonParams;
}
/**
* 自定義CookieJar
* @param builder
*/
public static void addCookie(OkHttpClient.Builder builder){
builder.cookieJar(new CookieJar() {
private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url, cookies);
//保存cookie //也可使用SP保存
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url);
//取出cookie
return cookies != null ? cookies : new ArrayList<Cookie>();
}
});
}
7.踩坑經驗
http://api.mydemo.com/api%2Fnews%2FnewsList?
罪魁禍首@Url與@Path註解,咱們開發過程當中,確定會須要動態的修改請求地址
兩種動態修改方式以下:
@POST()
Call<HttpResult<News>> post(@Url String url, @QueryMap Map<String, String> map);
@POST("api/{url}/newsList")
Call<HttpResult<News>> login(@Path("url") String url, @Body News post);
第一種是直接使用@Url,它至關於直接替換了@POST()裏面的請求地址
第二種是使用@Path("url"),它只替換了@POST("api/{url}/newsList")中的{url}
若是你用下面這樣寫的話,就會出現url被轉義
@POST("{url}")
Call<HttpResult<News>> post(@Path("url") String url);
你若是執意要用@Path,也不是不能夠,須要這樣寫
@POST("{url}")
Call<HttpResult<News>> post(@Path(value = "url", encoded = true) String url);
8.Form表單提交與multipart/form-data
8.1 form表單經常使用屬性
- action:url 地址,服務器接收表單數據的地址
- method:提交服務器的http方法,通常爲post和get
- name:最好好吃name屬性的惟一性
- enctype: 表單數據提交時使用的編碼類型,默認使用"pplication/x-www-form-urlencoded",若是是使用POST請求,則請求頭中的content-type指定值就是該值。若是表單中有上傳文件,編碼類型須要使用"multipart/form-data",類型,才能完成傳遞文件數據。
8.2 瀏覽器提交表單時,會執行以下步驟
- 識別出表單中表單元素的有效項,做爲提交項
- 構建一個表單數據集
- 根據form表單中的enctype屬性的值做爲content-type對數據進行編碼
- 根據form表單中的action屬性和method屬性向指定的地址發送數據
8.3 提交方式
- get:表單數據會被encodeURIComponent後以參數的形式:name1=value1&name2=value2 附帶在url?後面,再發送給服務器,並在url中顯示出來。
- post:content-type 默認"application/x-www-form-urlencoded"對錶單數據進行編碼,數據以鍵值對在http請求體重發送給服務器;若是enctype 屬性爲"multipart/form-data",則以消息的形式發送給服務器。
8.4 POST請求
- HTTP/1.1 協議規定的HTTP請求方法有OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 這幾種。其中POST通常用於向服務器提交數據。
- 你們知道,HTTP協議是以ASCII 碼傳輸,創建在TCP/IP協議之上的應用層規範。規範把HTTP請求分爲3大塊:狀態行、請求頭、消息體。相似於以下:
<method> <request-URL> <version>
<headers>
<entity-body>
- 協議規定POST提交的數據必須放在消息主題(entity-body)中,但協議並無規定數據必須使用什麼編碼方式。實際上,開發者能夠本身決定消息體的格式,只要後面發送的HTTP請求知足上面的格式就能夠了。
- 可是,數據發送出去後,還要服務器解析成功纔有意義。通常服務器都內置了自動解析常見數據格式的功能。服務端一般是根據請求頭(headers)中的Content-Type字段來獲知請求中的消息主體是用何種方式編碼,再對主體進行解析。因此說到POST提交數據方法,包含了Content-Type和消息主題編碼方式兩部分。
8.5 enctype指定的content-type
- application/x-www-form-urlencoded
- application/json
- text/xml
- multipart/form-data
9.content-type介紹
9.1 application/x-www-form-urlencoded
9.2 application/json
- application/json 這個Content-Type做爲響應頭你們確定不陌生。事實上如今已經基本都是都是這種方式了,來通知服務器消息體是序列化後的JSON字符串。因爲JSON規範的流行,除了低版本的IE以外的如今主流瀏覽器都原生支持JSON。固然服務器也有處理JSON的函數。
- JSON格式支持比鍵值對更復雜的結構化數據,這樣點也頗有用,在須要提交數據層次很是深的數據時,用JSON序列化以後提交,很是方便。
POST http://www.hao123.com/ HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
- 這種方案,能夠很方便的提交複雜的結構化的數據,特別適合RESTful的接口。並且各大抓包工具如chrome自帶的開發者工具,Firebug、Fidder,都會以樹形結構展現JSON數據,很是友好。
9.3 text/xml
9.4 multipart/form-data
- 在最初的http協議中,沒有定義上傳文件的Method, 爲了實現這個功能,http協議組改造了post請求,添加一種post規範,設定這種規範的Content-Type爲multipart/form-data;boundary=${bound},其中${bound}是定義分割符,用於分割各項內容(文件,key-value對),否則服務器沒法正確識別各項內容。post body裏須要用到,儘可能保證隨機惟一。
- 這又是一個常見的POST數據提交的方式。咱們使用表單上傳文件時,必須讓form表單enctype等於multipart/form-data。
<form action="/upload" enctype="multipart/form-data" method="post">
Username: <input type="text" name="username">
Password: <input type="password" name="password">
File: <input type="file" name="file">
<input type="submit">
</form>
關於其餘
參考博客
版本更新說明
- V1.0.1 更新2017年3月18日
- V1.0.2 更新2017年5月21日
- V1.0.3 更新2017年10月12日
- V2.0.0 更新2018年2月23日