asp.net ajax学习笔记:客户端访问WebService

asp.net ajax学习笔记:客户端访问WebService
2008年07月02日 星期三 下午 06:09
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 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 = “…")]这样一个标记
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);
      }
      对应上面的invoke参数说明和例子,对WebService生成的代理方法是如何调用WebService有一定了解
****************************************************************************************************************************
轉自: http://hi.baidu.com/freezesoul/blog/item/38bfb1115ef56c7bcb80c45e.html  
謝謝作者。

你可能感兴趣的:(asp.net ajax学习笔记:客户端访问WebService)