WebService开发常用功能详解

一、WebService中常用的属性(Attributes)
1. Web Service(Web服务)提供以下三个属性。
    Namespace:此属性的值包含 XML Web Service的默认命名空间。XML命名空间提供了一种在XML文档中创建名称的方法,该名称可由统一资源标识符(URI)标识。如果不指定命名空间,则使用默认命名空间 http://tempuri.org/
    Name:此属性的值包含XML Web Service的名称。在默认情况下,该值是实现XML Web Service的类的名称。
    Description:此属性的值包含描述性消息,此消息将在XML Web Service的说明文件(例如服务说明和服务帮助页)生成后显示给XML Web Service的潜在用户。
   
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        //......
    }
    运行结果:

WebService开发常用功能详解
    《图1》
   
2. WebMethod(Web服务方法)有以下4个常用属性。
   
    Description:是对Web Service方法的描述信息。就像Web Service方法的功能注释,可以让调用者看见的注释。
   
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(Description = "显示所有人员的信息")]
        public InfoData[] GetInfos(out string emsg)
        {
            //...
        }
    }
    运行结果:

WebService开发常用功能详解
    《图2》
   
    EnableSession:指示Web Service是否启动Session标志,主要通过Cookie完成,默认为false。
    默认情况下WebService的方法中不能使用Session,如果使用会产生异常。
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(Description = "显示所有人员的信息")]
        public InfoData[] GetInfos(out string emsg)
        {
            Session["test"] = DateTime.Now.ToString();
            return new InfoDA().Select().ToArray();
        }
    }
    运行结果:

WebService开发常用功能详解
    《图3》
   
    要在WebService中使用Session需要把上面的代码进行如下修改:
   
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(Description = "显示所有人员的信息", EnableSession = true)]
        public InfoData[] GetInfos(out string emsg)
        {
            Session["test"] = DateTime.Now.ToString();
            return new InfoDA().Select().ToArray();
        }
    }
   
    TransactionOption:指示Web Service方法的事务支持。
    要在WebService中使用事务时,不需要编写很多的代码,只需要在WebMethod属性中加上启用事务的声明即可
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod(TransactionOption= TransactionOption.RequiresNew)]
        public void AddInfo(InfoData data)
        {
            new InfoDA().Insert(data);
            new InfoDA().Insert(data);
        }
    }
    在上面的代码中向数据库中插入了两次数据,在第二次插入数据时会产生主键重复的异常。如果查看数据库时我们会发现第一次Insert也没有插入成功。这就是因为我们在该方法上加入了事务的功能。
   
    CacheDuration:设置响应应在缓存中保留的秒数。这样Web Service就不需要重复执行多遍,可以提高访问效率,而CacheDuration就是指定缓存时间的属性。
    示例代码如下:
    [WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
        [WebMethod( EnableSession=true, CacheDuration=10)]
        public string GetCNDateTime()
        {
            return DateTime.Now.ToString("yyyy年MM月dd日hh时mm分ss秒ms毫秒");
        }
    }
    上面的服务方法缓存了10秒钟。
    在我们运行该服务,手动调用方法的时候会发现好像缓存并没有起作用,每次调用的显示的时间总会变化。其实该缓存的效果在直接运行的时候并不会起作用,当我们编写客户端代码调用该服务的时候会发现缓存的确是起作用了。
    客户端代码如下:
    public static void Main(string[] args)
    {
        Services.MyServiceSoapClient client = new Client.Services.MyServiceSoapClient();
        string str = client.GetCNDateTime();
        Console.WriteLine(str);
    }
    在运行该客户端代码的时候,在10秒钟之内的两次运行显示的时间是不变的。
   
二、WebService的调用。
我们可以使用ASP.NET Web程序、WinForm程序、控制台程序和javascript来调用WebService。
ASP.NET、WinForm、控制台调用WebService方法基本一样也很简单,主要是添加Web引用,调用代理类来实现的,这里我们不再举例。
下面我们主要看一下如何使用JavaScript来调用WebService

WebService代码如下:
[WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    [WebMethod]
    public string GetCNDateTime()
    {
        return DateTime.Now.ToString("yyyy年MM月dd日hh时mm分ss秒ms毫秒");
    }
}

客户端JS调用代码如下:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <div id="dd"></div>
    </div>
    </form>
</body>
</html>
<script language="javascript">
    function showTime() {
        //下面我们使用XMLDOM对象对WebService访问,因为WebService是以XML方式传输数据的,所以我们可以使用XMLDOM对象来解析WebService返回的数据
        var obj = new ActiveXObject("Microsoft.XMLDOM");
        //设定读取方式是同步方式
        obj.async = false;
        //指定XMLDOM对象要加载的WebService方法返回的XML数据
        //如果使用开发服务器的运行WebService的话,那需要选运行起这个WebService。
        obj.load("http://localhost/TestWS/Service.asmx/GetCNDateTime");
        //取得返回的XML数据中的文本
        var res = obj.documentElement.text;
        dd.innerHTML = res;
    }
    window.setInterval(showTime, 1000);
</script>

这样运行并不成功会产生下面的问题:

WebService开发常用功能详解
《图4》

这是因为WebService并不接收JavaScript提交的方式。要完善这个问题我们需要在WebService的web.config配置文件中加入下面的配置
<system.web>
        <webServices>
            <protocols>
                <add name="HttpPost"/>
                <add name="HttpGet"/>
            </protocols>
        </webServices>
        .........
</system.web>

WebService开发常用功能详解
《图5》

三、实现异步调用Web Service

通调用Web Service的方法,在Web
Service执行期间客户端会一直等待服务执行完毕才能响应。从而造成客户端UI线程的阻塞假死现象。这时候,异步调用就显得很有用,它可以让客户端在
调用Web Service时,不至于阻塞客户端的UI线程导致假死,还可以在调用Web Service的同时做些其他的处理。
第一种方法,是通过利用Backgroundworker对象实现。
BackgroundWorker

类允许你在单独的专用线程上运行操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面(UI)似乎处于停止响应状态。如果你需要能进
行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用 BackgroundWorker 类方便地解决问题。
class Program
{
    public static void Main(string[] args)
    {
        //实现异步调用
        BackgroundWorker bgw = new BackgroundWorker();
        //DoWork事件是在BackgroundWorker对象调用RunWorkerAsync()方法后要触发的事件
        bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);  
        //RunWorkerCompleted事件是DoWork指向的方法执行完成的回调事件
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        //启运异步调用
        bgw.RunWorkerAsync();
        Debug.WriteLine("Main Function is Over....");
        Console.ReadLine();
    }

static void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.WriteLine("Work is over...");
    }

static void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        Debug.WriteLine("Work is Processing...");
    }
}

打印的结果应当是:
Main Function is Over....
Work is Processing...
Work is over...

其中的Main函数线程和bgw_DoWork线程同时运行的。

第二种方法,是调用Web Service的WebMethod中的Async方法实现。
当添加完Web Service的引用以后,会在本地生成代理类,其中,会有一个和原Web Service方法名字相同而后缀是Async的方法。
在.NET FrameWork3.5中并不会自动产生这种异步方法。需要我们来生成一下。
在“Web引用”上右击,选择“配置服务引用...”

WebService开发常用功能详解
《图6》
在服务引用设置窗口中把“生成异步操作”复选框选中点击确定。

WebService开发常用功能详解
《图7》
在客户端编写代码的时候,我们会看到代理类中的每个方法都多了个异步操作的方法

WebService开发常用功能详解
《图8》
客户端完整代码如下:
public static void Main(string[] args)
{
    Services.MyServiceSoapClient client = new Client.Services.MyServiceSoapClient();
   
client.GetCNDateTimeCompleted += new
EventHandler<Client.Services.GetCNDateTimeCompletedEventArgs>(client_GetCNDateTimeCompleted);
    client.GetCNDateTimeAsync();
    Debug.WriteLine("Doing other things");
    Console.ReadLine();
}
运行结果如图所示:

WebService开发常用功能详解
《图9》

四、保证Web Service的安全
要以安全的方式访问Web服务方法,可以考虑以下安全措施:
    是谁调用?——SoapHeader身份认证。
    来自哪里?——访问IP认证。
    加密传输 ——SSL安全访问。
在这里我们主要来讨论前两种措施,至于SSL安全访问需要对操作系统进行配置,这里不再赘述。
(一)SoapHeader身份认证。
1.在Web Service中定义自己的SoapHeader派生类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services.Protocols;

public class MySoapHeader:SoapHeader
{
    private string _UserName;

public string UserName
    {
        get { return _UserName; }
        set { _UserName = value; }
    }
    private string _Password;

public string Password
    {
        get { return _Password; }
        set { _Password = value; }
    }
    public MySoapHeader() { }
    public MySoapHeader(string uid,string pwd)
    {
        _UserName = uid;
        _Password = pwd;
    }
    public bool IsValid(out string errorMsg)
    {
        errorMsg = "";
        if (_UserName == "admin" && _Password == "123")
        {
            return true;
        }
        else
        {
            errorMsg = "您无权调用该服务";
            return false;
        }
    }
}

2.在Web Service中添加基于SoapHeader验证的Web Service接口方法:
[WebService(Description="测试WebService属性", Name="MyService", Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
    public MySoapHeader SoapHeader = new MySoapHeader();
   [SoapHeader("SoapHeader")]
    [WebMethod(Description = "显示所有人员的信息")]
    public InfoData[] GetInfos(out string emsg)
    {
        if (!SoapHeader.IsValid(out emsg))
            return null;
        return new InfoDA().Select().ToArray();
    }
}

3.在客户端调用具有SoapHeader的Web Service
Services.MyServiceSoapClient client = new Client.Services.MyServiceSoapClient();
Services.MySoapHeader header = new Client.Services.MySoapHeader();
header.UserName = "admin";
header.Password = "asasas";

e.Result = client.GetInfos(header,out outmsg);
Console.WriteLine(outmsg);

WebService开发常用功能详解

《图10》

(二)访问IP认证。
对来源IP的检查来进行验证,我们只允许指定IP的服务器来访问,保证点对点的安全。
bool ValidateIP(int UserID, out string exceptionInfo)
{
    exceptionInfo = "";
    string uip = HttpContext.Current.Request.UserHostAddress;
    Common dal = new Common();
    List<string> ips = dal.GetPermitIp(UserID);//得到该用户ID所允许的IP列表
    if (ips == null || ips.Count == 0)
    {
        exceptionInfo = "调用Web服务的客户端IP未被允许,无法访问!";
        return false;
    }
    if (ips.Contains(uip)) //允许IP列表中包含该IP
    {
        return true;
    }
    exceptionInfo = "调用Web服务的客户端IP未被允许,无法访问!";
    return false;
}
在具体Web方法里调用该方法检查用户访问者是否是以我们允许的IP进行访问的,以确保安全。
    优点:简单,防止非指定客户机器访问。
    缺点:IP是可以伪造的;维护IP地址表比较烦琐。且只适合于固定IP访问者的情况。

Web Service开发中需要注意的问题
WebMethod的名字、参数和返回值应该一看就知道这接口大概是干什么用的。
WebMethod参数要尽量简单。只有一个参数的服务接口,往往不能满足业务需求。但过多的参数也提高了出错的几率
WebMethod要提供对参数和返回值的校验
WebMethod返回值应该是简单的语言无关
WebMethod谨慎地抛出异常
    对Web Service中的任何异常都应该进行相应的处理。可以简单地归纳为以下两种情况。
        第1种情况是返回值是简单类型,比如bool型,就true和false两种情况,不抛出异常怎么办?选择有两种,一是抛出异常,二是改变接口,返回int用1和0对应true和false,用-1对应系统异常。
        第2种情况是返回值是复杂对象,可以通过参数out string exceptionInfo来返回异常信息。

WebMethod禁用HTTP POST/GET协议
    Web服务绑定到3种协议:HTTP/POST、HTTP/GET和SOAP。HTTP/GET的安全性不如SOAP。
WebMethod避免使用ASP.NET会话状态

上一篇:【bzoj1071】[SCOI2007]组队


下一篇:JPA 系列教程9-双向一对一唯一外键