ASP.NET AJAX(4)__客户端访问WebService服务器端释放WebService方法客户端访问WebService客户端访问PageMethod错误处理复杂数据类型使用基础客户端代理的

时间:2022-05-03
本文章向大家介绍ASP.NET AJAX(4)__客户端访问WebService服务器端释放WebService方法客户端访问WebService客户端访问PageMethod错误处理复杂数据类型使用基础客户端代理的,主要内容包括服务器端释放WebService方法、客户端访问WebService、客户端访问PageMethod、错误处理、复杂数据类型使用基础、客户端代理的使用细节、生成复杂参数类型的客户端代理、使用JavaScriptConverter、定义一个JavaScriptConverter、改变客户端访问时的方法名、使用HTTP GET访问WebService方法、让WebService方法返回XML对象、在WebService方法中使用Session、安全性、客户端代理的一些解释、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

服务器端释放WebService方法

  • 编写一个普通的WebService
  • 为WebService类添加自定义的属性标记__ScriptServiceAttribute
  • 释放WebService方法

                  __访问级别为Public

                  __使用WebServiceAttribute进行标记

  • 为页面中的ScriptManager引入asmx文件

客户端访问WebService

  1. [Namespaces.]ClassName.MethodName
  2. 依次传入参数
  3. 传入一个方法作为成功后的回调函数(即使没有返回值)
一个简单的访问WebService示例

         首先创建一个WevService名为WebServiceFoundation,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

/// <summary>
///WebServiceFoundation 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class WebServiceFoundation : System.Web.Services.WebService
{
    [WebMethod]
    public int GetRandom()
    {
        return new Random(DateTime.Now.Millisecond).Next();
    }

    [WebMethod]
    public int GetRangeRandom(int minValue, int maxValue)
    {
        return new Random(DateTime.Now.Millisecond).Next(minValue, maxValue);
    }
}

然后,创建一个页面名为WebServiceFoundation,添加ScriptManager,在它内部添加如下代码

<asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/WebServiceFoundation.asmx" />
            </Services>
        </asp:ScriptManager>

这样,我们就在客户端添加一些关于这个WebService的代码

接下来,我们就在客户端调用这个WebService的GetRandom方法

在页面中添加如下代码

<input type="button" value="Get Random" onclick="getRandom()" />
        <input type="button" value="Get Range Random" onclick="getRandom(100,500)" />
        
        <script language="javascript" type="text/javascript">

            function getRandom(minValue, maxValue) {
                if (arguments.length != 2) {//方法的参数个数,当不等于2时
                    WebServiceFoundation.GetRandom(getRandomSucceeded); //getRandomSucceeded是访问WebService的回调函数
                }
                else {
                    WebServiceFoundation.GetRangeRandom(minValue, maxValue,getRandomSucceeded);
                }
            }
            function getRandomSucceeded(result) {
                alert(result);
            }
        </script>

这样,我们就成功调用了这个WebService,当点击"Get Random"按钮时,弹出一个普通随机数,在单击"Get Range Random"按钮时,弹出一个介于100到500之间的随机数

客户端访问PageMethod

  • 只能在aspx页面中定义
  • 只能是public static方法
  • 使用WebMethodAttribute标记
  • ScriptManager的EnablePageMethod设置为true
  • 通过pageMethods.MethodName进行访问
一个访问PageMethod的示例

创建一个页面,页面代码如下

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
        </asp:ScriptManager>
        
        <input type="button" value="Get Current Time" onclick="getCurrentTime()" />
        
        <script language="javascript" type="text/javascript">
            function getCurrentTime() {
                
                PageMethods.GetCurrentTime(getCurrentTimeSucceeded);
            }
            function getCurrentTimeSucceeded(result) {
                alert(result);
            }
        </script>
    </form>
</body>
</html>

后台代码中添加

[WebMethod]
    public static DateTime GetCurrentTime()
    {
        return DateTime.Now;
    }

主要,要引入using System.Web.Services命名空间

这样,我们就可以在点击按钮后访问PageMethod得到一个当前时间啦

错误处理

  • 调用时,可以提供一个接收错误的回调函数
  • 包括超时和服务器端抛出的异常
  • 超时只能设置在WebService级别
  • 由Sys.Net.WebServiceError提供

      一个错误处理的示例

创建一个WebService添加如下代码

[WebMethod]
    public int GetDivision(int a, int b)//这里我们会使用它抛出一个经典的除0错误
    {
        return a / b;
    }

    [WebMethod]
    public int Timeout()//调用这个方法是,我们会首先设置它等待2秒,造成一个超时
    {
        System.Threading.Thread.Sleep(5000);
        return 0;
    }

然后创建一个页面,调用这个WebService

<asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/ErrorHandling.asmx" />
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="Get Division" onclick="getDivision(5,0)" />
        <input type="button" value="TimeOut" onclick="timeout()" />
        <script language="javascript" type="text/javascript">

            function getDivision(a, b) {
                ErrorHandling.GetDivision(a, b, null, failedCallback);
            }

            function timeout() {
                ErrorHandling.set_timeout(2000);
                ErrorHandling.Timeout(null, failedCallback);
            }

            function failedCallback(error) {
                var message = String.format("Timeout:{0}n Message:{1}n Exception Type:{2}n StackTrace:{3}", error.get_timedOut(), error.get_message(), error.get_exceptionType(), error.get_stackTrace());
                alert(message);
            }
        </script>

这时,我们点击按钮,就可以看到一些错误信息了,实际应用中,我们可以利用这些信息,在页面上相应的做一些提示

复杂数据类型使用基础

首先,定义一个Employee类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
///Employee 的摘要说明
/// </summary>
public class Employee
{
    private string _firstname;
    private string _lastname;
    private string _title;

    public Employee(string firstname, string lastname, string title)
    {
        this._firstname = firstname;
        this._lastname = lastname;
        this._title = title;
    }
    public Employee() { }
    public string FirstName
    {
        get { return this._firstname; }
        set { this._firstname = value; }
    }
    public string LastName
    {
        set { this._lastname = value; }
        get { return this._lastname; }
    }
    public string Title
    {
        get { return this._title; }
    }

    public int Salary { get; set; }

    public string FullName
    {
        get
        {
            return this.FirstName + this.LastName;
        }
    }
    public static implicit operator string(Employee employee)
    {
        return employee.FullName;
    }
}

然后创建一个WebService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

/// <summary>
///ComplexType 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class ComplexType : System.Web.Services.WebService
{

    [WebMethod]
    public Employee DoubleSalary(Employee employee)
    {
        employee.Salary *= 2;
        return employee;
    }

    [WebMethod]
    public List<int> Reverse(List<int> list)
    {
        list.Reverse();
        return list;
    }

    [WebMethod]
    public IDictionary<string, Employee> GetEmployee()
    {
        Dictionary<string, Employee> result = new Dictionary<string, Employee>();
        Employee emp1 = new Employee();
        emp1.FirstName = "bai";
        emp1.LastName = "yulong";
        emp1.Salary = 2000;
        result[emp1.FullName] = emp1;

        Employee emp2 = new Employee();
        emp2.FirstName = "xiao";
        emp2.LastName = "yaojian";
        emp2.Salary = 4000;
        result[emp2.FullName] = emp2;

        return result;
    }
    
}

然后创建一个页面,使用他们,首先在页面中添加ScriptManager,引入上面创建的WebService,添加如下代码

<input type="button" value="Double Salary" onclick="doubleSalary()" />
        <input type="button" value="Reverse" onclick="reerse([1,2,3,4,5])" />
        <input type="button" value="Get Employee" onclick="getEmploee()" />
        <script language="javascript" type="text/javascript">
            function doubleSalary() {
                var employee = new Object();
                employee.FirstName = "bai";
                employee.LastName = "yulong";
                employee.Salary = 1000;
                ComplexType.DoubleSalary(employee, doubleSalarySucceeded);
            }

            function doubleSalarySucceeded(result) {
                var message = String.format("FirstName:{0}nLastName:{1} nFullName:{2} nSalary:{3}", result.FirstName, result.LastName, result.FullName, result.Salary);
                alert(message);
            }

            function reerse(array) {
                ComplexType.Reverse(array, function(result) { alert(result); });
            }

            function getEmploee() {
                ComplexType.GetEmployee(getEmployeeSucceeded);
            }

            function getEmployeeSucceeded(result) {
                for (var name in result) {
                    alert(name + ":" + result[name].Salary);
                }
            }
        </script> 

这时,我们点击"Double Salary"按钮,就可以调用WebService上的DoubleSalary方法,使工资翻倍啦

如果我们这时用HTTP Watch看的话,就可以看见我们发送的是一个JSON字符串,返回的同样是一个JSON字符串,但是他在前面使用__type指定了一个Employee类型

其他的两个方法,演示的就是实现了IList和IDictionary接口的类型的使用方式,这里使用一些工具,就可以很明显的看到他们在发送和接受数据中的方式

客户端代理的使用细节

  • 函数调用的完整签名-Invoke(arg1,arg2,….,onSucceeded,onFailed,userContext)
  • 回调函数完整签名-onSucceeded(result,userContext,methodName),onFailed(error,userContext,methodName)
  • WebService级别默认属性:timtout,defaultUserContext,defaultSucceededCallBack,defaultFailedCallBack

生成复杂参数类型的客户端代理

  • 使用GenerateScriptTypeAttribute标记要生成的代理的参数类型
  • 可以标记在类,接口,以及方法上
  • 生成的代理中将包括客户端类型的代理
  • 调用方法时可以创建“具体类型”(使用提供的默认构造函数)

         一个示例,演示GenerateScriptTypeAttribute标记

          首先创建一个类Color

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
///Color 的摘要说明
/// </summary>
public class Color
{
    public byte Red;
    public byte Blue;
    public byte Green;


    public Color()
    {

    }

    public Color(byte red, byte green, byte blue)
    {
        this.Red = red;
        this.Blue = blue;
        this.Green = green;
    }
}

然后创建一个ColorService.asmx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;

/// <summary>
///ColorService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class ColorService : System.Web.Services.WebService
{

    [WebMethod]
    [GenerateScriptType(typeof(Color))]//这样就会在客户端生成一个复杂类型的代理
    public Color Reverse(Color color)
    {
        return new Color((byte)(255 - color.Red), (byte)(255 - color.Green), (byte)(255 - color.Blue));
    }
    
}

这样在页面中,使用这个WebService的时候,就会生成一个Color类型的代理,然后我们创建页面引入这个WebService

在页面中添加如下代码

<input type="button" value="Reserve Color" onclick="ReverseColor()" />
        
        <script type="text/javascript" language="javascript">
            function ReverseColor() {
                //var color = { "Red": 50, "Green": 100, "Blue": 200 };
                var color = new Color();
                color.Red = 50;
                color.Green = 100;
                color.Blue = 150;
                
                ColorService.Reverse(color, onSucceeded);
            }

            function onSucceeded(result) {
                alert(String.format("Red:{0}nGreen:{1}nBlue:{2}",result.Red,result.Green,result.Blue));
            }
        </script>

我们看到,这里我们就可以直接创建一个Color类型进行使用了

再写一个示例,演示客户端代理的作用

首先创建一个类文件Staff

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
///Staff 的摘要说明
/// </summary>
public abstract class Staff
{
    private int _Years;


    public int Years
    {
        get { return this._Years; }
        set { this._Years = value; }
    }

    public string RealStatus
    {
        get { return this.GetType().Name; }
    }

    public abstract int CaloulateSalary();
}

public class Intern : Staff
{
    public override int CaloulateSalary()
    {
        return 2000;
        //throw new NotImplementedException();
    }
}

public class Vendor : Staff
{
    public override int CaloulateSalary()
    {
        return 5000 + 1000 * (Years - 1);
        //throw new NotImplementedException();
    }
}

public class FulltimeEmployee : Staff
{
    public override int CaloulateSalary()
    {
        return 15000 + 2000 * (Years - 1);
        //throw new NotImplementedException();
    }
}

然后创建一个StaffService.asmx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;

/// <summary>
///StaffService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class StaffService : System.Web.Services.WebService 
{
    [WebMethod]
    [GenerateScriptType(typeof(Vendor))]
    [GenerateScriptType(typeof(Intern))]
    [GenerateScriptType(typeof(FulltimeEmployee))]
    public string CalculateSalary(Staff staff)
    {
        return "I'm " + staff.RealStatus + ",my  salary is" + staff.CaloulateSalary() + ".";
    }
}

然后创建页面

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager runat="server" ID="sm">
            <Services>
                <asp:ServiceReference Path="~/Demo03/StaffService.asmx" />
            </Services>
        </asp:ScriptManager>
        
        <div>Ysers:<input type="text" id="txtYears" /></div>
        <div>
            Status:
            <select id="comboStatus" style="width:150px;">
                <option value="Intern">Intern</option>
                <option value="Vendor">Vendor</option>
                <option value="FulltimeEmployee">FTE</option>
            </select>
        </div>
        <input type="button" value="Calculate!" onclick="calculateSalary()" /><br /><hr />
        <b>Result:</b>
        <div id="result"></div>
        
        <script language="javascript" type="text/javascript">
            function calculateSalary() {
                var emp = new Object();
                emp.__type = $get("comboStatus").value;
                
                emp.Years = parseInt($get("txtYears").value, 10);

                StaffService.CalculateSalary(emp, onSucceeded);
            }

            function onSucceeded(result) {
                $get("result").innerHTML = result;
            }
        </script>
    </form>
</body>
</html>

这样我们在输入一个工作年数,再选择一个员工类型后,点击"Calculate!"按钮, 就可以计算出他们的工资啦

这就是一个客户端代理做出多态效果的示例

使用JavaScriptConverter

  • 复杂类型作为返回值时可能会出现为题__循环引用
  • 解决方案___使用自定义的数据类型封装复杂类型,在web.config中定义converter

          一个使用JavaScriptConverter的示例

首先我们创建一个DataTableService.asmx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Data;

/// <summary>
///DataTableService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[ScriptService]
public class DataTableService : System.Web.Services.WebService 
{
    [WebMethod]
    public DataTable GetDataTable()
    {
        DataTable dt = new DataTable();
        dt.Columns.Add(new DataColumn("ID",typeof(int)));
        dt.Columns.Add(new DataColumn("Text", typeof(string)));

        Random random = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < 10; i++)
        {
            dt.Rows.Add(i, random.Next().ToString());
        }

        return dt;
    }
}

然后创建一个页面使用它

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo9/DataTableService.asmx" />
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="Get DataTable" onclick="getDataTable()" />
        
        <div id="result"></div>
        
        <script language="javascript" type="text/javascript">
            function getDataTable() {
                
                DataTableService.GetDataTable(onSucceeded, onFailed);
            }

            function onSucceeded(result) {
                //alert(result);
                var sb = new Sys.StringBuilder("<table border='1'>");
                sb.append("<tr><td>ID</td><td>TEXT</td></tr>");
                for (var i = 0; i < result.rows.length; i++) {
                    sb.append(String.format("<tr><td>{0}</td><td>{1}</td><tr>", result.rows[i].ID, result.rows[i].Text));

                }
                sb.append("</table>");
                $get("result").innerHTML = sb.toString();
            }
            
            function onFailed(error) {
                alert(error.get_message());
            }
        </script>
    </form>
</body>
</html>

这时,我们点击按钮时候,会弹出一个循环引用的错误提示,接下来我们就要解决它,首先在电脑中安装ASP.NET 2.0 AJAX Futures January CTP,然后找到里面的Microsoft.Web.Preview.dll,把它复制到当前项目的Bin目录下,然后在web.config的configuration中增加如下内容

<system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled="true" requireSSL="false"/>
        <profileService enabled="true"/>
        <jsonSerialization>
          <converters>
            <add name="DataSetConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataSetConverter,Microsoft.Web.Preview"/>
            <add name="DataRowConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataRowConverter,Microsoft.Web.Preview"/>
            <add name="DataTableConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter,Microsoft.Web.Preview"/>
          </converters>
        </jsonSerialization>
      </webServices>
    </scripting>
  </system.web.extensions>

这样,在使用DataSet等这些数据类型作为客户端复杂数据类型时,系统就会自动寻找这段jsonSerialization,对它进行转换

这时我们再刷新页面,点击按钮,就得到了我们预期的效果

定义一个JavaScriptConverter

  • 定义一个Converter继承JavaScriptConverter类
  • 实现SupportedTypes
  • 实现Serialize方法用于序列化复杂数据
  • 实现Deserizlize方法用于反序列化复杂数据
  • 在web.config中注册该Converter

       一个自定义的JavaScriptConverter示例

首先定义一个类文件BoyAndGirl.cs

using System;


public class Boy
{
    public string Name;
    public Girl GirlFriend;
}


public class Girl
{
    public string Name;
    public Boy BoyFriend;
}

然后创建WebService名为BoyGirlService.asmx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Diagnostics;

/// <summary>
///BoyGirlService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[ScriptService]
public class BoyGirlService : System.Web.Services.WebService
{
    [WebMethod]
    public Boy GetBoyWithGirlFriend()
    {
        Boy boy = new Boy();
        boy.Name = "xiaoyaojian";

        Girl girl = new Girl();
        girl.Name = "have not";

        boy.GirlFriend = girl;
        girl.BoyFriend = boy;
        return boy;
    }

    [WebMethod]
    public string SetBoyWithGirlFriend(Boy boy)
    {
        Debug.Assert(boy == boy.GirlFriend.BoyFriend);

        return String.Format("It's {0},his girlfriend is {1}", boy.Name, boy.GirlFriend.Name);
    }
}

然后在页面中使用

<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/BoyGirlService.asmx" />
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="Get Boy" onclick="getBoy()" />
        <input type="button" value="Set Boy" onclick="setBoy()" />
        
        <script language="javascript" type="text/javascript">
            function getBoy() {
                BoyGirlService.GetBoyWithGirlFriend(onGetBoySucceeded, onFailed);
            }

            function setBoy() {
                var boy = new Object();
                boy.Name = "xiaoyaojian";
                var girl = new Object();
                girl.Name = "have not";
                boy.GirlFriend = girl;

                BoyGirlService.SetBoyWithGirlFriend(boy, onSetBoySucceeded, onFailed);
            }

            function onGetBoySucceeded(result) {
                alert(String.format("It's {0},his girlfriend is {1}",result.Name,result.GirlFriend.Name));
            }

            function onFailed(error) {
                alert(error.get_message());
            }

            function onSetBoySucceeded(result) {
                alert(result);
            }
        </script>
    </form>
</body>
</html>

这时,因为类中有一个循环引用,所以程序会报错,接下来,我们创建一个类BoyConverter继承JavaScriptConverter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;

/// <summary>
///BoyConverter 的摘要说明
/// </summary>
public class BoyConverter:JavaScriptConverter
{
    public BoyConverter()
    {
        //
        //TODO: 在此处添加构造函数逻辑
        //
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        //throw new NotImplementedException();
        Boy boy = new Boy();
        
        boy.Name = (string)dictionary["Name"];
        boy.GirlFriend = serializer.ConvertToType<Girl>(dictionary["GirlFriend"]);
        boy.GirlFriend.BoyFriend = boy;

        return boy;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        //throw new NotImplementedException();
        Boy boy = (Boy)obj;
        IDictionary<string, object> result = new Dictionary<string, object>();

        result["Name"] = boy.Name;

        boy.GirlFriend.BoyFriend = null;
        result["GirlFriend"] = boy.GirlFriend;

        return result;
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            yield return typeof(Boy);
        }
    }
}

然后在web.config中注册这个Converter

<add name="BoyConverter" type="BoyConverter,App_Code"/>

这样我们打破了原本的循环引用,示例就可以正常通过啦

改变客户端访问时的方法名

  • 客户端无法重载方法(可以通过判断arguments数量来模拟)
  • 如果服务器端出现了方法重载?

                使用WebServiceAttribute指定客户端方法名

                使用和真正的WebService相同的做法

                [WebMethod(MessageName="…")]

  • 并非出现重载才能改变方法名称

        一个改变客户端访问时的方法名的示例

首先创建一个名为MethodOverloadService.asmx的WebService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

/// <summary>
///MethodOverloadService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class MethodOverloadService : System.Web.Services.WebService 
{

    [WebMethod]
    public int GetRandom()
    {
        return new Random(DateTime.Now.Millisecond).Next();
    }

    [WebMethod]
    public int GetRandom(int minValue, int maxValue)
    {
        return new Random(DateTime.Now.Millisecond).Next(minValue, maxValue);
    }
}

     然后我们创建一个页面,引用这个WebService,并设置ScriptManager的InlineScript="true",这样生成的代理就直接写到页面上了,我们可以看到,页面中只注册下面的一个GetRandom方法,因为第一个方法已经被覆盖

      如果我们要避免这种客户端对同名方法的覆盖,我们就要改变客户端访问这个方法时的名字,只需要在任意一个这样的方法下面加上如下代码就可以实现了

[WebMethod(MessageName = "GetRangeRandom")]

这时我们就可以在页面中找到它注册了两个方法 ,方法名分别是GetRandom和GetRangeRandom,好了,成功啦

使用HTTP GET访问WebService方法

  • 使用ScriptMethodAttribute进行标记(UseHttpGet属性设置为true),出于安全性考虑,默认只使用POST
  • 客户端使用代理的方法没有任何变化
  • 参数将使用Query String进行传递
  • 性能较HTTP POST方法略有提高
  • 一些特性略有改变(缓存的基础等,HTTP GET是没有缓存的)

一个使用HTTP GET访问WebService方法的示例

首先创建一个名为UserHttpGetService的WebService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;

/// <summary>
///UserHttpGetService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class UseHttpGetService : System.Web.Services.WebService
{
    [WebMethod]
    public int GetRandom()
    {
        return new Random(DateTime.Now.Millisecond).Next();
    }

    [WebMethod]
    [ScriptMethod(UseHttpGet = true)]
    public int GetRangeRandom(int minValue, int maxValue)
    {
        return new Random(DateTime.Now.Millisecond).Next(minValue, maxValue);
    }
}

然后创建页面,代码如下

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/UseHttpGetService.asmx" InlineScript="true" />
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="Get Random" onclick="getRandom()" />
        <input type="button" value="Get Range Random" onclick="getRandom(50,100)" />
        
        <script language="javascript" type="text/javascript">
            function getRandom(minValue, maxValue) {
                if (arguments.length != 2) {
                    UseHttpGetService.GetRandom(onSucceeded);
                }
                else {
                    UseHttpGetService.GetRangeRandom(minValue, maxValue, onSucceeded);
                }
            }

            function onSucceeded(result) {
                alert(result);
            }
        </script>
    </form>
</body>
</html>

这样我们就完成了这个示例,我们会发现页面代码和以前访问WebService是一摸一样的,但是我们使用HttpWatch查看就可以发现,他们的访问方式是不一样的,而且我们打开页面源代码也可以发现以下代码

this._invoke(this._get_path(), 'GetRandom',false,{},succeededCallback,failedCallback,userContext); 
this._invoke(this._get_path(), 'GetRangeRandom',true,{minValue:minValue,maxValue:maxValue},succeededCallback,failedCallback,userContext); }}

这里的false和true就表示是不是使用HTTP GET

让WebService方法返回XML对象

  • 默认以JSON格式返回数据
  • 使用ScriptMethodAttribute进行标记(ResponseFormat属性设置为Xml,Response的Context-Type将为text/xml)
  • 可以使用字符串拼接出XML并输出
  • 可以返回Xml相关类型(XmlDocument,XmlElement)
  • 返回普通对象时将使用XmlSerializer输出
一个让方法返回XML对象的示例

首先创建一个Employee类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
///Employee 的摘要说明
/// </summary>
public class Employee
{
    private string _firstname;
    private string _lastname;
    private string _title;
    
    public Employee(string firstname,string lastname,string title)
    {
        this._firstname = firstname;
        this._lastname = lastname;
        this._title = title;
    }
    public Employee() { }
    public string FirstName
    {   
        get { return this._firstname; }
        set { this._firstname = value; }
    }
    public string LastName
    {
        set { this._lastname = value; }
        get { return this._lastname; }
    }
    public string Title
    {
        get { return this._title; }
    }

    public int Salary { get; set; }

    public string FullName
    {
        get
        {
            return this.FirstName + this.LastName;
        }
    }
    public static implicit operator string(Employee employee)
    {
        return employee.FullName;
    }
}

然后创建一个ReturnXmlService.asmx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Xml;

/// <summary>
///ReturnXmlService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class ReturnXmlService : System.Web.Services.WebService
{
    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
    public XmlNode GetXmlDocument()
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml("<Employee><Name>Xiaoyaojian</Name><Salary>5000</Salary></Employee>");

        return doc;
    }

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
    public XmlNode GetXmlElement()
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml("<Employee><Name>Xiaoyaojian</Name><Salary>5000</Salary></Employee>");

        return doc.DocumentElement;
    }

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
    public Employee GetEmployee()
    {
        return new Employee("bai", "yulong","developer");
    }

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
    public string GetXmlString()
    {
        return "<Employee><Name>Xiaoyaojian</Name><Salary>5000</Salary></Employee>";
    }

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml, XmlSerializeString = true)]
    public string GetSerializedString()
    {
        return "<Employee><Name>Xiaoyaojian</Name><Salary>5000</Salary></Employee>";
    }
}

然后创建页面,使用这个WebService

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/ReturnXmlService.asmx" InlineScript="true"/>
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="GetXmlDocument" onclick="ReturnXmlService.GetXmlDocument(onSucceeded);" /><br /><br />
        <input type="button" value="GetXmlElement" onclick="ReturnXmlService.GetXmlElement(onSucceeded);" /><br /><br />
        <input type="button" value="GetEmployee" onclick="ReturnXmlService.GetEmployee(onSucceeded);" /><br /><br />
        <input type="button" value="GetXmlString" onclick="ReturnXmlService.GetXmlString(onSucceeded);" /><br /><br />
        <input type="button" value="GetSerializedString" onclick="ReturnXmlService.GetSerializedString(onSucceeded);" />
        
        <script language="javascript" type="text/javascript">
            function onSucceeded(result)
            {
                alert(result.xml);
            }
        </script>
    </form>
</body>
</html>

我们比较弹出的效果,就可以看出不同的标记和不同的返回类型,客户端对次不同的处理啦

在WebService方法中使用Session

  • ASP.NET中每个请求都由一个IHttpHandler对象来处理
  • 在处理时要使用Session则需要让Handler对象实现IRequiresSessionState借口
  • RestHandlerFactory根据所请求的方法的标记来选择是否启用Session
  • 启用方法:在WebMethodAttribute中标记(EnableSession属性设置为true)
一个在WebService方法中使用Session的示例

首先创建一个名为EnableSessionService.asmx的WebService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;

/// <summary>
///EnableSessionService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class EnableSessionService : System.Web.Services.WebService
{
    [WebMethod(EnableSession = true)]
    public int AddOne()
    {
        object objValue = Session["value"];
        int value = objValue == null ? 0 : (int)objValue;
        value++;
        Session["value"] = value;
        return value;
    }

    [WebMethod(true)]
    public int AddTwo()
    {
        object objValue = Session["value"];
        int value = objValue == null ? 0 : (int)objValue;
        value += 2;
        Session["value"] = value;
        return value;
    }
}

然后创建一个页面使用它

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/EnableSessionService.asmx" InlineScript="true" />
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="Add One" onclick="addOne()" />
        <input type="button" value="Add Two" onclick="addTwo()" />
        
        <script language="javascript" type="text/javascript">
            function addOne() {
                EnableSessionService.AddOne(onSucceeded);
            }

            function addTwo() {
                EnableSessionService.AddTwo(onSucceeded);
            }
            
            function onSucceeded(result) {
                alert(result);
            }
        </script>
    </form>
</body>
</html>

这样我们就可以正确的使用WebService访问Session啦,并且我们发现[WebMethod(EnableSession = true)]和[WebMethod(true)]的作用是一样的,区别就是,当我们需要设置一写其他属性的时候,我们就只能使用[WebMethod(EnableSession = true)]这种方式啦

安全性

  • 完全适用ASP.NET的认证机制(使用FormsAuthentication,Impersonation,PrincipalPermission)
  • ASP.NET AJAX访问WebService可以操作cookies
一个关于安全性的示例

首先,我们应该确定一下,web.config中的authentication标记的mode属性是否非Forms

创建一个名为SecurityService.asmx的WebService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

/// <summary>
///SecurityService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。 
[System.Web.Script.Services.ScriptService]
public class SecurityService : System.Web.Services.WebService
{
    [WebMethod]
    public string HelloWorld()
    {
        if (!HttpContext.Current.User.Identity.IsAuthenticated)
        {
            throw new ApplicationException("Please Login First");
        }
        return "Hello " + HttpContext.Current.User.Identity.Name;
    }
}

然后创建一个页面使用它

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Services>
                <asp:ServiceReference Path="~/Demo03/SecurityService.asmx" InlineScript="true" />
            </Services>
        </asp:ScriptManager>
        
        <input type="button" value="Call" onclick="call()" />
        
        <script language="javascript" type="text/javascript">
            function call() {
                SecurityService.HelloWorld(onSucceeded,onFailed);
            }

            function onSucceeded(result) {
                alert(result);
            }

            function onFailed(error) {
                alert(error.get_message());
            }
        </script>
    </form>
</body>
</html>

这时,我们点击页面中的Call按钮,就会弹出一个Please login first,我们成功了阻止了一次匿名的登陆

我们在页面的Load事件中增加如下代码

FormsAuthentication.SetAuthCookie("Xiaoyaojian",false); 

这样,我们在页面加载的时候就为它登陆了,页面就会正常显示我们想要的内容:Hello ,Xiaoyaojian(注意要在页面代码中引入System.Web.Security命名空间)

客户端代理的一些解释

  • 每个Service类对应客户端的一个代理类(还记得Type.registerNamespace这些东西吗?)
  • 每个代理类为Sys.Net.WebServiceProxy类的子类
  • 代理类的每个方法均对应一个类的静态方法(我们使用调用WebService的时候,可没有使用一个net来创建对象)
  • 最终由Sys.Net.WebServiceProxy.invoke方法发出请求

invoke方法签名

Sys.Net.WebServiceProxy.invoke=function(

                           servicePath//Service路径

                            methodName//方法名

                            useGet//是否使用HTTP GET访问

                            params//参数

                            onSucceeded//成功后的回调函数

                            onFailded//失败后的回调函数

                            userContext//用户上下文对象

                            timtOut//超时时间

                       ){};

一个示例

创建一个页面,但是这回我们不同的是,不向ScriptManager中添加一个WebService的引用

我们使用如下代码

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        
        <input type="button" value="Get Range Random" onclick="getRandom(0,100)" />
        
        <script language="javascript" type="text/javascript">
            function getRandom(minValue, maxValue) {
                Sys.Net.WebServiceProxy.invoke(
                    "UseHttpGetService.asmx",
                    "GetRangeRandom",
                    true,
                    {"minValue":minValue,"maxValue":maxValue},
                    onSucceeded,
                    null,
                    null,
                    -1
                );
                }

                function onSucceeded(result) {
                    alert(result);
                }
        </script>
    </form>
</body>
</html>

这里同样我们访问到了UseHttpGetService这个WebService,这就是我们使用Invoke的一个方法