这个帖子本16号放在首页,不知道为何不见了,也许是我误删除了吧,重新补上,放在我自己的首页。
最近在做手机上一程序,需要通过移动cmwap访问外部的webservice,但使用wsdl生成的代理类通过cmwap网关访问会出现"Client found response content type of 'text/vnd.wap.wml',but expected 'text/xml' ".的异常信息,换用cmnet网络则正常。
应该是通过cmwap网关请求返回的数据被转化为wml类型了,wsdl代理类只认xml;
把google翻了也找不到解决方案,只好自己用最笨的办法,用HttpWebRequest和HttpWebResponse实现访问soap协议。
代码如下:
/**//*
code by Aijoe(原 Ivan Chin)
http://www.lostway.net/
http://www.cnblogs.com/drw/
aijoe@lostway.net
转载请保留此信息。
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;
namespace IvanCF.Soap
{
internal class SoapClient
{
属性#region 属性
private string nameSpace = "http://tempurl.org/";
private Uri serviceUrl;
private IWebProxy webProxy = null;
private ICredentials credentials = null;
private int timeOut = 30000;
private Encoding encoding = Encoding.GetEncoding("UTF-8");
//设置请求的webservice命名空间
public string Namespace
{
get { return nameSpace; }
set { nameSpace = value; }
}
//webservice地址
public Uri ServiceUrl
{
get { return serviceUrl; }
set { serviceUrl = value; }
}
//客户端使用代理
public IWebProxy Proxy
{
get { return webProxy; }
set { webProxy = value; }
}
//客户端验证
public ICredentials Credentials
{
get { return credentials; }
set { credentials = value; }
}
//webservice请求编码格式
public Encoding ContentEncoding
{
get { return encoding; }
set { encoding = value; }
}
//超时时间
public int TimeOut
{
get { return timeOut; }
set { timeOut = value; }
}
#endregion
private const int BUFFER_SIZE = 512;
异步方法#region 异步方法
//用于操作等待线程。
public static ManualResetEvent allDone = new ManualResetEvent(false);
实现事件完成通知#region 实现事件完成通知
//异步调用完成的事件
public event SoapInvokeCompletedEventHandler SoapInvokeCompleted;
protected virtual void OnSoapInvokeCompleted(SoapInvokeCompleteArgs args)
{
if (SoapInvokeCompleted != null)
SoapInvokeCompleted(this, args);
}
#endregion
/**//// <summary>
/// 异步调用webService的方法
/// </summary>
/// <param name="methodName">调用的webService的方法名称</param>
/// <param name="parameters">发送的数据</param>
public void InvokeAsync(string methodName, PostDataArray parameters)
{
RequestState state = new RequestState();
state.MethodName = methodName;
state.Parameters = parameters;
try
{
state.Request = (HttpWebRequest) WebRequest.Create(ServiceUrl);
if (TimeOut > 0) state.Request.Timeout = TimeOut;
if (Proxy != null) state.Request.Proxy = Proxy;
if (Credentials != null) state.Request.Credentials = Credentials;
state.DataSend.Append(ResloveSendData(methodName, parameters));
state.Request.ContentLength = ContentEncoding.GetByteCount(state.DataSend.ToString());
state.Request.Method = "POST";
state.Request.ContentType = "application/soap+xml; charset=" + ContentEncoding.WebName.ToLower();
//开始获取请求流
state.Request.BeginGetRequestStream(new AsyncCallback(ReqCall), state);
//阻塞当前进程,直到收到完成的通知。
allDone.WaitOne();
}
catch (Exception ex)
{
state.Error = ex;
allDone.Set();
}
finally
{
allDone.Reset();
}
string rData = null;
if (state.Error == null)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(state.DataRecived.ToString());
rData = ResloveRecivedData(methodName, doc);
}
string sData = state.DataSend.ToString();
SoapInvokeCompleteArgs args = new SoapInvokeCompleteArgs(methodName,
sData,
rData,
state.Error);
//访问完成通知Soap完成事件。
OnSoapInvokeCompleted(args);
state.Close();
GC.WaitForPendingFinalizers();
GC.Collect();
}
private void ReqCall(IAsyncResult ar)
{
RequestState state = (RequestState) ar.AsyncState;
try
{
//写入请求
StreamWriter streamWriter = new StreamWriter(state.Request.EndGetRequestStream(ar));
streamWriter.Write(state.DataSend.ToString());
streamWriter.Flush();
streamWriter.Close();
//开始取得响应。
state.Request.BeginGetResponse(new AsyncCallback(ResCall), state);
}
catch (Exception ex)
{
state.Error = ex;
allDone.Set();
}
}
private void ResCall(IAsyncResult ar)
{
RequestState state = (RequestState) ar.AsyncState;
try
{
//取响应完毕。
state.Response = (HttpWebResponse) state.Request.EndGetResponse(ar);
state.ResponseStream = state.Response.GetResponseStream();
//开始读取流。
state.ResponseStream.BeginRead(state.BufferRead, 0, BUFFER_SIZE,
new AsyncCallback(ReadCall), state);
}
catch (Exception ex)
{
state.Error = ex;
allDone.Set();
}
}
private void ReadCall(IAsyncResult ar)
{
RequestState state = (RequestState) ar.AsyncState;
try
{
//分段读取流。
int read = state.ResponseStream.EndRead(ar);
if (read > 0)
{
string strRes = ContentEncoding.GetString(state.BufferRead, 0, read);
state.DataRecived.Append(strRes);
state.ResponseStream.BeginRead(state.BufferRead, 0, BUFFER_SIZE,
new AsyncCallback(ReadCall), state);
}
else
{
allDone.Set();
}
}
catch (Exception ex)
{
state.Error = ex;
allDone.Set();
}
}
#endregion
同步方法#region 同步方法
/**//// <summary>
/// 同步调用webService的方法
/// </summary>
/// <param name="methodName">方法名称</param>
/// <param name="data">发送的xml数据</param>
/// <returns>返回的xml数据</returns>
public InvokeResult Invoke(string methodName, PostDataArray data)
{
InvokeResult result = new InvokeResult();
result.MethodName = methodName;
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(ServiceUrl);
if (TimeOut > 0) request.Timeout = TimeOut;
if (Proxy != null) request.Proxy = Proxy;
if (Credentials != null) request.Credentials = Credentials;
result.DataSend = ResloveSendData(methodName, data);
request.ContentLength = ContentEncoding.GetByteCount(result.DataSend);
request.Method = "POST";
request.ContentType = "application/soap+xml; charset=" + ContentEncoding.WebName.ToLower();
StreamWriter streamWriter = new StreamWriter(request.GetRequestStream());
streamWriter.Write(result.DataSend);
streamWriter.Flush();
streamWriter.Close();
HttpWebResponse response = null;
Stream responseStream = null;
try
{
response = (HttpWebResponse) request.GetResponse();
responseStream = response.GetResponseStream();
XmlDocument doc = new XmlDocument();
doc.Load(responseStream);
result.DataRecived = ResloveRecivedData(methodName, doc);
}
catch (Exception ex)
{
result.Error = ex;
//AppNet.GetLog().wLog(ex.ToString(), "Exception at " + invokeMethod + " Invoke");
}
finally
{
if (response != null) response.Close();
if (responseStream != null) responseStream.Close();
GC.WaitForPendingFinalizers();
GC.Collect();
}
return result;
}
#endregion
helper Method#region helper Method
//将发送的数据从PostDataArray转化成soap1.2的xml数据格式。
private string ResloveSendData(string invokeMethod, PostDataArray data)
{
string soapNs = "http://www.w3.org/2003/05/soap-envelope";
string strSoapData;
using (MemoryStream stream = new MemoryStream())
{
using (XmlTextWriter writer = new XmlTextWriter(stream, ContentEncoding))
{
writer.WriteStartDocument();
writer.WriteStartElement("soap12", "Envelope", soapNs);
writer.WriteStartElement("soap12", "Body", soapNs);
writer.WriteStartElement(invokeMethod, Namespace);
AddData2XmlWriter(writer, data);
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream))
{
strSoapData = reader.ReadToEnd();
}
}
}
return strSoapData;
}
//我对返回的xml数据进行了简单的处理,仅留下了<方法名Result></方法名Result>这一节,以便后续处理。
private string ResloveRecivedData(string invokeMethod, XmlDocument doc)
{
bool IsSoap12 = false;
if (doc.OuterXml.IndexOf("soap12:Envelope") != -1) IsSoap12 = true;
XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable);
if (IsSoap12)
nsmanager.AddNamespace("soap12", "http://www.w3.org/2003/05/soap-envelope");
else
nsmanager.AddNamespace("soap", "http://www.w3.org/2003/05/soap-envelope");
string xpath = "//soap:Envelope/soap:Body";
if (IsSoap12) xpath = "//soap12:Envelope/soap12:Body";
XmlNode node = doc.SelectSingleNode(xpath, nsmanager);
string strDoc = node.InnerXml;
strDoc =
strDoc.Replace("<" + invokeMethod + "Response xmlns=\"" + Namespace + "\"",
"<" + invokeMethod + "Response");
doc = new XmlDocument();
doc.LoadXml(strDoc);
XmlNode rNode = doc.SelectSingleNode(invokeMethod + "Response");
strDoc = rNode.InnerXml;
return strDoc;
}
//添加PostDataArray到XmlWriter(这个方法为ResloveSendData服务)
private void AddData2XmlWriter(XmlWriter writer, PostDataArray datas)
{
if (datas.ColName.Length > 0) writer.WriteStartElement(datas.ColName);
foreach (PostData d in datas)
{
writer.WriteStartElement(d.Key);
if (d.Value.GetType() == typeof (byte[]))
{
byte[] bt = (byte[]) d.Value;
writer.WriteBase64(bt, 0, bt.Length);
}
else
writer.WriteValue(d.Value);
writer.WriteEndElement();
}
if (datas.ColName.Length > 0) writer.WriteEndElement();
if (datas.Childs.Count > 0)
foreach (PostDataArray col in datas.Childs)
AddData2XmlWriter(writer, col);
}
#endregion
}
这里是几个辅助类,自己去看看吧#region 这里是几个辅助类,自己去看看吧
//存储同步调用后的结果
internal struct InvokeResult
{
public Exception Error;
public string DataRecived;
public string DataSend;
public string MethodName;
}
internal struct PostData
{
public PostData(string key, object value)
{
Key = key;
Value = value;
}
public string Key;
public object Value;
}
internal class PostDataArray : CollectionBase
{
public PostDataArray()
{
dataCols = new List<PostDataArray>();
}
private List<PostDataArray> dataCols;
private string colN = "";
public string ColName
{
get { return colN; }
set { colN = value; }
}
public PostData this[int index]
{
get { return (PostData) List[index]; }
}
public List<PostDataArray> Childs
{
get { return dataCols; }
}
public void Add(PostData obj)
{
List.Add(obj);
}
public void Add(string key, object value)
{
List.Add(new PostData(key, value));
}
public void Add(PostDataArray obj)
{
dataCols.Add(obj);
}
public new void Clear()
{
base.Clear();
dataCols.Clear();
}
}
internal class RequestState
{
public const int BUFFER_SIZE = 512;
public byte[] BufferRead;
public HttpWebRequest Request;
public HttpWebResponse Response;
public Stream ResponseStream;
public Stream RequestStream;
public string MethodName;
public PostDataArray Parameters;
public StringBuilder DataSend;
public StringBuilder DataRecived;
public Exception Error;
public RequestState()
{
BufferRead = new byte[BUFFER_SIZE];
DataRecived = new StringBuilder();
DataSend = new StringBuilder();
Request = null;
ResponseStream = null;
RequestStream = null;
}
public void Close()
{
if (ResponseStream != null) ResponseStream.Close();
if (Response != null) Response.Close();
if (Request != null) Request.Abort();
Response = null;
Request = null;
ResponseStream = null;
RequestStream = null;
DataRecived = null;
DataSend = null;
MethodName = null;
Parameters = null;
}
}
#endregion
//储存事件通知结果
internal struct SoapInvokeCompleteArgs
{
public SoapInvokeCompleteArgs(Exception ex)
{
MethodName = null;
Error = ex;
DataSend = null;
DataRecived = null;
IsCompleted = true;
}
public SoapInvokeCompleteArgs(string data, string methodname)
{
MethodName = methodname;
Error = null;
DataSend = null;
DataRecived = data;
IsCompleted = true;
}
public SoapInvokeCompleteArgs(string methodname, string sendData, string recivedData, Exception error)
{
MethodName = methodname;
DataSend = sendData;
DataRecived = recivedData;
Error = error;
IsCompleted = true;
}
//调用的方法名称
public string MethodName;
//返回的xml数据
public string DataRecived;
//发送给weService的xml数据
public string DataSend;
//调用过程的错误信息
public Exception Error;
//是否已完成
public bool IsCompleted;
}
//委托完成事件
internal delegate void SoapInvokeCompletedEventHandler(object sender, SoapInvokeCompleteArgs e);
}
代码原理比较简单,主要是了解Soap1.2的格式和xml操作。
已经在代码中加入了部分注释。下面是一个调用例子
void getwebservice(){PostDataArray data = new PostDataArray();data.Add("参数1", "参数值1");PostDataArray indata = new PostDataArray();indata.Add("嵌套参数1","嵌套参数值1");indata.ColName = "嵌套节点名称";data.Add(indata);SoapClient soap = new SoapClient();soap.ContentEncoding = Encoding.GetEncoding(65001);soap.Proxy = new WebProxy("代理");soap.ServiceUrl = new Uri("webService地址");soap.TimeOut = 180000;//超时时间(毫秒)//用同步方法调用ws,直接返回InvokeResult结果;soap.Invoke("webService方法名称", data);//用异步方法调用ws,在SoapInvokeCompleted事件中处理结果。soap.SoapInvokeCompleted += new SoapInvokeCompletedEventHandler(soap_SoapInvokeCompleted);soap.InvokeAsync("webService方法名称", data); }