1、客户端访问WebService,那么WebService类必须加上[ScriptService]标记,此标记在 System.Web.Script.Services命名空间下,并且被访问的方法必须是public和加上[WebMethod]标记。客户端在 ScriptManager标签之间加上如下代码
<Services>
<asp:ServiceReference Path="命名空间"WebService.asmx" InlineScript="true" />
</Services>
InlineScript属性表示是否将代理缓存到页面中(HTML源码中)。
2、客户端访问PageMethod,页面的方法必须写在aspx页面文件对应的aspx.cs文件上。方法必须是public和 static并且用[WebMethod]标记。客户端必须使用PageMethods.MethodName()访问,并且与访问WebService一样有成功后的回调函数。注:ScriptManager的EnablePageMethods属性必须设置为true,才能访问页面的方法。
3、WebMethod(arg1, …, argN, onSucceeded, onFailed,userContext)
4、传递负责类型,复杂类型默认以JSON方式传递
存在派生类时:
namespace ComplexType
{
public abstract class Employee
{
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 CalculateSalary();
}
public class Intern : Employee
{
public override int CalculateSalary()
{
return 2000;
}
}
public class Vendor : Employee
{
public override int CalculateSalary()
{
return 5000 + 1000 * (Years - 1);
}
}
public class FulltimeEmployee : Employee
{
public override int CalculateSalary()
{
return 15000 + 2000 * (Years - 1);
}
WebService要针对不再方法中传递的对象设置属性:
[WebMethod]
[GenerateScriptType(typeof(Intern))]
[GenerateScriptType(typeof(Vendor))]
[GenerateScriptType(typeof(FulltimeEmployee))]
public string CalculateSalary(Employee employee)
{
return "I'm " + employee.RealStatus +
", my salary is " + employee.CalculateSalary() + ".";
}
客户端使用技巧:
<select id="comboStatus" style="width:150px;">
<option value="ComplexType.Intern">Intern</option>
<option value="ComplexType.Vendor">Vendor</option>
<option value="ComplexType.FulltimeEmployee">FTE</option>
</select>
====================
function calculateSalary()
{
// var emp = null;
// var combo = $get("comboStatus");
//
// switch(combo.options[combo.selectedIndex].text)
// {
// case "Intern":
// emp = new ComplexType.Intern();
// break;
// case "Vendor":
// emp = new ComplexType.Vendor();
// break;
// case "FTE":
// emp = new ComplexType.FulltimeEmployee();
// break;
// }
var emp = new Object();
emp.__type = $get("comboStatus").value;
emp.Years = parseInt($get("txtYears").value, 10);
EmployeeService.CalculateSalary(emp, onSucceeded);
}
5、用JavaScriptConverter解决复杂对象由于循环引用的问题
使用DataTable这样的复杂类型时,可以使用JavaScriptConverter来解决
引用Microsoft.Web.Preview.dll程序集,web.config做如下设置
<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" />
<add name="BoyConverter" type="Converter.BoyConverter, App_Code" />
</converters>
</jsonSerialization>
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;
}
客户端可通过对象方式引用:
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());
}
自己编写JavaScriptConverter
namespace Converter
{
public class BoyConverter : JavaScriptConverter
{
//此方法在由客户端的对象向服务段对象进行传递时调用
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
//实例化一个Boy对象
Boy boy = new Boy();
//将Boy的Name被设置为dictionary的Name的key的值
boy.Name = (string)dictionary["Name"];
//注意dictionary["GirlFriend"]的值是一个JSon对象,用来保存客户端GirlFriend的对象
//所以需要用JavaScriptSerializer来将此JSon对象转换为一个服务段Gril对象
boy.GirlFriend = serializer.ConvertToType<Girl>(dictionary["GirlFriend"]);
//设置boy的GirlFriend的BoyFirend,在服务端不用担心循环引用的问题
boy.GirlFriend.BoyFriend = boy;
//返回被设置好的Boy对象
return boy;
}
//此方法在由服务端对象向客户端对象进行传递是调用
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
//转换传递进来的参数为Boy类型
Boy boy = (Boy)obj;
//实例化一个字典类型
IDictionary<string, object> result = new Dictionary<string, object>();
//设置字典类型的key为Name,值为Boy的Name
result["Name"] = boy.Name;
//将boy的GirFriend的BoyFriend引用设置为空,避免在客户端出现循环引用的问题
boy.GirlFriend.BoyFriend = null;
//设置字典类型的key为GirlFriend为Boy的GirlFriend
result["GirlFriend"] = boy.GirlFriend;
//返回IDictionary对象,到客户端被转换为JSon对象,并且,不能使用boy.GirlFriend.BoyFriend
//类似的代码
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
//在 foreach 每次循环调用迭代程序的
//MoveNext 方法时,它都从前一次 yield return 语句停止的地方开始执行
yield return typeof(Boy);
}
}
}
在实现好JavaScriptConverter类之后,需要在WebConfig里加上指定的Converter标签<add name="BoyConverter" type="Converter.BoyConverter, App_Code"/>加载的地方与DataTable类似。Name:转换者的名字,随便起 Type:Converter.BoyConverter转换者的命名空间加类名,App_Code:转换者类文件存放的地址,好像无所谓,我随便填也没出错
6、序列化和反序列化
* 客户端的反序列化:
Sys.Serialization.JavaScriptSerializer.deserialize('<%= this.SerializedDateTime %>');
* 服务端的序列化
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.Serialize(DateTime.Now);
注意:客户端是静态方法。DateTime序列化后不是一个JSon字符串。
* 上面是简单类型的序列化和反序列化,如果是复杂的对象类型需要用到JavaScriptTypeResolver
自定义一个Resolver类并继承JavaScriptTypeResolver类,重写ResolveType、ResolveTypeId两个方法
JavaScriptTypeResolver不是一个直接使用的类,他是用来辅助JavaScriptSerializer类的,作为构造函数的参数传入。
namespace TypeResolver
{
/**//// <summary>
/// Summary description for CustomizeTypeResolver
/// </summary>
public class CustomizeTypeResolver : JavaScriptTypeResolver
{
//从字符串标识获取一个Type对象
public override Type ResolveType(string id)
{
//id就是从客户端对象的__type的值
switch (id)
{
case "0": return typeof(Intern);
case "1": return typeof(Vendor);
case "2": return typeof(FulltimeEmployee);
}
return null;
}
//得到Type对象的标识字符串
public override string ResolveTypeId(Type type)
{
if (type == typeof(Intern))
{
//返回客户端就是__type属性的值
return "0";
}
else if (type == typeof(Vendor))
{
return "1";
}
else if (type == typeof(FulltimeEmployee))
{
return "2";
}
return null;
}
}
}
这是类的定义
Employee emp = null;
switch (id)
{
case 0: emp = new Intern(); break;
case 1: emp = new Vendor(); break;
default: emp = new FulltimeEmployee(); break;
}
//用Resolver对象来实例一个JavaScriptSerializer对西那个
JavaScriptSerializer serializer = new JavaScriptSerializer(new CustomizeTypeResolver());
//序列化复杂对象
return serializer.Serialize(emp);
这是使用方法
7、改变客户端访问WebService代理方法名
使用Get方式访问WebService的方法,必须加上[ScriptMethod(UseHttpGet=true)]标记, 参数将使用QueryString进行传递,性能较HTTPPOST方法略有提高
9、让方法返回XML对象
Invoke方法签名
Sys.Net.WebServiceProxy.invoke= function (
servicePath,/*Service路径*/
methodName,/*方法名*/
useGet,/*是否使用HTTPGET方法*/
params,/*方法参数*/
onSucceeded,/*成功后的回调函数*/
onFailure,/*失败后的回调函数*/
userContext,/*用户上下文对象*/
timeout /* 超时时间*/){ }
function getRandom(minValue, maxValue)
{
Sys.Net.WebServiceProxy.invoke(
"Services/UseHttpGetService.asmx",
"GetRangeRandom",
true,
{ "minValue" : minValue,
"maxValue" : maxValue},
onSucceeded,
null,
null,
-1);
}
<Services>
<asp:ServiceReference Path="命名空间"WebService.asmx" InlineScript="true" />
</Services>
InlineScript属性表示是否将代理缓存到页面中(HTML源码中)。
2、客户端访问PageMethod,页面的方法必须写在aspx页面文件对应的aspx.cs文件上。方法必须是public和 static并且用[WebMethod]标记。客户端必须使用PageMethods.MethodName()访问,并且与访问WebService一样有成功后的回调函数。注:ScriptManager的EnablePageMethods属性必须设置为true,才能访问页面的方法。
3、WebMethod(arg1, …, argN, onSucceeded, onFailed,userContext)
4、传递负责类型,复杂类型默认以JSON方式传递
- 在客户端和服务段传递对象时自动进行(JSon)序列化与反序列化。
- 生成复杂对象代理时,需要在服务端使用GenerateScriptType属性标记要代理的类[GenerateScriptType(typeof(Color))]注:此标记可以标记在类、接口以及方法上
- 如 果服务器端类型有基类、派生类等继承关系,当然可以根据具体的情况生成具体的派生类对象,还可以直接实例化一个Object对象,设置这个对象 __type属性,在把这个对象传递到服务端时,服务端可根据此标记自动反序列化为一个派生类,这样可以实现一个多态的效果,这个Object对象就是基 类的对象,根据__type属性自动实例化一个派生类(__type指定了传递负责对象对应的类型名称)
存在派生类时:
namespace ComplexType
{
public abstract class Employee
{
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 CalculateSalary();
}
public class Intern : Employee
{
public override int CalculateSalary()
{
return 2000;
}
}
public class Vendor : Employee
{
public override int CalculateSalary()
{
return 5000 + 1000 * (Years - 1);
}
}
public class FulltimeEmployee : Employee
{
public override int CalculateSalary()
{
return 15000 + 2000 * (Years - 1);
}
WebService要针对不再方法中传递的对象设置属性:
[WebMethod]
[GenerateScriptType(typeof(Intern))]
[GenerateScriptType(typeof(Vendor))]
[GenerateScriptType(typeof(FulltimeEmployee))]
public string CalculateSalary(Employee employee)
{
return "I'm " + employee.RealStatus +
", my salary is " + employee.CalculateSalary() + ".";
}
客户端使用技巧:
<select id="comboStatus" style="width:150px;">
<option value="ComplexType.Intern">Intern</option>
<option value="ComplexType.Vendor">Vendor</option>
<option value="ComplexType.FulltimeEmployee">FTE</option>
</select>
====================
function calculateSalary()
{
// var emp = null;
// var combo = $get("comboStatus");
//
// switch(combo.options[combo.selectedIndex].text)
// {
// case "Intern":
// emp = new ComplexType.Intern();
// break;
// case "Vendor":
// emp = new ComplexType.Vendor();
// break;
// case "FTE":
// emp = new ComplexType.FulltimeEmployee();
// break;
// }
var emp = new Object();
emp.__type = $get("comboStatus").value;
emp.Years = parseInt($get("txtYears").value, 10);
EmployeeService.CalculateSalary(emp, onSucceeded);
}
5、用JavaScriptConverter解决复杂对象由于循环引用的问题
使用DataTable这样的复杂类型时,可以使用JavaScriptConverter来解决
引用Microsoft.Web.Preview.dll程序集,web.config做如下设置
<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" />
<add name="BoyConverter" type="Converter.BoyConverter, App_Code" />
</converters>
</jsonSerialization>
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;
}
客户端可通过对象方式引用:
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());
}
自己编写JavaScriptConverter
namespace Converter
{
public class BoyConverter : JavaScriptConverter
{
//此方法在由客户端的对象向服务段对象进行传递时调用
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
//实例化一个Boy对象
Boy boy = new Boy();
//将Boy的Name被设置为dictionary的Name的key的值
boy.Name = (string)dictionary["Name"];
//注意dictionary["GirlFriend"]的值是一个JSon对象,用来保存客户端GirlFriend的对象
//所以需要用JavaScriptSerializer来将此JSon对象转换为一个服务段Gril对象
boy.GirlFriend = serializer.ConvertToType<Girl>(dictionary["GirlFriend"]);
//设置boy的GirlFriend的BoyFirend,在服务端不用担心循环引用的问题
boy.GirlFriend.BoyFriend = boy;
//返回被设置好的Boy对象
return boy;
}
//此方法在由服务端对象向客户端对象进行传递是调用
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
//转换传递进来的参数为Boy类型
Boy boy = (Boy)obj;
//实例化一个字典类型
IDictionary<string, object> result = new Dictionary<string, object>();
//设置字典类型的key为Name,值为Boy的Name
result["Name"] = boy.Name;
//将boy的GirFriend的BoyFriend引用设置为空,避免在客户端出现循环引用的问题
boy.GirlFriend.BoyFriend = null;
//设置字典类型的key为GirlFriend为Boy的GirlFriend
result["GirlFriend"] = boy.GirlFriend;
//返回IDictionary对象,到客户端被转换为JSon对象,并且,不能使用boy.GirlFriend.BoyFriend
//类似的代码
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
//在 foreach 每次循环调用迭代程序的
//MoveNext 方法时,它都从前一次 yield return 语句停止的地方开始执行
yield return typeof(Boy);
}
}
}
在实现好JavaScriptConverter类之后,需要在WebConfig里加上指定的Converter标签<add name="BoyConverter" type="Converter.BoyConverter, App_Code"/>加载的地方与DataTable类似。Name:转换者的名字,随便起 Type:Converter.BoyConverter转换者的命名空间加类名,App_Code:转换者类文件存放的地址,好像无所谓,我随便填也没出错
6、序列化和反序列化
* 客户端的反序列化:
Sys.Serialization.JavaScriptSerializer.deserialize('<%= this.SerializedDateTime %>');
* 服务端的序列化
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.Serialize(DateTime.Now);
注意:客户端是静态方法。DateTime序列化后不是一个JSon字符串。
* 上面是简单类型的序列化和反序列化,如果是复杂的对象类型需要用到JavaScriptTypeResolver
自定义一个Resolver类并继承JavaScriptTypeResolver类,重写ResolveType、ResolveTypeId两个方法
JavaScriptTypeResolver不是一个直接使用的类,他是用来辅助JavaScriptSerializer类的,作为构造函数的参数传入。
namespace TypeResolver
{
/**//// <summary>
/// Summary description for CustomizeTypeResolver
/// </summary>
public class CustomizeTypeResolver : JavaScriptTypeResolver
{
//从字符串标识获取一个Type对象
public override Type ResolveType(string id)
{
//id就是从客户端对象的__type的值
switch (id)
{
case "0": return typeof(Intern);
case "1": return typeof(Vendor);
case "2": return typeof(FulltimeEmployee);
}
return null;
}
//得到Type对象的标识字符串
public override string ResolveTypeId(Type type)
{
if (type == typeof(Intern))
{
//返回客户端就是__type属性的值
return "0";
}
else if (type == typeof(Vendor))
{
return "1";
}
else if (type == typeof(FulltimeEmployee))
{
return "2";
}
return null;
}
}
}
这是类的定义
Employee emp = null;
switch (id)
{
case 0: emp = new Intern(); break;
case 1: emp = new Vendor(); break;
default: emp = new FulltimeEmployee(); break;
}
//用Resolver对象来实例一个JavaScriptSerializer对西那个
JavaScriptSerializer serializer = new JavaScriptSerializer(new CustomizeTypeResolver());
//序列化复杂对象
return serializer.Serialize(emp);
这是使用方法
7、改变客户端访问WebService代理方法名
- 客户端无法重载函数,只能根据arguments的参数来判断。并且不能根据参数的类型来判断
- 如果WebService端有函数的重载,这时候映射到客户端是无法区别的。那么我们需要把函数的重载在客户端映射成非重载函数。在WebService方法上添加一个[WebMethod(MessageName = “…")]这样一个标记
使用Get方式访问WebService的方法,必须加上[ScriptMethod(UseHttpGet=true)]标记, 参数将使用QueryString进行传递,性能较HTTPPOST方法略有提高
9、让方法返回XML对象
- 客户端调用WebService方法默认使用JSon字符串返回数据。
- 要返回XML对象必须给ScriptMethod标记加上ResponseForma=ResponseFormat.Xml参数
- Response的Content-Type将被设置为text/xml
- 返回普通对象时将使用XmlSerializer输出,如:return new Employee("FreezeSoul", 1000);
- 返回字符串时可以直接作为XML字符串输出,就是说就算给出XML结构类似的字符串,经过XmlSerializeString处理之后,会将<和>转义,并且根元素为string。指定[ScriptMethod(ResponseFormat = ResponseFormat.Xml, XmlSerializeString = true)]
10、在WebService方法中使用Session
在WebMethod标签中加入EnableSession=true参数11、在客户端调用WebService的安全性
- 完全适应Asp.Net的认证机制
- 可以使用FormsAuthentication,让WebService方法可以操作Cookie
- Impersonation
- PrincipalPermission
Invoke方法签名
Sys.Net.WebServiceProxy.invoke= function (
servicePath,/*Service路径*/
methodName,/*方法名*/
useGet,/*是否使用HTTPGET方法*/
params,/*方法参数*/
onSucceeded,/*成功后的回调函数*/
onFailure,/*失败后的回调函数*/
userContext,/*用户上下文对象*/
timeout /* 超时时间*/){ }
function getRandom(minValue, maxValue)
{
Sys.Net.WebServiceProxy.invoke(
"Services/UseHttpGetService.asmx",
"GetRangeRandom",
true,
{ "minValue" : minValue,
"maxValue" : maxValue},
onSucceeded,
null,
null,
-1);
}