JQuery Ajax通過Handler訪問外部XML資料

weixin_34391854發表於2010-05-31

  JQuery是一款不錯的Javascript指令碼框架,相信園子裡的很多朋友對它都不陌生,我們在開發Web應用程式時難免會使用到Javascript指令碼,而使用一款不錯的指令碼框架將會大大節省我們的開發時間, 並可以毫不費力地實現很多非常酷的效果。JQuery的使用非常簡單,我們只需要從其官方網站上下載一個指令碼檔案並引用到頁面上即可,然後你就可以在你的指令碼程式碼中任意使用JQuery提供的物件和功能了。

  在JQuery中使用Ajax方法非同步獲取伺服器資源非常簡單,讀者可以參考其官方網站上提供的例子http://api.jquery.com/category/ajax/。當然,作為客戶端指令碼,JQuery也會遇到跨域訪問資源的問題,什麼是跨域訪問呢?簡單來說就是指令碼所要訪問的資源屬於網站外部的資源,指令碼所在的位置和資源所在的位置不在同一區域。預設情況下,瀏覽器是不允許直接進行資源的跨域訪問的,除非客戶端瀏覽器有設定,否則訪問會失敗。在這種情況下,我們一般都會採用在伺服器端使用handler來解決,就是說在指令碼和資源之間建立一個橋樑,讓指令碼訪問本站點內的handler,通過handler去訪問外部資源。這個是非常普遍的做法,而且操作起來也非常簡單,因為會經常使用到,所以在此記錄一下,方便日後使用!

  首先需要在網站中建立一個handler,在Visual Studio中新建一個Generic Handler檔案,拷貝下面的程式碼:

<%@ WebHandler Language="C#" Class="WebApplication1.Stock" %>

namespace WebApplication1
{
    
using System;
    
using System.IO;
    
using System.Net;
    
using System.Text;
    
using System.Web;
    
using System.Collections.Generic;
    
using System.Linq;

    
/// <summary>
    
/// Asynchronous HTTP handler for rendering external xml source.
    
/// </summary>
    public class Stock : System.Web.IHttpAsyncHandler
    {
        
private static readonly SafeList safeList = new SafeList();
        
private HttpContext context;
        
private WebRequest request;

        
/// <summary>
        
/// Gets a value indicating whether the HTTP handler is reusable.
        
/// </summary>
        public bool IsReusable
        {
            
get { return false; }
        }

        
/// <summary>
        
/// Verify that the external RSS feed is hosted by a server on the safe list
        
/// before making an asynchronous HTTP request for it.
        
/// </summary>
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            var u 
= context.Request.QueryString["u"];
            var uri 
= new Uri(u);
            
if (safeList.IsSafe(uri.DnsSafeHost))
            {
                
this.context = context;
                
this.request = HttpWebRequest.Create(uri);
                
return this.request.BeginGetResponse(cb, extraData);
            }
            
else
            {
                
throw new HttpException(204"No content");
            }
        }

        
/// <summary>
        
/// Render the response from the asynchronous HTTP request for the RSS feed
        
/// using the response's Expires and Last-Modified headers when caching.
        
/// </summary>
        public void EndProcessRequest(IAsyncResult result)
        {
            
string expiresHeader;
            
string lastModifiedHeader;
            
string rss;
            
using (var response = this.request.EndGetResponse(result))
            {
                expiresHeader 
= response.Headers["Expires"];
                lastModifiedHeader 
= response.Headers["Last-Modified"];

                
using (var stream = response.GetResponseStream())
                
using (var reader = new StreamReader(stream, true))
                {
                    rss 
= reader.ReadToEnd();
                }
            }

            var output 
= this.context.Response;
            output.ContentEncoding 
= Encoding.UTF8;
            output.ContentType 
= "text/xml;"// "application/rss+xml; charset=utf-8";
            output.Write(rss);

            var cache 
= output.Cache;
            cache.VaryByParams[
"u"= true;

            DateTime expires;
            var hasExpires 
= DateTime.TryParse(expiresHeader, out expires);

            DateTime lastModified;
            var hasLastModified 
= DateTime.TryParse(lastModifiedHeader, out lastModified);

            cache.SetCacheability(HttpCacheability.Public);
            cache.SetOmitVaryStar(
true);
            cache.SetSlidingExpiration(
false);
            cache.SetValidUntilExpires(
true);

            DateTime expireBy 
= DateTime.Now.AddHours(1);
            
if (hasExpires && expires.CompareTo(expireBy) <= 0)
            {
                cache.SetExpires(expires);
            }
            
else
            {
                cache.SetExpires(expireBy);
            }

            
if (hasLastModified)
            {
                cache.SetLastModified(lastModified);
            }
        }

        
/// <summary>
        
/// Do not process requests synchronously.
        
/// </summary>
        public void ProcessRequest(HttpContext context)
        {
            
throw new InvalidOperationException();
        }
    }

    
/// <summary>
    
/// Methods for matching hostnames to a list of safe hosts.
    
/// </summary>
    public class SafeList
    {
        
/// <summary>
        
/// Hard-coded list of safe hosts.
        
/// </summary>
        private static readonly IEnumerable<string> hostnames = new string[]
        {
            
"cnblogs.com",
            
"msn.com",
            
"163.com",
            
"csdn.com"
        };

        
/// <summary>
        
/// Prefix each safe hostname with a period.
        
/// </summary>
        private static readonly IEnumerable<string> dottedHostnames =
            from hostname 
in hostnames
            select 
string.Concat(".", hostname);

        
/// <summary>
        
/// Tests if the <paramref name="hostname" /> matches exactly or ends with a
        
/// hostname from the safe host list.
        
/// </summary>
        
/// <param name="hostname">Hostname to test</param>
        
/// <returns>True if the hostname matches</returns>
        public bool IsSafe(string hostname)
        {
            
return MatchesHostname(hostname) || MatchesDottedHostname(hostname);
        }

        
/// <summary>
        
/// Tests if the <paramref name="hostname" /> ends with a hostname from the
        
/// safe host list.
        
/// </summary>
        
/// <param name="hostname">Hostname to test</param>
        
/// <returns>True if the hostname matches</returns>
        private static bool MatchesDottedHostname(string hostname)
        {
            
return dottedHostnames.Any(host => hostname.EndsWith(host, StringComparison.InvariantCultureIgnoreCase));
        }

        
/// <summary>
        
/// Tests if the <paramref name="hostname" /> matches exactly with a hostname
        
/// from the safe host list.
        
/// </summary>
        
/// <param name="hostname">Hostname to test</param>
        
/// <returns>True if the hostname matches</returns>
        private static bool MatchesHostname(string hostname)
        {
            
return hostnames.Contains(hostname, StringComparer.InvariantCultureIgnoreCase);
        }
    }
}

   我給出的例子中是想通過Ajax非同步取得msn站點上微軟的股票資訊,其外部資源地址為http://money.service.msn.com/StockQuotes.aspx?symbols=msft,我們在頁面上這樣使用JQuery api通過Handler來訪問資料:

<!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>
    
<title></title>

    
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>

</head>
<body>
    
<div id="con">
        
<span id="loader">loading...</span>
    
</div>

    
<script type="text/javascript">
        
function getData() {
            $(
"#loader").ajaxStart(function() {
                $(
this).show();
            });

            $(
"#loader").ajaxComplete(function() {
                $(
this).hide();
            });

            $.ajax({
                type: 
"GET",
                url: 
"Stock.ashx?u=http://money.service.msn.com/StockQuotes.aspx?symbols=msft",
                dataType: 
"xml",
                success: 
function(data) {
                    
var last = "";
                    
var change = "";
                    
var percentchange = "";
                    
var volume = "";
                    
var cap = "";
                    
var yearhigh = "";
                    
var yearlow = "";
                    $(data).find(
'ticker').each(function() {
                        last 
= $(this).attr('last');
                        change 
= $(this).attr('change');
                        percentchange 
= $(this).attr('percentchange');
                        volume 
= $(this).attr('volume');
                        cap 
= $(this).attr('marketcap');
                        yearhigh 
= $(this).attr('yearhigh');
                        yearlow 
= $(this).attr('yearlow');
                        document.getElementById(
'con').innerHTML = '<span>name:' + last + '&nbsp;&nbsp;&nbsp;&nbsp;high:' + volume + '&nbsp;&nbsp;&nbsp;&nbsp;low:' + cap + '</span>';
                    })
                }
            });
        }

        $(window).load(getData);

    
</script>

</body>
</html>

   下面是實現的結果:

name:25.8    high:67,502,221    low:$226,107,039,514

  Handler的寫法基本都大同小異,因此可以寫成一個通用的例子,以後如遇到在指令碼中需要跨域訪問資源時便可以直接使用!程式碼記錄於此,方便查閱。

相關文章