一 在Web Service中运用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();
}
在客户端如下调用
SessionService sessionService
=
new
SessionService();
sessionService.CookieContainer
=
new
System.Net.CookieContainer();
sessionService1.UpdateSessionHitCount()
发现如下现象:
在同一执行过程中UpdateSessionHitCount调用多少次,HitCount的值就增加多少次,而页面刷新或者重新执行则HitCount又从1开始。所以推断:只有同一SessionService的调用才是同一个Session。
但是又有不好理解的地方:
如果同一对象的调用是一个Session,那跟调用本地对象时的计数不是一样吗?那还要Session干什么?
于是又有以下实验:
private
int
count
=
0
;
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
SessionService ()
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
//Uncomment the following line if using designed components
//InitializeComponent();
}
在web service中定义一个全局变量,然后在UpdateSessionHitCount的方法中将Count++
[WebMethod(EnableSession
=
true
)]
public
String UpdateSessionHitCount()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
count++;
......
//return count
}
发现每次调用Count的值都是1。
结论:
客户端new 一个SessionService对象,然后调用其方法,似乎只new 了一次,其实每次调用都是一个新的对象(对象产生的过程还在学习中)。但是客户端同一个SessionService的多次调用是一个Session。所以这个Session的用法和平时所常见的Session用法还不是完全相同。
二 在Web Service中运用Application变量
[WebMethod(EnableSession
=
false
)]
public
String UpdateApplicationHitCounter()
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
if (Application["HitCounter"] == null)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
Application["HitCounter"] = 1;
}
else
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
Application["HitCounter"] = ((int)Application["HitCounter"]) + 1;
}
return "You have accessed this service " + Application["HitCounter"].ToString() + " times.";
}
这和运用Session的区别是EnableSession不用设置为True,而且不用设置CookieContainer为System.Net.CookieContainer的新实例。而这在运用Session时是必须设置的。
三 用于异步 WebMethod 调用的基于事件的新模型
在web服务中HelloWorld方法如下:
[WebMethod]
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
string
HelloWorld()
...
{
Thread.Sleep(5000);
return "Hello World";
}
同步调用
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();
异步调用
protected
void
ParallelButton_Click(
object
sender, EventArgs e)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
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)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
String whichCall = (string)args.UserState;
if (whichCall.Equals("second call"))
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
//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;
}
}
其中异步调用的结果在args.result中。从调用HelloWorld方法所需的时间来看,同步调用大致是5.0311534妙, 异步调用两次是5.2655239,同步调用一次是5.0624028。
四 允许运行时选择 Web 服务的 ASP.NET Web 服务客户端
可以将值存储在配置文件中,而不是对 Web 服务的位置进行硬编码或在客户端代理中使用默认位置。这样,您或管理员可以在不更改代码的情况下更改配置文件中的位置。
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();
五 根据架构对传入消息进行验证的 Web 服务
若要在服务上验证消息,请使用 XmlValidatingReader。验证消息的最佳位置是在 SOAP 扩展中。这使您可以完全控制 SOAP 消息的内容,并可以在执行相对来说代价较高的反序列化步骤之前拒绝不符合架构的消息。
请注意,本示例在 WebMethod 体中验证消息。这样做是为了简单。无论在什么位置,验证消息的步骤都是相同的。有关 SOAP 扩展中的验证的更多信息,请参考以下 MSDN article(MSDN 文章)。
[WebService(Namespace
=
"
http://tempuri.org/
"
)]
[WebServiceBinding(ConformsTo
=
WsiProfiles.BasicProfile1_1)]
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
class
MessageValidationService : System.Web.Services.WebService
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
private string returnMessage = "Success! Validation was successful.";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
public MessageValidationService ()
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
//Uncomment the following line if using designed components
//InitializeComponent();
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
[WebMethod]
public string SendToValidator(string input)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
XmlTextReader tr = new XmlTextReader(input,XmlNodeType.Document,null);
XmlValidatingReader vr = new XmlValidatingReader(tr);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
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
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
while (vr.Read())
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
//do nothing
}
}
catch (Exception ex)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
returnMessage = "Failure. An Exception was received, most likely indicating malformed XML. Message: " + ex.Message;
}
return returnMessage;
}
public void ValidationHandler(object sender, ValidationEventArgs args)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
returnMessage = "Failure. Validation was not successful. Message: " + args.Message;
}
}
六 引发带有自定义信息的 Soap 异常的 ASP.NET Web 服务
服务器可以使用 SoapException 将自定义错误信息发送到客户端。引发(但未捕获)SoapException 以后,服务器以 SOAP 错误的形式在网络上发送错误信息。该 SOAP 错误在客户端上被反序列化回 SoapException。在 SOAP 错误的 Detail 元素中发送自定义(计算机可读的)错误信息。一个 SOAP 错误还包括一条可以人读的错误信息、一个错误代码和一个可选的 SOAP actor。
示例代码:
服务端:
[WebMethod]
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
string
ThrowSoapException()
...
{
string myNS = "Microsoft.Samples.XmlMessaging.WebServices.SoapExceptionSample";
if (true)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
XmlDocument doc = new XmlDocument();
XmlNode detail = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
XmlNode errorType = doc.CreateNode(XmlNodeType.Element, "ErrorType", myNS);
errorType.InnerText = "Validation";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
XmlNode linePos = doc.CreateNode(XmlNodeType.Element, "Position", myNS);
linePos.InnerText = "11";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
XmlNode lineNum = doc.CreateNode(XmlNodeType.Element, "Line", myNS);
lineNum.InnerText = "24";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
detail.AppendChild(errorType);
detail.AppendChild(linePos);
detail.AppendChild(lineNum);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
string errorMsg = "处理消息时遇到错误(see Detail element for more information)";
SoapException exc = new SoapException(errorMsg,SoapException.ClientFaultCode,"",detail);
throw exc;
}
return "Hello World";
}
客户端:
SoapExceptionService service
=
new
SoapExceptionService();
try
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
string returnValue = service.ThrowSoapException();
}
catch
(SoapException exception)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
output.Text = "遇到Soap异常";
message.Text = " " + exception.Message;
if (exception.Actor == "")
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
actor.Text = " (empty)";
}
else
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
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 服务对 SOAP 标头的支持。该示例使用一个授权标头,该标头与含有用户名/密码信息的请求一起发送。第一次调用 WebMethod 时未包括 SOAP 标头,因此失败。第二次调用 WebMethod 时包括 SOAP 标头,因此成功返回。
Sample Code:
服务端
/**/
/// <summary>
/// AuthHeader class extends from SoapHeader
/// </summary>
public
class
AuthHeader : SoapHeader
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
public string Username;
public string Password;
}
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
/**/
/// <summary>
/// Summary description for SoapHeaders
/// </summary>
[WebService(Namespace
=
"
http://tempuri.org/
"
)]
[WebServiceBinding(ConformsTo
=
WsiProfiles.BasicProfile1_1)]
public
class
SoapHeaders : System.Web.Services.WebService
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
public AuthHeader sHeader;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
[WebMethod]
[SoapHeader("sHeader")]
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public string SecureMethod() ...{
if (sHeader == null)
return "错误: 请提供身份凭据";
string usr = sHeader.Username;
string pwd = sHeader.Password;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
if(AuthenticateUser(usr,pwd))
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
return "成功";
}
else
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
return "失败";
}
}
private bool AuthenticateUser(string usr, string pwd)//用于验证身份
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
if ((usr != null) && (pwd != null))//可能是查询数据库
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
...{
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;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
Call the secure method without credentials
Response.Write(
"
<h5>First call result without SOAP Header: </h5>
"
);
try
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
Response.Write("<p>");
Response.Write(h.SecureMethod());
Response.Write("</p>");
}
catch
(Exception ex)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
...
{
Response.Write("<pre>");
Response.Write(ex.StackTrace);
Response.Write("</pre>");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
//
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>
"
);
客户端执行结果如下:
Using Soap Headers for Custom Authentication
First call result without SOAP Header:
错误: 请提供身份凭据
Second call result with SOAP Header:
成功
八 UseDefaultCredentials 功能的简单 Web 服务
对于客户端代理类的实例来说,如果将 UseDefaultCredentials 属性设置为 true,则客户端将使用默认凭据对 Web 服务进行身份验证。例如:
UseDefaultCredentialsService service
=
new
UseDefaultCredentialsService();
service.UseDefaultCredentials
=
true
;
将 UseDefaultCredentials 属性设置为 true 等效于下列代码行
service.Credentials
=
System.Net.CredentialCache.DefaultCredentials;
同时,还需要在站点部署后在IIS中去掉服务端匿名访问,允许Windows集成身份验证。
总结:
本次练习主要参考ASPNET2.0的QuickStart示例,学习了Web 服务调用,会话状态的使用,同步、异步调用,验证,授权,异常。以及WSDL.exe的常见用法。