使用Web代理實現Ajax跨域


目前的工作項目分為前端和后台,雙方事先約定接口,之后獨立開發。后台每天開發完后在測試服務器上部署,前端連接測試服務器進行數據交互。前端和后台分開的好處是代碼不用混在一個工程里一起build,互不干涉。但由此也引發出一個問題,那就是Ajax跨域。目前的項目是一個Single Page App, 基本上所有數據交互都是通過Ajax請求來完成的。為了方便平時前端開發,必須解決跨域問題。

跨域方案有多種,我認為基本上可分為兩大類,一類是需要目標Server配合的,另一類則不需要。前者限制稍多,必須由服務器顯式允許跨域才行,比如返回HTTP頭信息,修改服務器配置,返回JavaScript等。可以用JSONP,iframe等方式實現。后者主動權就掌握在跨域客戶端,服務器不用為此做任何配置。這就是本文要說的Web代理。

作為前端開發,自然希望主動權在自己手里,不用勞煩Sever配合。所以我選擇了使用Web代理方案。該方案的原理其實很簡單,就是將跨域請求轉變為同源請求。具體來說,就是在本地搭建一個Web站點(代理),該站點可以向目標服務器發送HTTP請求,接收響應。它的行為跟瀏覽器類似,因此目標服務器是不用區分對待的。然后將本地的前端站點也部署到這個Web站點中,這樣它們就屬於同一個域了。所有針對目標服務器的Ajax請求,都發送到這個代理,然后由代理負責轉發和接收響應。這樣就避開了跨域之名,卻有跨域之實。

setting

圖有點丑,歡迎拍磚。

剩下的就是實現細節了。由於對Asp.Net比較熟悉,就用它創建一個Web站點。在實現過程中,我覺得有幾點細節需要關注一下。

HTTP Request攔截

由於事先不知道會有哪些請求(就算知道,請求的URL可能也會太多),不可能針對每個URL寫一個轉發規則。因此需要獲取所有Ajax請求,經過統一的處理再轉發到目標服務器。眾所周知,Asp.Net的IHttpHandler接口定義了針對某個具體的HTTP請求的處理方法,如前所述,不可能為每個請求URL寫一個Handler,有沒有一種辦法可以獲取任何請求的信息?答案是HttpModule。HttpModule是先於HttpHandler處理的,所以在這里可以做些手腳。定義一個HttpModule也很簡單,只要實現IHttpModule接口,監聽Request事件就可以了。當然,需要在Web.config文件里注冊這個HttpModule才能使用。

XDomainProxy.cs定義:

public class XDomainProxy : IHttpModule
{
    public void Dispose() { }
    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(Application_BeginRequest);
        context.EndRequest += new EventHandler(Application_EndRequest);
    }
    public void Application_BeginRequest(object sender, EventArgs e)
    {

    }
    public void Application_EndRequest(object sender, EventArgs e)
    {
        HttpApplication application = sender as HttpApplication;
        HttpContext context = application.Context;
        HttpResponse response = context.Response;
        response.StatusCode = 200;
    }
}

Web.config中注冊HttpModule

<configuration>
	<system.web>
		<httpHandlers>
	     	... 
		</httpHandlers>
	    <httpModules>
	      <add name="ProxyModule" type="AAProxy.Proxy"/>
	    </httpModules>
	</system.web>
</configuration>

模擬HTTP請求

.Net 已經封裝好了HttpWebRequestHttpWebResponse兩個關鍵的類,非常方便。

//實例化web訪問類
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
request.Method = context.Request.HttpMethod;
request.ContentType = context.Request.ContentType;

string postData = context.Request.Form.ToString();
byte[] postdatabytes = Encoding.UTF8.GetBytes(postData);
request.ContentLength = postdatabytes.Length;
request.AllowAutoRedirect = false;
request.CookieContainer = loginCookie;
request.KeepAlive = true;
request.UserAgent = context.Request.UserAgent;

if (context.Request.HttpMethod == "POST")
{
    Stream stream;
    stream = request.GetRequestStream();
    stream.Write(postdatabytes, 0, postdatabytes.Length);
    stream.Close();
}

//接收響應
response = (HttpWebResponse)request.GetResponse();
 using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
    string content = reader.ReadToEnd();
}

這樣就完成了一個簡單的HTTP請求。注意POST請求需要另外發參數。

由於所有的Ajax請求都需要用戶登錄才能進行,所以代理程序也必須模擬用戶登錄目標服務器站點。用戶的登錄信息是保存在cookie里的,所以在模擬請求的時候還需要保存cookie。HttpWebRequest有個CookieContainer屬性,就是用來裝cookie的。保存好cookie后,之后的每個請求都必須帶上它,這樣才能維持登錄狀態。另外,還需要把這些cookie寫到瀏覽器中。這里需要注意,.Net封裝了兩個關於cookie的類,HttpCookieCookie。前者是Asp.Net程序寫回給瀏覽器用的,而后者是向別的服務器發起HTTP請求時用的。所以代理程序收到目標Server返回的Cookie對象時,要轉換成HttpCookie對象再返回給瀏覽器。

//response是目標服務器的響應對象,context是返回給瀏覽器的上下文對象
void SetCookie(HttpWebResponse response, HttpContext context)
{
    foreach (Cookie cookie in response.Cookies)
    {
        HttpCookie httpCookie = new HttpCookie(cookie.Name, cookie.Value);
        httpCookie.Domain = cookie.Domain;
        httpCookie.Expires = cookie.Expires;
        httpCookie.Path = cookie.Path;
        httpCookie.HttpOnly = cookie.HttpOnly;
        httpCookie.Secure = cookie.Secure;
        context.Response.SetCookie(httpCookie);
    }
}

Https連接

在使用過程中發現,如果目標服務器的數字證書是不受信任的,連接將被拒絕。這在連接Server端開發同事的PC調試時不方便,怎么破?.Net對Http請求有個證書驗證機制,只要讓這個驗證總是通過就好了。(一切為了開發方便)

ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(ValidateRemoteCertificate);

private bool ValidateRemoteCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
    return true;
}

部署Web代理

部署就很簡單了,新建一個IIS站點,根目錄指向前端項目的路徑,設定一個端口號。將Web代理發布到本地的某個目錄下,然后作為一個應用程序添加到之前的IIS站點中即可。

總結

經過以上幾個關鍵步驟,基本上搭建好了Web代理。當然,這個過程中還有一些細節需要關注,比如轉發請求的URL映射處理,Session過期,異常處理等。總的來說,沒有用到什么高深的技術,只是針對各種跨域方案做了一個選擇。

原文:使用Web代理實現Ajax跨域


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM