1. 程式人生 > 其它 >ASP.NET AJAX(4)__客戶端訪問WebService伺服器端釋放WebService方法客戶端訪問WebService客戶端訪問PageMethod錯誤處理複雜資料型別使用基礎客戶端代理的

ASP.NET AJAX(4)__客戶端訪問WebService伺服器端釋放WebService方法客戶端訪問WebService客戶端訪問PageMethod錯誤處理複雜資料型別使用基礎客戶端代理的

伺服器端釋放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的一個方法