ASP.NET AJAX(4)__客戶端訪問WebService伺服器端釋放WebService方法客戶端訪問WebService客戶端訪問PageMethod錯誤處理複雜資料型別使用基礎客戶端代理的
伺服器端釋放WebService方法
- 編寫一個普通的WebService
- 為WebService類新增自定義的屬性標記__ScriptServiceAttribute
- 釋放WebService方法
__訪問級別為Public
__使用WebServiceAttribute進行標記
- 為頁面中的ScriptManager引入asmx檔案
客戶端訪問WebService
- [Namespaces.]ClassName.MethodName
- 依次傳入引數
- 傳入一個方法作為成功後的回撥函式(即使沒有返回值)
一個簡單的訪問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的一個方法