retrofit2调用webservice请求一共3篇
一:工具
二:通过retrofit2 进行soap请求
三:拦截器,通过retrofit2 拦截器拼接入参和过滤出返回值,使soap请求更趋向于http请求
先优化下返回值,截取需要的数据,用了Rx的compose关键字:
/**
* 从网络请求里面的那串截取出需要的json数据
* @return
*/
public static Observable.Transformer<ResponseBody, String> gsonResult(final String papapa) {
return new Observable.Transformer<ResponseBody, String>() {
@Override
public Observable<String> call(Observable<ResponseBody> tObservable) {
return tObservable.flatMap(
new Func1<ResponseBody, Observable<String>>() {
@Override
public Observable<String> call(ResponseBody response) {
try {
if (response == null) {
Log.v("test", "--->请求发生未知错误");
return Observable.error(new ApiException("0002", "接口调用无返回值"));
}
String res = response.string();// 记得要关闭!!! ResponseBody .string()会自动关闭 .string()和toString()完全不一样!!!
if (res != null && !res.equals("")) {
// 字符转义<-->转义完之后就开始截取数据
String subStr = res.replace("<", "<").replace(">", ">");
// success date like this <GetSysDateTimeResult>string</GetSysDateTimeResult>
String ostar = "<" + papapa + "Result>";
String oend = "</" + papapa + "Result>";
if (subStr.contains(ostar) && subStr.contains(oend)) {
int startIndex = subStr.indexOf(ostar) + ostar.length();
int endtIndex = subStr.lastIndexOf(oend);
String ores = subStr.substring(startIndex, endtIndex);
return returnString(ores);
}
}
} catch (Exception e) {
Log.v("test", "--->IOException e: " + e.getMessage());
e.printStackTrace();
return Observable.error(new ApiException("0003", e.getMessage()));
}
// return Observable.empty();
return Observable.error(new ApiException("0001","无"+papapa+"接口信息,请检查调用的接口是否正确"));
}
}
);
}
};
}
调用:
/**
* 通过省份获取城市代码,截取数据
*/
public void getSupportCityBySecond() {
Map map = new HashMap<>();
map.put("byProvinceName", "福建");
String result = ApiNode.getParameter("getSupportCity", map);
AppClient.getInstance().getSupportCity(result)
.compose(ApiSchedulersHelper.gsonResult("getSupportCity")) // -->获取从数据源获取json
.subscribeOn(Schedulers.io())// 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread())// 指定 Subscriber 的回调发生在主线程
.subscribe(new RxSubscriber<String>() {
@Override
public void _onNext(String string) {
Log.e("test", "---getSupportCityBySecond _onNext str--->"+string);
// <string>福州 (58847)</string><string>厦门 (59134)</string><string>龙岩 (58927)</string><string>南平 (58834)</string><string>宁德 (58846)</string><string>莆田 (58946)</string><string>泉州 (59137)</string><string>三明 (58828)</string><string>漳州 (59126)</string>
tv_date.setText(string);
}
@Override
public void _onError(String msg) {
Log.d("test", "--->getSupportCityBySecond msg:" + msg);
tv_date.setText(msg);
}
});
}
接下来要换拦截器的车开了
重新撸一个AppSoapClient,初始化retrofit
import com.blankj.utilcode.utils.EncodeUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import linc.ps.net_common.ApiNode;
import linc.ps.net_common.BuildConfig;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
/**
* Retrofit初始化工具,拦截器模式
*/
public class AppSoapClient {
// 超时时间 默认5秒
private static final int DEFAULT_TIMEOUT = 5;
public static Retrofit mRetrofit;
private ApiSoapStores apiSoapStores;
private AppSoapClient() {
if (mRetrofit == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
// 日志信息拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//设置 Debug Log 模式
builder.addInterceptor(loggingInterceptor);
//SOAP请求过滤器
HttpRequestInterceptor httpRequestInterceptor = new HttpRequestInterceptor();
builder.addInterceptor(httpRequestInterceptor);
OkHttpClient okHttpClient = builder.build();
mRetrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.API_SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(okHttpClient)
.build();
}
apiSoapStores = mRetrofit.create(ApiSoapStores.class);
}
/**
* SOAP请求过滤器
*/
static class HttpRequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
//获取原Resuqest
Request originalRequest = chain.request();
//解析出namespace
String url = originalRequest.url().toString();
String namespace = url.replace(BuildConfig.API_SERVER_URL + ApiSoapStores.URL_HEAD, "");
RequestBody requestBody = originalRequest.body();
if(requestBody != null) {
//构建新的ResuqestBody
RequestBody newRequestBody = buildRequestBody(namespace, requestBody);
if(newRequestBody != null) {
//构建新的Request
Request.Builder builder = originalRequest.newBuilder();
Request request = builder
.url(url.replace("/" + namespace, ""))
.addHeader("Content-type", ApiSoapStores.CONTENT_TYPE)
.addHeader("SOAPAction", ApiSoapStores.SOAP_ACTION_HEAD + namespace)
.post(newRequestBody)
.build();
//开始进行网络请求
Response originalResponse = chain.proceed(request);
if(originalResponse != null) {
//获取原ResponseBody
ResponseBody responseBody = originalResponse.body();
//构建新的ResponseBody
ResponseBody newResponseBody = parseResponseBody(namespace, responseBody);
if (newResponseBody != null) {
Response.Builder responseBuilder = originalResponse.newBuilder();
//返回新的Resonse
return responseBuilder.body(newResponseBody).build();
}
}
return originalResponse;
}
}
return chain.proceed(originalRequest);
}
/**
* 入参数据处理
* @param namespace
* @param requestBody
* @return
*/
private RequestBody buildRequestBody(String namespace, RequestBody requestBody) {
Map<String, String> map = new HashMap<>();
if (requestBody instanceof FormBody) {
Log.e("test", "--->有入参");
FormBody formBody = (FormBody) requestBody;
for (int i = 0; i < formBody.size(); i++) {
String name = formBody.encodedName(i);
String value = EncodeUtils.urlDecode(formBody.encodedValue(i)).replace("<", "<").replace(">", ">").replace("%24", "$");
// String value = formBody.encodedValue(i).replace("<", "<").replace(">", ">").replace("%24", "$");//--->转义字符得封装成一个类
map.put(name, value);
}
String newBody = ApiNode.getParameter(namespace, map);// 拼接入参的方法
Log.e("test", "--->有入参-->"+newBody);
MediaType mediaType = MediaType.parse(ApiSoapStores.CONTENT_TYPE);
RequestBody newRequestBody = RequestBody.create(mediaType, newBody);
Log.e("test", "--->有入参-->"+newRequestBody.toString());
return newRequestBody;
}else{// 无参的特殊处理,因为转换的FormBody会为空
Log.e("test", "--->无入参");
String newBody = ApiNode.getParameter(namespace, map);
MediaType mediaType = MediaType.parse(ApiSoapStores.CONTENT_TYPE);
RequestBody newRequestBody = RequestBody.create(mediaType, newBody);
Log.e("test", "--->无入参-->"+newRequestBody.toString());
return newRequestBody;
}
}
/**
* 解析Response
* @param namespace
* @param responseBody
* @return
*/
private ResponseBody parseResponseBody(String namespace, ResponseBody responseBody) {
try {
String res = responseBody.string();
MediaType mediaType = MediaType.parse(ApiSoapStores.CONTENT_TYPE);
// 获取到的数据-->截取
if (res != null && !res.equals("")) {
// 字符转义
String ostar = "<" + namespace + "Result>";
String oend = "</" + namespace + "Result>";
if (res.contains(ostar) && res.contains(oend)) {
int startIndex = res.indexOf(ostar) + ostar.length();
int endIndex = res.lastIndexOf(oend);
String ores = res.substring(startIndex, endIndex);
return ResponseBody.create(mediaType, ores);
}
}else{
return ResponseBody.create(mediaType, res);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//在访问HttpMethods时创建单例
private static class SingletonHolder{
private static final AppSoapClient INSTANCE = new AppSoapClient();
}
//获取单例
public static AppSoapClient getInstance(){
return AppSoapClient.SingletonHolder.INSTANCE;
}
public Observable<ResponseBody> getSupportCity(String byProvinceName){
return apiSoapStores.getSupportCity(byProvinceName);
}
}
retrofit的接口类ApiSoapStores
import okhttp3.ResponseBody;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import rx.Observable;
/**
* Created by Frank on 2016/12/9.
* (请求有用拦截器)
* 无入参的要去掉@FormUrlEncoded
*/
public interface ApiSoapStores {
/** head基础参数 **/
String CONTENT_TYPE = "text/xml; charset=utf-8";
String SOAP_ACTION_HEAD = "http://WebXml.com.cn/";
String URL_HEAD = "WeatherWebService.asmx/";
//** 带参请求 **//*
@FormUrlEncoded
@POST("WeatherWebService.asmx/getSupportCity")
Observable<ResponseBody> getSupportCity(@Field("byProvinceName") String byProvinceName);
/** 无参请求 **/
@POST("WeatherWebService.asmx/getSupportCity")
Observable<ResponseBody> getSupportCity();
}
无入参的要去掉@FormUrlEncoded
无入参的要去掉@FormUrlEncoded
无入参的要去掉@FormUrlEncoded
重要的事情说三遍
调用:
/**
* 通过省份获取城市代码,截取数据,中文乱码de解决的方案-->入参赋值之前手动转换一次
*/
public void getSupportCityByThrid() {
AppSoapClient.getInstance().getSupportCity(et_cityname.getText().toString())
.subscribeOn(Schedulers.io())// 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread())// 指定 Subscriber 的回调发生在主线程
.subscribe(new Subscriber<ResponseBody>() {
@Override
public void onCompleted() {
Log.e("test", "---getSupportCityByThrid onCompleted--->");
}
@Override
public void onError(Throwable e) {
Log.e("test", "---getSupportCityByThrid onError--->"+e.getMessage());
}
@Override
public void onNext(ResponseBody response) {
Log.e("test", "---getSupportCityByThrid onNext--->");
try {
String res = response.string();
Log.e("test", "---getSupportCityByThrid onNext str--->"+res);
tv_date.setText(res);
} catch (IOException e) {
Log.e("test", "---getSupportCityByThrid onNext str-IOException-->"+e.getMessage());
e.printStackTrace();
}
}
});
}
上面的和原来没用拦截器的几点说明一下:
1.接口会比较清晰
原来:
// 通过省份获取城市代码,头文件在第一篇都有讲到
@Headers({
"Content-Type:text/xml; charset=utf-8",
"SOAPAction:http://WebXml.com.cn/getSupportCity"
})
// 这里对应 http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getWeatherbyCityName 里面的WeatherWebService
@POST("WeatherWebService.asmx")
Observable<ResponseBody> getSupportCity(@retrofit2.http.Body String s);
现在:
@FormUrlEncoded
@POST("WeatherWebService.asmx/getSupportCity")
Observable<ResponseBody> getSupportCity(@Field("byProvinceName") String byProvinceName);
2.是入参更清晰
3.返回值不用在每个请求里面特殊处理去截取数据
原来:
Map map = new HashMap<>();
map.put("byProvinceName", "福建");
String result = ApiNode.getParameter("getSupportCity", map);
AppClient.getInstance().getSupportCity(result)
.compose(ApiSchedulersHelper.gsonResult("getSupportCity")) // -->获取从数据源获取json
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new RxSubscriber<String>() {
@Override
public void _onNext(String string) {
tv_date.setText(string);
}
@Override
public void _onError(String msg) {
tv_date.setText(msg);
}
});
现在:
AppSoapClient.getInstance().getSupportCity(et_cityname.getText().toString())
.subscribeOn(Schedulers.io())// 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread())// 指定 Subscriber 的回调发生在主线程
.subscribe(new Subscriber<ResponseBody>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResponseBody response) {
try {
String res = response.string();
tv_date.setText(res);
} catch (IOException e) {
e.printStackTrace();
}
}
});
因为拼接入参,截取返回值的都在拦截器里做了
但是我这样写会遇到两个问题,现在也还没有很完美的处理方法:
1.入参中文乱码的处理
拦截器获取到的入参,手动再转了一次
转换的方法是调用的网上别人写的
https://github.com/Blankj/AndroidUtilCode/blob/master/README-CN.md
里面的EncodeUtils.urlDecode,但是用起来还是感觉有点奇怪,像请求接口那边加@Headers,入参改成@Field(value = "byProvinceName", encoded=true) String byProvinceName,这些测试了没有效果。
2.现在的项目都是Rx去继承重写了Subscriber,所以异常情况(webservice接口不存在,或者http的异常处理)都可以捕获而且去判断,然后做出相应的提示如果,在拦截器里不知道怎么很好的去实现这个。
以上问题有了解的麻烦告知一下,非常感谢