前言:自己比较小白,本文中有很多不对的地方欢迎指正
自己以前用python的flask框架写后端程序,在实现跨域时安装flask_cors,然后如下:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
//......
这样的话就可以实现跨域了,且所有的Origin都可以对其发送请求,例如www.baidu.com可以向自己的127.0.0.1:5000的flask程序发送请求,这种跨域方式中,如果前端发送请求时不带上cookie,那么在向前端返回数据时就可以使用
from flask import jsonfiy
@app.route('/../')
def func():
//......
return jsonfiy(Obejct)
来返回数据给前端
但是如果前端发送的请求需要带上cookie 的话,那获取cookie 和 返回数据就需要另一种方式
from flask import request,make_response
@app.route('/.../')
def func():
cookie = request.cookies.get('key')#获取cookie
response = make_response(jsonfiy(Object))
response.set_cookie('key','value',max_age=3600)
response.headers['Access-Control-Allow-Credentials']='true'#设置响应数据的头部,不然无法将response返回给前端
return response
至于为什么要这么麻烦,是因为,如果使用flask采用前后端分离的方式进行开发时,就会导致跨域,且flask的通过session进行用户验证的方式也无法使用,表现为:session['key']取值为空,暂时未去深挖原因,预估还是前后端分离的开发模式导致的。
前面说了一大堆,主要是想说明一种情况:跨域可以实现:1.所有的Origin对自己开发的服务器程序发送请求,2.能发送和接收cookie。这两个效果,在进行.Net Core开发后端程序时,也以为也可以很简单的实现这种效果,但是比较麻烦:
一:常规的.Net Core设置跨域方式
public void ConfigureServices(IServiceCollection services)
{
//.......
services.AddControllers();
services.AddCors(options => { options.AddPolicy("allOrigin", p => { p.AllowAnyOrigin(); }); });//所有Origin可以请求,但是这里p.AllowAnyOrigin()后面不允许加上.AllowCredentials()来达到请求携带cookie的效果,原因如下
//services.AddCors(options => { options.AddPolicy("allOrigin", p => { p.WithOrigins(["http://www.baidu.com"]).AllowCredentials();}); });//这里可以允许来自http://wwww.baidu.com的携带cookie的请求
//......
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseCookiePolicy();
app.UseCors("allOrigin");
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
这种跨域方式可以允许所有的Origin对其进行请求,在服务器响应数据中,我们能看到头部的Access-Control-Allow-Origin : *
但是这种方式的跨域不允许发送带有cookie的请求。因为,如果需要请求带有cookie,那么响应头的Access-Control-Allow-Credentials="true",但是在服务器响应时,不允许在Access-Control-Allow-Origin=*时,将Access-Control-Allow-Credentials="true",只能将Access-Control-Allow-Origin设置为某一个特定的Origin时,如:hhtp://www.baidu.com,只有这样才可以将Access-Control-Allow-Credentials="true",从而实现请求接收和发送cookie。
可是又有另外一个问题,当我们并不是企业那样的开发,而只是作业,或者其他不能够明确自己前端的Origin的情况下,怎么办呢,换句话说,该怎么样才能够实现像上面的flask那样的两个效果。
思路是:在.Net 进行跨域处理前,先截获此过程。例如,我从http://www.baidu.com发送一个带cookie的请求,按照正常的流程:
①.Net拿到请求,
②读取startup.cs设置的跨域配置,此时分两种情况:a:p.AllowAnyOrigin().AllowCredentials()(这是错误的写法,虽然想达到允许所有Origin发送携带cookie请求的效果,但是有问题,原因如下),b:p.policy.WithOrigins("http://www.baidu.com").AllowCredentials();
③ 根据前一步的结果进行一系列判定,如果是情况a,在准备将Access-Control-Allow-Credentials设置为true时,发现前者为*,那么此过程报错,拒绝响应。对于情况b,对比后发现请求的Origin在[http://www.baidu.com]之中,那么就将Access-Control-Allow-Origin设置为"http://www.baidu.com",在准备将Access-Control-Allow-Credentials设置为true时,发现前者不为*,那么此过程顺利进行。响应数据给前端
我们能做的就是在第③步前,覆盖.Net判定跨域的函数,但是此时需要对startup.cs的跨域配置修改一下,思路是:将允许跨域的Origin数组设为空,然后在第③步前将请求的Oringin添加进去,这样的话,在第③步判定时请求的Oringin必然存在于p.WithOrigins([string url])中了。
public class WildcardCorsService : CorsService
{
public WildcardCorsService(IOptions<CorsOptions> options, ILoggerFactory loggerFactory) : base(options, loggerFactory)
{
}
public override void EvaluateRequest(HttpContext context, CorsPolicy policy, CorsResult result)
{
var origin = context.Request.Headers[CorsConstants.Origin];
policy.Origins.Add(origin);
result.IsOriginAllowed = true;
result.AllowedOrigin = origin;
base.EvaluatePreflightRequest(context, policy, result);
}
public override void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
{
var origin = context.Request.Headers[CorsConstants.Origin];
Console.WriteLine(origin);
policy.Origins.Add(origin);
base.EvaluatePreflightRequest(context, policy, result);
}
}
再然后将其注入到service:
public void ConfigureServices(IServiceCollection services)
{
//.......
services.AddControllers();
services.AddCors(options => { options.AddPolicy("allOrigin", p => { p.AllowAnyOrigin(); }); });//所有Origin可以请求,但是这里p.AllowAnyOrigin()后面不允许加上.AllowCredentials()来达到请求携带cookie的效果,原因如下
//services.AddCors(options => { options.AddPolicy("allOrigin", p => { p.policy.WithOrigins("http://www.baidu.com").AllowCredentials();}); });//这里可以允许来自http://wwww.baidu.com的携带cookie的请求
services.Add(ServiceDescriptor.Transient<ICorsService, WildcardCorsService>());
//......
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseCookiePolicy();
app.UseCors("allOrigin");
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
然后就大功告成了!!!