ASP.NET Ajax学习笔记(3.webservice调用)

  三.调用webService

       ScriptManagerASP.NET Ajax技术中最关键的组件,每一个页面都必须包含一个ScriptManager。他包含的属性有:

1.       AllowCustomErrorsRedirect—决定updatapanel局部刷新时出现错误是否跳转到自定义错误界面。

2.       AsyncPostBackErrorMessage—设定当updatapanel刷新错误时显示的字符串。

3.       AysncPostBackTimeout—设定updatapanel异步刷新的超时值。

4.       AuthenticationService—设定客户端验证服务的设置。

5.       EnablePageMethods—设置使页面可以使用基于页面的web服务。

6.       EnablePartialRendering—通知Ajax 客户端运行库给updatapanel提供支持。True设置提供支持,false不提供支持。

7.       EnableScriptGlobalization—设定script是否为全球化。

8.       EnableScriptLocalization—设定script是否为本地化。

9.       LoadScriptBeforeUI—设定是否在浏览器显示界面前载入脚本。

10.   ProfileService—提供客户端个性化服务设置。

11.   ScriptModel—设定载入的脚本类型(测试版或发行版)。

12.   ScriptPath—设定非web resource程序集脚本的下载路径。

13.   Script—页面中需要的脚本文件名集合。

14.   Service—页面中需要调用的web服务集合。

关于HTTP 请求:调用web服务的Http请求分为两种方式:PostGet。在Post方式是调用web服务的默认方式,在这种方式下,一个整体的数据将会从浏览器传输到服务器,这些数据的大小是不受限制的,所以这种方式比较合适大量数据传输的情况中。浏览器把数据格式化为JSON格式并传输到服务器,服务器会解析JSON数据并格式化为.NET 数据,然后启动调用web服务。在Get方式下,只有初始化方式与post不同,其他方式都是一样的。浏览器会将一定的参数作为查询字符串传输到服务器,数据传输的大小是有限制的,并会存在一个安全上的问题。要改成get模式要使用ScriptMethod属性:

[ScriptMethod(UseHttpGet=true)]

Javascript的简化形式:

    Javascript在ASP.NET Ajax里面有了一些简化方式:

    1.$get(ControlName)-$get是Sys.UI.DomElement.getElementById和DOM中document.getElementById的便捷写法。

    2.$addhandler(Element,EventName,HandlerMethod)-是Sys.UI.DomEvent.addHandler.Elment的便捷写法。它将为一个form元素添加一些事件。EventName是事件的名称,比如“Click”等。

    3.$clearHandlers(Element)-是Sys.UI.DomElement.clearhandlers.Element的便捷写法。

    4.$create()-sys.Component.create的便捷写法。

    5.$find()-Sys.Application.findComponent的便捷写法。

    6.removeHandler()-$removeHandler是Sys.UI.DomEvent.removeHandler的便捷写法。

调用Web服务的例子:

(1)客户端代码:

    <asp:ScriptManager ID="ScriptManager1" runat="server" >

        <scripts>

            <asp:ScriptReference Path="TestScriptFile.js"></asp:ScriptReference>

        </scripts>

        <services>

            <asp:ServiceReference Path="WebServices/GetData.asmx"></asp:ServiceReference>

        </services>

</asp:ScriptManager>

<script language="javascript" type="text/javascript">

        function pageLoad()

        {

            GetCodeCampList();

        }

        function GetCodeCampList()

        {

            GetData.CodeCampList(CodeCampListOnCompleteCallBack,

                CodeCampOnServerException);

        }

        function GetCodeCampInfo()

        {

            var iCodeCamp;

            iCodeCamp = $get("CodeCamp").options[$get("CodeCamp").selectedIndex].value;

            GetData.CodeCampInfo(iCodeCamp, CodeCampInfoOnCallBack, CodeCampOnServerException);

        }

        function CodeCampListOnCompleteCallBack(result)

        {

            var iLength = document.getElementById("CodeCamp").options.length;

            for(i=0; i<iLength; i++)

            {

                $get("CodeCamp").options[0] = null;

            }

            document.getElementById("CodeCamp").options.add(new Option("", ""));

            for(i=0;i<result.length;i++)

            {

               $get("CodeCamp").options.add(

                    new Option(result[i].Name,

                    result[i].CodeCampId));

            }

            $get("ExceptionInfo").style.visibility = "hidden";

        }

        function CodeCampOnServerException(result)

        {

            var ExceptionOutput;

            var Return = "<br />";

            if ( null != result )

            {

                for(m in result)

                    alert(m);

                ExceptionOutput = "Message: " + result.get_message() + Return;

                ExceptionOutput += "Stack Trace: " + result.get_stackTrace() + Return;

                ExceptionOutput += "Exception Type: " + result.get_exceptionType() + Return;

                $get("ExceptionInfo").innerHTML = ExceptionOutput;

                $get("ExceptionInfo").style.visibility = "visible";

            }

        }

        function CodeCampInfoOnCallBack(result)

        {

            if ( null != result )

            {

                if ( result.length == 1 )

                {

                    $get("<%=CityInfo.ClientID%>").innerHTML = result[0].City;

                    $get("<%=EventDate.ClientID%>").innerHTML = result[0].DateOfEvent;

                    $get("<%=AttendeeNum.ClientID%>").innerHTML = result[0].NumberOfAttendees;

                }

                else{

                    alert(result.get_length() + " number of rows were returned.");

                }

            }

        }

</script>

(2)服务端代码:

using System;

using System.Data;

using System.Data.SqlClient;

using System.Collections;

using System.Collections.Generic;

using System.Web;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Web.Script.Services;

///<summary>

/// Summary description for GetData

///</summary>

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[ScriptService]

public class GetData : System.Web.Services.WebService

{

    public GetData()

    {

        //Uncomment the following line if using designed components

        //InitializeComponent();

    }

    /* 

     * [WebMethod]

        public string HelloWorld() {

            return "Hello World";

        }

        */

    [WebMethod]

    [GenerateScriptType(typeof(CodeCamp))]

    public CodeCamp[] CodeCampList()

    {

        CodeCamp lSpec;

        List<CodeCamp> lData = new List<CodeCamp>();

        DataTable dtData;

        CCData CodeCampData = new CCData();

        dtData = CodeCampData.GetCodeCamps();

        foreach (DataRow drData in dtData.Rows)

        {

            lSpec = new CodeCamp(Convert.ToInt32(drData["CodeCampId"]),

                Convert.ToString(drData["Name"]));

            lData.Add(lSpec);

        }

        return (lData.ToArray());

    }

    [WebMethod]

    [GenerateScriptType(typeof(CodeCampInformation))]

    public CodeCampInformation[] CodeCampInfo(int CodeCampId)

    {

        CodeCampInformation lSpec;

        List<CodeCampInformation> lData = new List<CodeCampInformation>();

        DataTable dtData;

       CCData CodeCampData = new CCData();

        dtData = CodeCampData.CodeCampInfo(CodeCampId);

        foreach (DataRow drData in dtData.Rows)

        {

            lSpec = new CodeCampInformation(Convert.ToString(drData["City"]),

                Convert.ToDateTime(drData["DateOfEvent"]),

                Convert.ToInt32(drData["NumberOfAttendees"]));

            lData.Add(lSpec);

        }

        //throw (new ApplicationException("Exception in method CodeCampInformation. Data passed: " + Convert.ToString(CodeCampId)));

        return (lData.ToArray());

    }

    [WebMethod]

    public DataTable CodeCampInfoWithDelay(int CodeCampId)

    {

        CCData CodeCampData = new CCData();

        System.Threading.Thread.Sleep(10000);

        return CodeCampData.CodeCampInfo(CodeCampId);

    }

}

需要注意的是在服务端代码中web服务在ajax应用中需要特别添加的是:

1.[ ScriptService]属性;

2.当有负责对象作为数据返回时,这个对象的信息必须要在[GenerateScriptType(typeof())]属性中标明,本例中是[GenerateScriptType(typeof(CodeCamp))]

解析:在客户端,scriptManager包含了需要使用的web服务和脚本文件(本例直接使用脚本代码)。

当页面载入时,pageload函数会被自动调用,pageload()函数是Asp.net Ajax里面特殊函数,如果页面包含此函数将会被页面初始化时自动调用。

        function GetCodeCampList()

        {

            GetData.CodeCampList(CodeCampListOnCompleteCallBack,

                CodeCampOnServerException);

        }

      当调用GetData.CodeCampList()函数时,在web服务中该函数是没有参数的,但是这里设置的两个参数并不会传递给服务端,第一个参数CodeCampListOnCompleteCallBackweb服务调用结束后调用的javascript函数,CodeCampOnServerException是在调用过程中出现错误时调用的客户端方法。

      其余的代码就是调用服务端然后返回结果并显示。在这里面已经使用了异步通信方法,当服务端响应完毕时调用某个函数。传统的同步方法有一些弱点:同步过程中页面会被锁定直到服务端响应,静态的页面并不能给用户足够的信息提示,有时候会出现无法响应的情况,这时候用户无法得到信息。

      ASP.NET Ajax异步通信包含五个基本的层:

1.     Client Application—开发者开发的应用程序。

2.     Script core library—本层包含了一些内嵌的模块可以被开发者使用。

3.     Client asynchronous communications layer—包含了多种通信方法。

4.     Browser compalibility layer—本层包含了对多种主流浏览器的抽象化。

5.     The browser itself—浏览器本身。

其中最负责的层就是client asynchronous communications layer.它包含了5个子层:

1.     代理层:包含web服务代理,页面方法代理,个性化代理和验证代理。这些是服务端暴露的服务。

2.     WebRequest—WebRequest类是用于创建Http请求。

3.     WebRequestManager-此类用于管理web请求和产生请求的对象间的关系(?)。

4.     XmlHttpExecutor—此类通过浏览器提供的支持产生网络请求。

5.     xmlHttp—此对象是xml http request的底层实现。

xmlhttpexecutorxmlhttp同层次的还有JSON序列化器,会在以后提及。

      在异步调用过程中数据的传递可以是简单类型,比如整形,字符串型等等,也可以是复杂类型,比如用户自定义类型。Web服务也可以不出现在单调的web服务页面中,也可以压缩在同一个aspx页面中,下面的例子:

<%@ Page Language="C#" %>

<%@ Import Namespace="System.Web.Services" %>

    <script language="c#" runat="server">

        [WebMethod]

        public static string CallMethod(string UserName)

        {

            return ("Hi " + UserName);

        }

    </script>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Page Methods Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true"></asp:ScriptManager>

    <div>

    <input type="button" value="Click Me" id="btnClickMe" onclick="CallPageMethod()" />

    </div>

    </form>

    <script type="text/javascript">

        function CallPageMethod()

        {

            PageMethods.CallMethod("Wally", CallPageMethodOnComplete);

        }

        function CallPageMethodOnComplete(result)

        {

            alert("Value returned: " + result);

        }

    </script>

</body>

</html>

      需要注意的是:

1.     <scriptManager>EnablePageMethod属性需要设置为true.

2.     服务端方法建立时应该冠以[WebMethod]属性并且是静态(static).即使服务端代码很可能是对页面是私有的,它也必须被标记为public共有的.

3.     在调用的脚本代码中,要用PageMethods.MethodName来代替ClassName.MethodName.

     

数据传输格式JSON:

      虽然Ajax的命名包含了Xml但是ASP.NET Ajax的数据传输格式却并不是XML而是JSON. JSON是一种数据格式化方法,他的内容有两种方式:

1.     一组值,如数值等.

2.     一组名字/对的集合,例如对象.

例如类:

public class CodeCampInformation

{

    private string _City;

    private DateTime _DateOfEvent;

    private int _NumberOfAttendees;

    public CodeCampInformation()

    {

    }

    public CodeCampInformation(string City, DateTime DateOfEvent, int NumberOfAttendees)

    {

        _City = City;

        _DateOfEvent = DateOfEvent;

        _NumberOfAttendees = NumberOfAttendees;

    }

    public string City

    {

        get { return (_City); }

        set { _City = value; }

    }

    public DateTime DateOfEvent

    {

        get { return (_DateOfEvent); }

        set { _DateOfEvent = value; }

    }

    public int NumberOfAttendees

    {

        get { return (_NumberOfAttendees); }

        set { _NumberOfAttendees = value; }

    }

}

会被格式化为:

[{"City":"Orlando","DateOfEvent":""/Date(1143302400000)"/","NumberOfAttendees":150}]

如果要更改传输格式为XML可以在web服务中更改以下属性:

      [ScriptMethod(ResponseFormat.XML)]

      现在我们已经可以调用webservice进行异步操作了,在这种情况下有很多好处,但是在某些情况下可能会出现一些问题。

      考虑以下的情况:

1.     有一个服务器控件 drop-downlist(下拉框)。

2.     另一个窗体产生ajax调用并使服务器传回数据。

3.     传回的数据将填充drop-downlist的项。

4.     用户点击一个按键,引发服务器控件单击事件。

   当上面的四个步骤完成以后,你就会发现一个异常非法回传或是回传参数异常,这些异常是因为ASP.NET运行时无法知道下拉框的新值。出现这个问题的原因是ASP.NET运行时试图验证发给服务器的数据,下拉框的值将会被再次和web请求时的值进行验证,当这些值不同时,运行时就会抛出异常。

      解决这个问题有下面几种方法:

            1.将EnableEventValidation属性设置为false,当设置为false时,将会有安全漏洞,所以应该在需要的页面进行这样的设置。

           2. 在页面的Render事件,运行时可以设置控件的允许值。这种方法可能会引起大量数据的保存,从而使ViewState数据库变的很大。方法实例:

      Protected override void Render(HtmlTextWriter writer)
      {

           ClientScript.RegisterForEventValidation(

                 This.ddlName.UniqueID,”Fred Smith”);

           ClientScript.RegisterForEventValidation(

                 This.ddlName.UniqueID,String.Empty);

           Base.Render(writer);

      }

3.     采用ASP.NET HTML控件,HTML控件不保存自己的状态,也不会对数据进行验证,将普通HTML控件的属性里面添加 runat=”server”就可以作为ASP.NET HTML控件使用了。

需要注意的是,上述问题在TextBox控件下不会发生。

      除此之外,还有一个问题。由于ASP.NETViewState机制,可能会引起Ajax异步调用时的值丢失问题,例如:

1.     有一个页面包含一个下拉框控件,IDddlName,一个客户端按钮,当用户按下时会引发ASP.NET Ajax异步调用,返回数据“HTML Button”,还有一个服务器控件按钮,会引起页面回发和刷新,返回数据”Server Button”

2.     当用户点击客户端按钮时,引发Ajax异步调用并将返回值填充下拉框”HTML Button”

3.     用户再次点击服务器按钮,这时你会发现下拉框原先的“HTML Button“会被刷新掉,并被赋值为他的初始值。

引起这个问题的原因就是ViewState,每次刷新界面,控件的值将会被ViewState重新填充。有两种方法可以解决这个问题:

1.     自己控制状态,你会在客户端将状态序列化并保存在form元素,然后反序列化这些数据并填充到

户端控件中。

2.     使一个在完成Ajax操作之后,只能运行ajax操作,在这种情况下只能使用ajax操作,是一种简单的避免上述问题的方法,当然可能无法满足在页面要求复杂功能的情况。

调用WebService异步操作最后一个需要注意的是错误处理,我们不可能保证异步操作的每一步都能正确运行,调用web服务的格式如下:

WebServiceClass.MethodName(p1,p2,…Pn,OnCompleteCallBack,OnServerExceptionCallBack);

其中p1p2…pnweb服务参数,OnCompeleteCallBackweb服务调用完毕后返回数据的处理,最后一个参数OnServerExceptionCallBack就是当服务器调用出现错误时,调用的处理函数。OnServerExceptionCallBack函数是一个JavaScript函数并接受一个异常对象,实例:

        function CodeCampOnServerException(result)

        {

            var ExceptionOutput;

            var Return = "<br />";

            if ( null != result )

            {

                for(m in result)

                    alert(m);

                ExceptionOutput = "Message: " + result.get_message() + Return;

                ExceptionOutput += "Stack Trace: " + result.get_stackTrace() + Return;

                ExceptionOutput += "Exception Type: " + result.get_exceptionType() + Return;

                $get("ExceptionInfo").innerHTML = ExceptionOutput;

                $get("ExceptionInfo").style.visibility = "visible";

            }

      }

      Result是一个异常对象,他的方法有:

           Get_Message();//获得异常错误消息。

           Get_stackTrace();//可以跟踪错误堆栈并获得相应信息。

           Get_ExceptionType();//获得异常类型信息。

           Get_StatusCode();//获得HTTP状态代码

           Get_TimedOut();//一个二进制标识代表是否有超时发生。

转载于:https://www.cnblogs.com/leochu2008/articles/1070684.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值