目前的工作项目分为前端和后台,双方事先约定接口,之后独立开发。后台每天开发完后在测试服务器上部署,前端连接测试服务器进行数据交互。前端和后台分开的好处是代码不用混在一个工程里一起build,互不干涉。但由此也引发出一个问题,那就是Ajax跨域。目前的项目是一个Single Page App, 基本上所有数据交互都是通过Ajax请求来完成的。为了方便平时前端开发,必须解决跨域问题。

跨域方案有多种,我认为基本上可分为两大类,一类是需要目标Server配合的,另一类则不需要。前者限制稍多,必须由服务器显式允许跨域才行,比如返回HTTP头信息,修改服务器配置,返回JavaScript等。可以用JSONP,iframe等方式实现。后者主动权就掌握在跨域客户端,服务器不用为此做任何配置。这就是本文要说的Web代理。

作为前端开发,自然希望主动权在自己手里,不用劳烦Sever配合。所以我选择了使用Web代理方案。该方案的原理其实很简单,就是将跨域请求转变为同源请求。具体来说,就是在本地搭建一个Web站点(代理),该站点可以向目标服务器发送HTTP请求,接收响应。它的行为跟浏览器类似,因此目标服务器是不用区分对待的。然后将本地的前端站点也部署到这个Web站点中,这样它们就属于同一个域了。所有针对目标服务器的Ajax请求,都发送到这个代理,然后由代理负责转发和接收响应。这样就避开了跨域之名,却有跨域之实。

图有点丑,欢迎拍砖。

剩下的就是实现细节了。由于对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请求需要另外发参数。

Cookie

由于所有的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跨域

使用Web代理实现Ajax跨域的更多相关文章

  1. asp.net web api2.0 ajax跨域解决方案

    asp.net web api2.0 ajax跨域解决方案 Web Api的优缺点就不说了,直接说怎么跨域,我搜了一下,主要是有两种.  一,ASP.NET Web API支持JSONP,分两种 1, ...

  2. 浅谈linux 下,利用Nginx服务器代理实现ajax跨域请求。

    ajax跨域请求对于前端开发者几乎在任何一个项目中都会用到,众所周知,跨域请求有三种方式: jsonp; XHR2 代理: jsonp: 这种应该是开发中是使用的最多的,最常见的跨域请求方法,其实aj ...

  3. 用iframe设置代理解决ajax跨域请求问题

    面对ajax跨域请求的问题,想用代理的方式来解决这个跨域问题.在服务器端创建一个静态的代理页面,在客户端用iframe调用这个代理 今天在项目中需要做远程数据加载并渲染页面,直到开发阶段才意识到aja ...

  4. 支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示

    随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Angularjs的框架来做UI,而数据则由另一个Web Api 的网站项目来支撑.注意,这里是 ...

  5. web api 跨域请求,ajax跨域调用webapi

    1.跨域问题仅仅发生在Javascript发起AJAX调用,或者Silverlight发起服务调用时,其根本原因是因为浏览器对于这两种请求,所给予的权限是较低的,通常只允许调用本域中的资源,除非目标服 ...

  6. Web Api 2(Cors)Ajax跨域访问

    支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示   随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Ang ...

  7. $.ajax 跨域请求 Web Api

    WepApi确实方便好用,没有配置文件,一个apicontroller直接可以干活了.但今天用$.ajax跨域请求的时候总是获取不到数据,用fiddler一看确实抓到了数据,但回到$.ajax函数中, ...

  8. AJAX跨域解决方案

    从AJAX诞生那天起,XMLHttprequest对象不能跨域请求的问题就一直存在,这似乎是一个很经典的问题了,是由于javascript的同源策略所导致. 解决的办法,大概有如下几种: 1. 使用中 ...

  9. 浅析Ajax跨域原理及JQuery中的实现分析

    AJAX 的出现使得网页可以通过在后台与服务器进行少量数据交换,实现网页的局部刷新.但是出于安全的考虑,ajax不允许跨域通信.如果尝试从不同的域请求数据,就会出现错误.如果能控制数据驻留的远程服务器 ...

随机推荐

  1. 需要MARK一下,奇怪的ANDROID SDK自带的APK加密功能的问题

    花了两天时间,各种调试APP,发现问题不在于代码. 在于用了SDK里的加密,导致运行其中一个多线程中的ACTIVITY, 就会黑屏,返回按钮也没用. 发现这个问题的思路是因为,我发现连手机直接调试,一 ...

  2. jQuery之对话框

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta http ...

  3. C#_简单Excel导入

    引用程序集 Microsoft.Office.Core Microsoft.Office.Interop.Excel using System; using System.Collections.Ge ...

  4. Cocos移植到Android-通过命令行交叉编译打包

    当我们在Windows下使用Visual Studio开发好Win32平台的游戏程序后,我们需要把它们移植到不同的平台上.Cocos2d-x支持很多个平台,然而,我们不可能介绍全部平台的移植.我们总结 ...

  5. 验证hashmap非线程安全

    http://www.blogjava.net/lukangping/articles/331089.html final HashMap<String, String> firstHas ...

  6. NodeJS服务器退出:完成任务,优雅退出

    上一篇文章,我们通过一个简单的例子,学习了NodeJS中对客户端的请求(request)对象的解析和处理,整个文件共享的功能已经完成.但是,纵观整个过程,还有两个地方明显需要改进: 首先,不能共享完毕 ...

  7. 重回cnblogs

    毕业一年,关于工作的想法和思路渐渐充实,是时候回到cnblogs,开始写技术日志了.

  8. web项目嵌入Jetty运行的两种方式(Jetty插件和自制Jetty服务器)

    在开发Java web项目时候,可以在项目中嵌入Jetty服务的方式来运行web程序. 由于最近开发web项目,自己使用的是比较旧的eclipse不支持导入tomcat来运行项目,于是就学习了下使用项 ...

  9. 数据库降级-从sqlserver 2008 降到 2005

    前天遇到一个问题,是一个数据库是Sqlserver 2008的,而平台数据库库是2005的,需要把2008的数据库附加进来,试了很多办法,现在觉得最好的办法就是导出导入办法. 第一步 新建一个Sqls ...

  10. A3992学习记录

    ATmega64+A3992驱动步进电机 //ATmega 64a 电机驱动板程序//编译环境 AVR Studio 4.17/AVR GCC//系统外部时钟16M//作者:虞恺 //日期:2012. ...