一 在Web Service中運(yùn)用Session
在web service中如下定義
[WebMethod(EnableSession=true)]
public String UpdateSessionHitCount()
{
count++;
if (Session["HitCount"] == null)
...{
Session["HitCount"] = 1;
}
else
...{
Session["HitCount"] = ((int)(Session["HitCount"])) + 1;
}
return "You have accessed this service " + Session["HitCount"].ToString() + " times.Count="+count.ToString();
}
在客戶端如下調(diào)用
SessionService sessionService = new SessionService();
sessionService.CookieContainer = new System.Net.CookieContainer();
sessionService1.UpdateSessionHitCount()
發(fā)現(xiàn)如下現(xiàn)象:
在同一執(zhí)行過(guò)程中UpdateSessionHitCount調(diào)用多少次,HitCount的值就增加多少次,而頁(yè)面刷新或者重新執(zhí)行則HitCount又從1開(kāi)始。所以推斷:只有同一SessionService的調(diào)用才是同一個(gè)Session。
但是又有不好理解的地方:
如果同一對(duì)象的調(diào)用是一個(gè)Session,那跟調(diào)用本地對(duì)象時(shí)的計(jì)數(shù)不是一樣嗎?那還要Session干什么?
于是又有以下實(shí)驗(yàn):
private int count = 0;
public SessionService () ...{
//Uncomment the following line if using designed components
//InitializeComponent();
}
在web service中定義一個(gè)全局變量,然后在UpdateSessionHitCount的方法中將Count++
[WebMethod(EnableSession=true)]
public String UpdateSessionHitCount()
...{
count++;
......
//return count
}
發(fā)現(xiàn)每次調(diào)用Count的值都是1。
結(jié)論:
客戶端new 一個(gè)SessionService對(duì)象,然后調(diào)用其方法,似乎只new 了一次,其實(shí)每次調(diào)用都是一個(gè)新的對(duì)象(對(duì)象產(chǎn)生的過(guò)程還在學(xué)習(xí)中)。但是客戶端同一個(gè)SessionService的多次調(diào)用是一個(gè)Session。所以這個(gè)Session的用法和平時(shí)所常見(jiàn)的Session用法還不是完全相同。
二 在Web Service中運(yùn)用Application變量
[WebMethod(EnableSession = false)]
public String UpdateApplicationHitCounter()
...{
if (Application["HitCounter"] == null)
...{
Application["HitCounter"] = 1;
}
else
...{
Application["HitCounter"] = ((int)Application["HitCounter"]) + 1;
}
return "You have accessed this service " + Application["HitCounter"].ToString() + " times.";
}
這和運(yùn)用Session的區(qū)別是EnableSession不用設(shè)置為True,而且不用設(shè)置CookieContainer為System.Net.CookieContainer的新實(shí)例。而這在運(yùn)用Session時(shí)是必須設(shè)置的。
三 用于異步 WebMethod 調(diào)用的基于事件的新模型
在web服務(wù)中HelloWorld方法如下:
[WebMethod]
public string HelloWorld() ...{
Thread.Sleep(5000);
return "Hello World";
}
同步調(diào)用
HelloWorldWaitService service = new HelloWorldWaitService();
DateTime startTime = DateTime.Now;
service.HelloWorld();
DateTime endTime = DateTime.Now;
TimeSpan timeFromStartToEnd = endTime - startTime;
output.Text = "Total Time (in seconds): " + timeFromStartToEnd.TotalSeconds.ToString();
異步調(diào)用
protected void ParallelButton_Click(object sender, EventArgs e)
...{
HelloWorldWaitService service = new HelloWorldWaitService();
service.HelloWorldCompleted += this.HelloWorldCompleted;
Session["StartTime"] = DateTime.Now;
service.HelloWorldAsync("first call");
service.HelloWorldAsync("second call");
}
public void HelloWorldCompleted(Object sender, HelloWorldCompletedEventArgs args)
...{
String whichCall = (string)args.UserState;
if (whichCall.Equals("second call"))
...{
//we can now compute total time for two asynchronous calls
DateTime endTime = DateTime.Now;
DateTime startTime = (DateTime)Session["StartTime"];
TimeSpan timeFromStartToEnd = endTime - startTime;
output.Text = "Total Time (in seconds): " + timeFromStartToEnd.TotalSeconds;
}
}
其中異步調(diào)用的結(jié)果在args.result中。從調(diào)用HelloWorld方法所需的時(shí)間來(lái)看,同步調(diào)用大致是5.0311534妙, 異步調(diào)用兩次是5.2655239,同步調(diào)用一次是5.0624028。
四 允許運(yùn)行時(shí)選擇 Web 服務(wù)的 ASP.NET Web 服務(wù)客戶端
可以將值存儲(chǔ)在配置文件中,而不是對(duì) Web 服務(wù)的位置進(jìn)行硬編碼或在客戶端代理中使用默認(rèn)位置。這樣,您或管理員可以在不更改代碼的情況下更改配置文件中的位置。
HelloWorldService service = new HelloWorldService();
//Change the location of the Web service in machine.config if desired
service.Url = System.Configuration.ConfigurationSettings.AppSettings["WSUrl"];
output.Text = service.HelloWorld();
五 根據(jù)架構(gòu)對(duì)傳入消息進(jìn)行驗(yàn)證的 Web 服務(wù)
若要在服務(wù)上驗(yàn)證消息,請(qǐng)使用 XmlValidatingReader。驗(yàn)證消息的最佳位置是在 SOAP 擴(kuò)展中。這使您可以完全控制 SOAP 消息的內(nèi)容,并可以在執(zhí)行相對(duì)來(lái)說(shuō)代價(jià)較高的反序列化步驟之前拒絕不符合架構(gòu)的消息。
請(qǐng)注意,本示例在 WebMethod 體中驗(yàn)證消息。這樣做是為了簡(jiǎn)單。無(wú)論在什么位置,驗(yàn)證消息的步驟都是相同的。有關(guān) SOAP 擴(kuò)展中的驗(yàn)證的更多信息,請(qǐng)參考以下 MSDN article(MSDN 文章)。
private string returnMessage = "Success! Validation was successful.";
public MessageValidationService ()
...{
//Uncomment the following line if using designed components
//InitializeComponent();
}
[WebMethod]
public string SendToValidator(string input)
...{
XmlTextReader tr = new XmlTextReader(input,XmlNodeType.Document,null);
XmlValidatingReader vr = new XmlValidatingReader(tr);
XmlSchemaCollection schemas = new XmlSchemaCollection();
schemas.Add("Microsoft.Samples.Web.Services", "
http://localhost/quickstartv20/webservices/samples/MessageValidation/Book.xsd");
vr.Schemas.Add(schemas);
vr.ValidationType = ValidationType.Schema;
vr.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);
try
...{
while (vr.Read())
...{
//do nothing
}
}
catch (Exception ex)
...{
returnMessage = "Failure. An Exception was received, most likely indicating malformed XML. Message: " + ex.Message;
}
return returnMessage;
}
public void ValidationHandler(object sender, ValidationEventArgs args)
...{
returnMessage = "Failure. Validation was not successful. Message: " + args.Message;
}
}
六 引發(fā)帶有自定義信息的 Soap 異常的 ASP.NET Web 服務(wù)
服務(wù)器可以使用 SoapException 將自定義錯(cuò)誤信息發(fā)送到客戶端。引發(fā)(但未捕獲)SoapException 以后,服務(wù)器以 SOAP 錯(cuò)誤的形式在網(wǎng)絡(luò)上發(fā)送錯(cuò)誤信息。該 SOAP 錯(cuò)誤在客戶端上被反序列化回 SoapException。在 SOAP 錯(cuò)誤的 Detail 元素中發(fā)送自定義(計(jì)算機(jī)可讀的)錯(cuò)誤信息。一個(gè) SOAP 錯(cuò)誤還包括一條可以人讀的錯(cuò)誤信息、一個(gè)錯(cuò)誤代碼和一個(gè)可選的 SOAP actor。
示例代碼:
服務(wù)端:
[WebMethod]
public string ThrowSoapException() ...{
string myNS = "Microsoft.Samples.XmlMessaging.WebServices.SoapExceptionSample";
if (true)
...{
XmlDocument doc = new XmlDocument();
XmlNode detail = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace);
XmlNode errorType = doc.CreateNode(XmlNodeType.Element, "ErrorType", myNS);
errorType.InnerText = "Validation";
XmlNode linePos = doc.CreateNode(XmlNodeType.Element, "Position", myNS);
linePos.InnerText = "11";
XmlNode lineNum = doc.CreateNode(XmlNodeType.Element, "Line", myNS);
lineNum.InnerText = "24";
detail.AppendChild(errorType);
detail.AppendChild(linePos);
detail.AppendChild(lineNum);
string errorMsg = "處理消息時(shí)遇到錯(cuò)誤(see Detail element for more information)";
SoapException exc = new SoapException(errorMsg,SoapException.ClientFaultCode,"",detail);
throw exc;
}
return "Hello World";
}
客戶端:
SoapExceptionService service = new SoapExceptionService();
try
...{
string returnValue = service.ThrowSoapException();
}
catch (SoapException exception)
...{
output.Text = "遇到Soap異常";
message.Text = " " + exception.Message;
if (exception.Actor == "")
...{
actor.Text = " (empty)";
}
else
...{
actor.Text = "" + exception.Actor;
}
StringBuilder builder = new StringBuilder();
StringWriter writer = new StringWriter(builder);
XmlTextWriter xtw = new XmlTextWriter(writer);
xtw.WriteString(exception.Detail.OuterXml);
xtw.Flush();
detail.Text = builder.ToString();
xtw.Close();
}
七 SOAP Header
本示例演示如何使用 ASP.NET Web 服務(wù)對(duì) SOAP 標(biāo)頭的支持。該示例使用一個(gè)授權(quán)標(biāo)頭,該標(biāo)頭與含有用戶名/密碼信息的請(qǐng)求一起發(fā)送。第一次調(diào)用 WebMethod 時(shí)未包括 SOAP 標(biāo)頭,因此失敗。第二次調(diào)用 WebMethod 時(shí)包括 SOAP 標(biāo)頭,因此成功返回。
Sample Code:
服務(wù)端
public AuthHeader sHeader;
[WebMethod]
[SoapHeader("sHeader")]
public string SecureMethod() ...{
if (sHeader == null)
return "錯(cuò)誤: 請(qǐng)?zhí)峁┥矸輵{據(jù)";
string usr = sHeader.Username;
string pwd = sHeader.Password;
if(AuthenticateUser(usr,pwd))
...{
return "成功";
}
else
...{
return "失敗";
}
}
private bool AuthenticateUser(string usr, string pwd)//用于驗(yàn)證身份
...{
if ((usr != null) && (pwd != null))//可能是查詢數(shù)據(jù)庫(kù)
...{
return true;
}
return false;
}
}
客戶端:
Response.Write("<font face='verdana'><h4>Using Soap Headers for Custom Authentication</h4>");
// Create a new instance of the UsingSoapHeaders
// proxy class used to call the remote .asmx file
SoapHeaders h = new SoapHeaders();
h.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Call the secure method without credentials
Response.Write("<h5>First call result without SOAP Header: </h5>");
try
...{
Response.Write("<p>");
Response.Write(h.SecureMethod());
Response.Write("</p>");
}
catch (Exception ex)
...{
Response.Write("<pre>");
Response.Write(ex.StackTrace);
Response.Write("</pre>");
}
// Create a new instance of the AuthHeader class
AuthHeader myHeader = new AuthHeader();
//WARNING: This sample is for demonstration purposes only. Username/password information is sent in plain text,
//which should never be done in a real application. It is not secure without modification.
myHeader.Username = "JaneDoe";
myHeader.Password = "password";
// Set the AuthHeader public member of the
// UsingSoapHeaders class to myHeader
h.AuthHeaderValue = myHeader;
// Call the secure method with credentials
Response.Write("<h5>Second call result with SOAP Header: </h5><p>" + h.SecureMethod() + "</p></font>");
客戶端執(zhí)行結(jié)果如下:
Using Soap Headers for Custom Authentication
First call result without SOAP Header:
錯(cuò)誤: 請(qǐng)?zhí)峁┥矸輵{據(jù)
Second call result with SOAP Header:
成功
八 UseDefaultCredentials 功能的簡(jiǎn)單 Web 服務(wù)
對(duì)于客戶端代理類的實(shí)例來(lái)說(shuō),如果將 UseDefaultCredentials 屬性設(shè)置為 true,則客戶端將使用默認(rèn)憑據(jù)對(duì) Web 服務(wù)進(jìn)行身份驗(yàn)證。例如:
UseDefaultCredentialsService service = new UseDefaultCredentialsService();
service.UseDefaultCredentials = true;
將 UseDefaultCredentials 屬性設(shè)置為 true 等效于下列代碼行
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
同時(shí),還需要在站點(diǎn)部署后在IIS中去掉服務(wù)端匿名訪問(wèn),允許Windows集成身份驗(yàn)證。
總結(jié):
本次練習(xí)主要參考ASPNET2.0的QuickStart示例,學(xué)習(xí)了Web 服務(wù)調(diào)用,會(huì)話狀態(tài)的使用,同步、異步調(diào)用,驗(yàn)證,授權(quán),異常。以及WSDL.exe的常見(jiàn)用法。