目前的工作项目分为前端和后台,双方事先约定接口,之后独立开发。后台每天开发完后在测试服务器上部署,前端连接测试服务器进行数据交互。前端和后台分开的好处是代码不用混在一个工程里一起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. hiho欧拉路·二 --------- Fleury算法求欧拉路径

    hiho欧拉路·二 分析: 小Ho:这种简单的谜题就交给我吧! 小Hi:真的没问题么? <10分钟过去> 小Ho:啊啊啊啊啊!搞不定啊!!!骨牌数量一多就乱了. 小Hi:哎,我就知道你会遇 ...

  2. Contoso 大学 - 1 - 为 ASP.NET MVC 应用程序创建 EF 数据模型

    原文 Contoso 大学 - 1 - 为 ASP.NET MVC 应用程序创建 EF 数据模型 原文地址:Creating an Entity Framework Data Model for an ...

  3. Cocos移植到Android-使用Eclipse交叉编译打包

    如果对命令行望而生畏,我们可以借助于安装有ADT插件的Eclipse工具实现交叉编译.使用Eclipse进行交叉编译,首先需要将要编译的工程导入到Eclipse的Workspace中,Workspac ...

  4. (POJ 2318)TOYS 向量叉积

    题目链接:http://poj.org/problem?id=2318 #include<stdio.h> #include<cstdlib> #include<cstr ...

  5. STL Traits编程技法

    traits编程技法大量运用于STL实现中.通过它在一定程度上弥补了C++不是强型别语言的遗憾,增强了C++关于型别认证方面的能力. traits编程技法是利用“内嵌型别”的编程技法和编译器的temp ...

  6. MFC中快速应用OpenCV(转)

    转载链接:http://wiki.opencv.org.cn/index.php/MFC%E4%B8%AD%E5%BF%AB%E9%80%9F%E5%BA%94%E7%94%A8OpenCV 简介和缘 ...

  7. linux下golang的配置

    linux下golang的配置 之前开发golang一直在windows下,今天在linux下试了一下 ,遇到一些梗,比如go 找不到 sync包.花了一小时全部解决,把过程记录一下. 安装 go 我 ...

  8. Nginx日志按天分割

    核心思想:使用crontab在每日23:59执行日志分割. 1.配置nginx日志信息,vim /etc/logrotate.d/nginx /var/log/nginx/*.log { nocomp ...

  9. apache重写字段详细说明

    用Apache虚拟主机的朋友很多,apache提供的.htaccess模块可以为每个虚拟主机设定rewrite规则,这对网站SEO优化相当有用,同时也改善了用户体验.国内的虚拟机一般不提供.htacc ...

  10. 获取客户端访问的ip地址

    function real_ip() { static $realip = NULL; if ($realip !== NULL) { return $realip; } if (isset($_SE ...