asp.net客户端访问WebService[转帖]

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方式传递

  • 在客户端和服务段传递对象时自动进行(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 >

<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 >");

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 = “…")]这样一个标记
8、使用Http的Get方式访问WebService的方法

使用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
12、不使用WebService代理的对应方法,使用客户端代理直接调用WebService方法。

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);

}