Retorfit2.0+Rxjava1+Okhttp3实现soap协议的Webservice

来源:互联网 发布:大狗狗影视下载软件 编辑:程序博客网 时间:2024/06/03 22:46

最近业余接了一个项目,服务器端使用的是SOAP协议来传输数据,虽然之前也使用过ksoap包来实现SOAP协议传输,但懂不能一直停留在之前的技术上吧… 所以,这次我想换个方法来实现SOAP协议传输。

既然要换SOAP协议的实现方法,那么就先研究一下这个SOAP协议具体是个什么东东
下面是一段SOAP请求:

POST /WebServices/WeatherWebService.asmx HTTP/1.1User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.3603)Content-Type: text/xml; charset=utf-8SOAPAction: "http://WebXml.com.cn/getSupportCity"Host: www.webxml.com.cnContent-Length: 348Expect: 100-continueConnection: Keep-Alive<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><getSupportCity xmlns="http://WebXml.com.cn/"><byProvinceName>广东</byProvinceName></getSupportCity></soap:Body></soap:Envelope>

上面的内容显示,其实SOAP协议实现的就是一个HTTP的POST请求,不过使用了SOAPAction来标识请求的内容是SOAP协议的,接收端需要根据SOAP协议来解析内容;最后的XML格式的内容就是SOAP具体的请求内容了。

明白了这层关系,那么我们就可以考虑使用retrofit和okhttp来实现SOAP协议了。
本文使用到的库:

 // Rxjava    compile 'io.reactivex:rxandroid:1.2.1'    compile 'io.reactivex:rxjava:1.1.6'    // okhttp3    compile 'com.squareup.okhttp3:okhttp:3.7.0'    compile 'com.squareup.okhttp3:logging-interceptor:3.7.0'    compile 'com.squareup.okhttp3:okhttp-urlconnection:3.7.0'    // Retorfit2    compile 'com.squareup.retrofit2:retrofit:2.2.0'    //将网络请求转化成java bean对象 也可以自定义    compile 'com.squareup.retrofit2:converter-gson:2.2.0'    compile 'com.squareup.retrofit2:converter-scalars:2.2.0'    //XML解析    compile('com.squareup.retrofit2:converter-simplexml:2.2.0') {        exclude group: 'xpp3', module: 'xpp3'        exclude group: 'stax', module: 'stax-api'        exclude group: 'stax', module: 'stax'    }    //retrofit和rxandroid连接    compile 'com.squareup.retrofit2:adapter-rxjava:2.2.0'

注:由于SOAP使用的是XML的数据格式,因此需要使用xml解析器,但需要去掉retrofit中默认的xml格式解析的模块。

首先,需要对okhttp3进行一些设置,包括缓存以及超时机制等,直接上代码:

 //------------------------设置缓存策略------------------------------------ OkHttpClient.Builder okHttpClient = getdefOkhttp(); //设置Cache目录 okHttpClient.cache(defcache()); //设置缓存 okHttpClient.addInterceptor(defcacheInterceptor); okHttpClient.addNetworkInterceptor(defcacheInterceptor); //-------------------------------------------------------------- mOkHttpClient = okHttpClient.build();
 /** * 缓存 * * @return */ private static Cache defcache() {    int cacheSize = 10 * 1024 * 1024;    return new Cache(new File(FileUtil.getRootPath() + File.separator + "cache"), cacheSize); }
/*** 缺省OKHttp配置** @return*/private static OkHttpClient.Builder getdefOkhttp() {   //Log相关   HttpLoggingInterceptor logging = new HttpLoggingInterceptor();   logging.setLevel(HttpLoggingInterceptor.Level.BODY);   OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();   okHttpClient.connectTimeout(10, TimeUnit.SECONDS);   okHttpClient.readTimeout(8, TimeUnit.SECONDS);   okHttpClient.writeTimeout(8, TimeUnit.SECONDS);   okHttpClient.addInterceptor(logging);   //失败重连   okHttpClient.retryOnConnectionFailure(true);   return okHttpClient;}

然后,配置retrofit2,注意使用的是SimpleXmlConverterFactory:

Retrofit.Builder retrofitBuilder = new Retrofit.Builder()                .baseUrl(url)                //设置OKHttpClient                .client(mOkHttpClient)                .addConverterFactory(ScalarsConverterFactory.create())                .addConverterFactory(SimpleXmlConverterFactory.create(new Persister(new AnnotationStrategy())))                .addCallAdapterFactory(RxJavaCallAdapterFactory.create());        return retrofitBuilder.build().create(serviceClass);

OK,以上完成了HTTP请求的通信配置,接下来就是组装SOAP请求内容以及解析SOAP返回值的部分。

首先,我们需要使用一个工具:SOAPUI,来确定服务器端的SOAP协议版本,协议的命名空间。方法名称以及参数等信息,举个栗子:

请求内容:

这里写图片描述

应答内容:(请忽略应答错误内容….)
这里写图片描述

整明白请求以及应答的头部以及具体内容之后,接下来就是哟个retrofit的simplexml解析器来组装请求和解析应答:(注解的方式)

请求内容:

// 请求头部最外层接口public abstract class ISOAPReqEnv {    @Element(name = "soapenv:Body")    ISOAPReqBody mBody = null;}// 请求头部body接口public abstract class ISOAPReqBody {}// 请求头部env层以及body层的实现,因为所有请求的这两层格式都一样@Root(name = "soapenv:Envelope")@NamespaceList({        @Namespace(reference = "http://tempuri.org/", prefix = "tem"),        @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soapenv")})public class SOAPReqEnv extends ISOAPReqEnv {    public SOAPReqEnv(ISOAPReqBody body) {        this.mBody = body;    }}......// 登录请求body@Root(name = "soapenv:Body", strict = false)public class SOAPLoginReqBody extends ISOAPReqBody {    @Element(name = "tem:AppLogin")    private SOAPLoginReqParams mLoginParams;    public SOAPLoginReqBody(SOAPLoginReqParams loginParams) {        this.mLoginParams = loginParams;    }}// 登录请求参数@Root(name = "tem:AppLogin", strict = false)public class SOAPLoginReqParams {    @Element(name = "tem:key")    private String mKey;    @Element(name = "tem:userid")    private String mUserId;    @Element(name = "tem:pwd")    private String mPwd;    @Element(name = "tem:hospitalName")    private String hospital;    public SOAPLoginReqParams(String key, String userId, String pwd) {        this.mKey = key;        this.mUserId = userId;        this.mPwd = pwd;    }}

说明一下参数,如果参数中存在数组,可使用数组注解:

@ElementArray(name = "tem:filespath", entry = "tem:string")    private String[] mFilePaths;

应答内容:

// 应答头部最外层@Root(name = "Envelope")@NamespaceList({        @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),        @Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),        @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap")})public class SOAPLoginResEnv {    @Element(name = "Body")    SOAPLoginResBody mBody = null;    public SOAPLoginResBody getBody() {        return mBody;    }}// 应答头部body@Root(name = "Body", strict = false)public class SOAPLoginResBody {    @Element(name = "userLoginResponse")    private SOAPLoginResParams mLoginParams;    public SOAPLoginResParams getLoginParams() {        return mLoginParams;    }}// 应答内容@Root(name = "userLoginResponse", strict = false)@Namespace(reference = "http://tempuri.org/")public class SOAPLoginResParams{    @Element(name = "userLoginResult")    private String mResult;    public String getResult() {        return mResult;    }    public void setResult(String result) {        mResult = result;    }}

由于我们这边服务端的应答内容都是自行组装的xml格式的字符串,所以我们的应答内容中就只有一个String类型的response。

如果你的应答内容也是一个类似请求参数类型的,那么可以继续使用请求参数类型的注解方式来解析。

最后,由于我们使用了rxjava1的方式来进行一步的网络请求和解析,我们需要这样发送请求和接收应答:

// 发送请求接口public interface IServiceStore {    /*     * 指定请求头:     * "Content-Type: text/xml; charset=utf-8"指定文本格式,及编码格式     * SOAPAction的值为     * 分解为http://tempuri.org/ + userLogin,其实就是命名空间+接口名     */    @Headers({            "Content-Type: text/xml; charset=utf-8",            "SOAPAction: http://tempuri.org/userLogin"    })    @POST("webToPad.asmx")    Observable<SOAPLoginResEnv> userLogin(@Body ISOAPReqEnv requestEnvelope);}    public static void login(ISOAPReqEnv soapReqEnv, final ResultCallback callback) {        createStore();......// 发送请求接收应答的实现        public static void login(ISOAPReqEnv soapReqEnv, final ResultCallback callback) {        createStore();        mRxServiceStore.userLogin(soapReqEnv).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<SOAPLoginResEnv>() {            @Override            public void onCompleted() {            }            @Override            public void onError(@NonNull Throwable e) {                e.printStackTrace();                callback.fail((Exception) e);            }            @Override            public void onNext(@NonNull SOAPLoginResEnv isoapResEnv) {                String result = isoapResEnv.getBody().getLoginParams().getResult();                if (isSuccess(result)) {                    callback.success(result);                } else {                    onError(new Exception(StringUtil.getMiddle(result, "<msg>", "</msg>")));                }            }        });    }...... private static IServiceStore mRxServiceStore;    private static void createStore() {        mRxServiceStore = createService(IServiceStore.class);    }

这样就完成了SOAP协议的实现。

原创粉丝点击