服务器端释放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="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的一个方法