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

xiaoxiao2021-02-28  151

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

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

POST /WebServices/WeatherWebService.asmx HTTP/1.1 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.3603) Content-Type: text/xml; charset=utf-8 SOAPAction: "http://WebXml.com.cn/getSupportCity" Host: www.webxml.com.cn Content-Length: 348 Expect: 100-continue Connection: 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协议的实现。

转载请注明原文地址: https://www.6miu.com/read-25222.html

最新回复(0)