C# WebClient調(diào)用WebService
WebClient調(diào)用WebService
(文末下載完整代碼)
先上代碼:
object[] inObjects = new[] { "14630, 14631" }; HttpWebClient wc = new HttpWebClient(2300); var result1 = WebServiceClientHelper.InvokeWebService("ESBService_TEST", "http://localhost/ESBService/VitalSign.svc?wsdl", "QueryVocabSet", inObjects, wc); WriteLine(result1.ToString());
public class HttpWebClient : WebClient { /// <summary> /// 初始化需要設(shè)置超時(shí)時(shí)間,以毫秒為單位 /// </summary> /// <param name="timeout">毫秒為單位</param> public HttpWebClient(int timeout) { Timeout = timeout; } public int Timeout { get; set; } /// <summary> /// 重寫 GetWebRequest,添加 WebRequest 對(duì)象超時(shí)時(shí)間 /// </summary> protected override WebRequest GetWebRequest(Uri address) { HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address); request.Timeout = Timeout; request.ReadWriteTimeout = Timeout; return request; } }
HttpWebRequest 改造依據(jù):
|
WebClient |
HttpWebRequest |
|
提供用于將數(shù)據(jù)發(fā)送到由 URI 標(biāo)識(shí)的資源及從這樣的資源接收數(shù)據(jù)的常用方法。 |
public class HttpWebRequest : WebRequest, ISerializable |
|
Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.dll |
Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.dll |
protected virtual WebRequest GetWebRequest(Uri address)
{
WebRequest request = WebRequest.Create(address);
this.CopyHeadersTo(request);
if (this.Credentials != null)
request.Credentials = this.Credentials;
if (this.m_Method != null)
request.Method = this.m_Method;
if (this.m_ContentLength != -1L)
request.ContentLength = this.m_ContentLength;
if (this.m_ProxySet)
request.Proxy = this.m_Proxy;
if (this.m_CachePolicy != null)
request.CachePolicy = this.m_CachePolicy;
return request;
}
protected virtual WebResponse GetWebResponse(WebRequest request)
{
WebResponse response = request.GetResponse();
this.m_WebResponse = response;
return response;
}
|
public class HttpWebClient : WebClient
{
/// <summary>
/// 初始化需要設(shè)置超時(shí)時(shí)間,以毫秒為單位
/// </summary>
/// <param name="timeout">毫秒為單位</param>
public HttpWebClient(int timeout)
{
Timeout = timeout;
}
public int Timeout { get; set; }
/// <summary>
/// 重寫 GetWebRequest,添加 WebRequest 對(duì)象超時(shí)時(shí)間
/// </summary>
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.Timeout = Timeout;
request.ReadWriteTimeout = Timeout;
return request;
}
}
|
調(diào)用服務(wù)處理:
public static object InvokeWebService(string providerName, string url, string methodName, object[] args, WebClient wc = null) { object result = null; if (wc == null) wc = new WebClient(); using (wc) { using (Stream wsdl = wc.OpenRead(url)) { var client = GetClient(wsdl, url, methodName, providerName); client.SetValue("Timeout", wsdl.ReadTimeout); result = client.InvokeService(args); } } return result; }
形如這樣 http://192.168.2.100:8090/services/dududuTest?wsdl 的地址,
返回的是 dududuTest 服務(wù)下公開的方法,以流的形式,
代碼處理里面需要解讀這種流,目前看到的一種方式是,把這個(gè)流解讀編譯成一個(gè)動(dòng)態(tài)的dll,利用反射,動(dòng)態(tài)調(diào)用方法。
/// <summary>為從具有 <see cref="T:System.String" /> 指定的 URI 的資源下載的數(shù)據(jù)打開一個(gè)可讀的流。</summary> /// <returns>一個(gè) <see cref="T:System.IO.Stream" />,用于從資源讀取數(shù)據(jù)。</returns> /// <param name="address">以 <see cref="T:System.String" /> 形式指定的 URI,將從中下載數(shù)據(jù)。</param> public Stream OpenRead(string address) { if (address == null) throw new ArgumentNullException(nameof (address)); return this.OpenRead(this.GetUri(address)); } /// <summary>為從具有 <see cref="T:System.Uri" /> 指定的 URI 的資源下載的數(shù)據(jù)打開一個(gè)可讀的流</summary> /// <returns>一個(gè) <see cref="T:System.IO.Stream" />,用于從資源讀取數(shù)據(jù)。</returns> /// <param name="address">以 <see cref="T:System.Uri" /> 形式指定的 URI,將從中下載數(shù)據(jù)。</param> public Stream OpenRead(Uri address) { if (Logging.On) Logging.Enter(Logging.Web, (object) this, nameof (OpenRead), (object) address); if (address == (Uri) null) throw new ArgumentNullException(nameof (address)); WebRequest request = (WebRequest) null; this.ClearWebClientState(); try { request = this.m_WebRequest = this.GetWebRequest(this.GetUri(address)); Stream responseStream = (this.m_WebResponse = this.GetWebResponse(request)).GetResponseStream(); if (Logging.On) Logging.Exit(Logging.Web, (object) this, nameof (OpenRead), (object) responseStream); return responseStream; } catch (Exception ex) { Exception innerException = ex; if (innerException is ThreadAbortException || innerException is StackOverflowException || innerException is OutOfMemoryException) { throw; } else { if (!(innerException is WebException) && !(innerException is SecurityException)) innerException = (Exception) new WebException(SR.GetString("net_webclient"), innerException); WebClient.AbortRequest(request); throw innerException; } } finally { this.CompleteWebClientState(); } }
類 DefaultWebServiceClient 定義:
public class DefaultWebServiceClient { Type _type; MethodInfo _method; object _obj; public object InvokeService(object[] args) { object proxy = GetProxy(); return _method.Invoke(proxy, args); } public void SetValue(string fieldName, object value) { object proxy = GetProxy(); PropertyInfo field = _type.GetProperty(fieldName); if (field != null) field.SetValue(proxy, value); } public object GetProxy() { if (_obj == null) _obj = Activator.CreateInstance(_type); return _obj; } public MethodInfo MethodInfo { get { return _method; } } public DefaultWebServiceClient(Stream wsdl, string url, string methodname, string providerName) { if (wsdl == null || (wsdl.CanWrite && wsdl.Length == 0)) throw new Exception("Wsdl為空"); try { ServiceDescription sd = ServiceDescription.Read(wsdl); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd, "", ""); CodeNamespace cn = new CodeNamespace(string.Format("DefaultWebServiceClient_{0}_{1}", providerName, wsdl.GetHashCode().ToString())); DiscoveryClientProtocol dcp = new DiscoveryClientProtocol(); dcp.DiscoverAny(url); dcp.ResolveAll(); foreach (object osd in dcp.Documents.Values) { if (osd is ServiceDescription) sdi.AddServiceDescription((ServiceDescription)osd, null, null); ; if (osd is XmlSchema) sdi.Schemas.Add((XmlSchema)osd); } //生成客戶端代理類代碼 CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); CSharpCodeProvider csc = new CSharpCodeProvider(); ICodeCompiler icc = csc.CreateCompiler(); //設(shè)定編譯器的參數(shù) CompilerParameters cplist = new CompilerParameters(); cplist.GenerateExecutable = false; cplist.GenerateInMemory = true; cplist.ReferencedAssemblies.Add("System.dll"); cplist.ReferencedAssemblies.Add("System.XML.dll"); cplist.ReferencedAssemblies.Add("System.Web.Services.dll"); cplist.ReferencedAssemblies.Add("System.Data.dll"); //編譯代理類 CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu); if (true == cr.Errors.HasErrors) { System.Text.StringBuilder sb = new StringBuilder(); foreach (CompilerError ce in cr.Errors) { sb.Append(ce.ToString()); sb.Append(System.Environment.NewLine); } throw new Exception(sb.ToString()); } //生成代理實(shí)例,并調(diào)用方法 Assembly assembly = cr.CompiledAssembly; Type type = null; foreach (Type t in assembly.GetExportedTypes()) { if (t.GetCustomAttributes(typeof(System.Web.Services.WebServiceBindingAttribute), false).Count() > 0) { type = t; break; } } MethodInfo mi = null; if (type == null) { throw new Exception("從Wsdl中找不到可調(diào)用的類型"); } mi = type.GetMethod(methodname); if (mi == null) { throw new Exception(string.Format("從Wsdl中找不到可調(diào)用的方法{0}.{1}", type.Name, methodname)); } _type = type; _method = mi; } catch (Exception ex) { throw new Exception("創(chuàng)建WebService客戶端失敗!", ex); } } }
WebClient 是一個(gè)操作 WebRequest 的類型,相當(dāng)于一個(gè) worker,它把服務(wù)器能提供的內(nèi)容(資源)都以流的形式返回來,供我們調(diào)用,
解讀這種流,需要 ServiceDescription 類實(shí)例,再將每項(xiàng)加載入 ServiceDescriptionImporter 類的實(shí)例中,
再把它編譯成能本地使用的組件,動(dòng)態(tài)地調(diào)用,相當(dāng)于運(yùn)行時(shí)加載了一個(gè)dll。
(注:三種方法調(diào)用 webService https://www.jb51.net/article/190211.htm)
|
|
驗(yàn)證 1 |
驗(yàn)證 2 |
|
|
新建了一個(gè)webClient(服務(wù)代理), 它請(qǐng)求的處理對(duì)象默認(rèn)是webRequest, 因?yàn)閣ebRequest沒有TimeOut屬性,于是我繼承了webCLient,override處理對(duì)象為HttpWebRequest (注,HttpWebRequest 是 WebRequest的子類) |
新建了一個(gè)webClient, 它請(qǐng)求的處理對(duì)象默認(rèn)是webRequest
|
|
結(jié)果 |
在完成整個(gè)調(diào)用當(dāng)中charles獲取了2次http請(qǐng)求,其中: 第一次:在獲取wsdl流的時(shí)候,請(qǐng)求包是http請(qǐng)求的get方式; 第二次:加載動(dòng)態(tài)dll反射調(diào)用方法時(shí),請(qǐng)求包是http請(qǐng)求的post方式; |
同左 |
|
證 |
webClient真的是一個(gè)代理,像一個(gè)worker,代碼上看似在本地動(dòng)態(tài)引用了第三方的dll,但實(shí)際上還是http請(qǐng)求 |
就算不寫死類型為 HttpWebRequest,識(shí)別出來的對(duì)象仍然是HttpWebRequest 的實(shí)例,這個(gè)服務(wù)本質(zhì)提供的就是 http 請(qǐng)求 |
|
待證 |
|
識(shí)別為http請(qǐng)求應(yīng)該是url的前綴是http的原因,如果是ftp://...那么識(shí)別出來就應(yīng)該是ftpWebRequest |
這種 WebClient 的方式應(yīng)該只適用于取 wsdl 來處理,
我嘗試直接用 HttpWebRequest 用 get 方法取 http://localhost/ESBService/VitalSign.svc?wsdl 的內(nèi)容,返回了一段服務(wù)中的方法說明 xml,
而我試過直接照著第二次包的請(qǐng)求內(nèi)容來發(fā) HttpWebRequest,并不能成功(報(bào)錯(cuò)500內(nèi)部服務(wù)器出錯(cuò)),可能是我入?yún)⑻幚聿粚?duì)。
WebClient 方式和直接發(fā) HttpWebRequest 方式應(yīng)該是互通的,可以互相轉(zhuǎn)換的,
不過 WebClient 使用 Httpwebrequest 的方式封裝了別的處理,某些程度上減少了程序員的工作,總歸是數(shù)據(jù)結(jié)構(gòu)整合的問題,這塊就不多研究了。
WebClient封裝 HttpWebRequest 的部分內(nèi)容(頭部等),它還提供了一些方法,
這里有一個(gè)示例,是異步請(qǐng)求,得到響應(yīng)后觸發(fā)事件的適用,不過這個(gè)實(shí)例比較久遠(yuǎn)了,2008年的:
https://docs.microsoft.com/zh-cn/archive/blogs/silverlight_sdk/using-webclient-and-httpwebrequest
.Net當(dāng)中,WebRequest 與 HttpWebRequest 區(qū)別,從名字感覺很相似,反編譯結(jié)果確實(shí)是。
|
由客服端構(gòu)建的(不同協(xié)議的)請(qǐng)求發(fā)給服務(wù)器 |
||||
|
WebRequest |
FtpWebRequest |
FileWebRequest |
HttpWebRequest |
WebSocket |
|
抽象基類 |
繼承于WebRequest |
繼承于WebRequest |
繼承于WebRequest |
抽象基類 |
|
與WebResponse成對(duì)存在 |
ftp:// 開頭 |
file:// 開頭,一般是打開本機(jī)文件, 形如: file:///C:/Users/dududu/0209.pdf file://dududu/Share2021/ |
http:// 開頭 |
ws:// 開頭 wss:// |
|
|
|
|
Websocket主要是javaScript在頁面中使用,不需要像http請(qǐng)求那樣等待一整個(gè)頁面文件返回,只取業(yè)務(wù)數(shù)據(jù)。 |
|
放一些地址給觀察觀察,感知請(qǐng)求和接受的運(yùn)作模式,各有性格,殊途同歸:
http://192.168.8.100:9000/empId=<EmployeeId>&jobId=<JobId>
get 方式,相當(dāng)于有這樣一個(gè)格式的服務(wù)地址,可以返回一個(gè)超文本內(nèi)容(網(wǎng)頁)或者一些某種格式的數(shù)據(jù)。
(我比較好奇是服務(wù)器怎么響應(yīng)網(wǎng)絡(luò),光知道配置總顯得蒼白)
http://172.18.99.100:8080/yyzx/index.action?userName=<userName>&passWord=<passWord>
有些網(wǎng)址中這樣是等價(jià)的,應(yīng)該是哪里(服務(wù)配置、架構(gòu)配置等)處理到了這個(gè)邏輯:
http://192.168.29.100:8081/emr/index.php/index/emr/getemr?personid=7321446
http://192.168.29.100:8081/emr/index.php/index/emr/getemr/personid/7321446

浙公網(wǎng)安備 33010602011771號(hào)