最近一段时间,我一直在积极尝试对站点的后台管理系统进行重大的架构调整,即将其前后端分离,并且把后台改造为 asp.net core webapi 的形式。在这个充满挑战的过程中,我着实遇到了其中的一些棘手问题。
首先需要明确的是,在进行前后端分离后,跨域问题必然会成为一个需要重点关注和解决的关键环节。虽然可以通过使用代理的方式来实现跨域访问,但考虑到代码是由自己掌控的,完全可以进行针对性的修改和优化,以达到更好的效果。
在 asp.net core 中,要解决跨域问题,首先需要在 Startup.cs 文件的 ConfigureServices 方法中添加以下代码:
services.AddCors(options =>
{
options.AddPolicy("any", builder =>
{
builder.AllowAnyOrigin() //允许任何来源的主机访问
//builder.WithOrigins("http://localhost:8080") //允许特定的 http://localhost:8080 的主机访问
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();//指定处理 cookie
});
});
接着,在下面的 Configure 方法中加入 app.UseCors("any");。原本以为这样就能够顺利解决跨域问题,然而实际情况却并非如此,依旧出现了报错信息。
为了解决这个问题,我决定加入一个过滤器,用于在请求的 Header 中手动添加 Access-Control-Allow-Origin 等关键信息。以下是过滤器的具体实现代码:
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Blogs.Service.Manage
{
public class CorsActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
string referer = context.HttpContext.Request.Headers["Referer"].ToString();
if (!String.IsNullOrEmpty(referer))
{
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", Regex.Match(referer, "(https{0,1}://.*?)/").Groups[1].Value);
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true"); //这个为 true 时,Access-Control-Allow-Origin 不能设置为 *
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Methods", "*"); //GET, HEAD, OPTIONS, POST, PUT
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Headers", "*"); //Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers
}
else
{
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Methods", "*");
context.HttpContext.Response.Headers.Add("Access-Control-Allow-Headers", "*");
}
}
}
}
然后,在 Startup 中的 ConfigureServices 方法中再次添加以下代码:
csharp
Copy
services.AddMvc(config =>
{
config.Filters.Add(new CorsActionFilter());
});
需要特别注意的是,当 Access-Control-Allow-Credentials 设置为 true 的时候,Access-Control-Allow-Origin 不能为 *。在此之前,我自己也并不清楚这个关键要点。Credentials 的主要作用在于,当使用 JavaScript 进行请求的时候能够带上 Cookie,这样服务端就可以准确识别请求的来源。例如在使用 axios 库时,可以设置 axios.defaults.withCredentials=true;。
貌似如果不进行第一步,只进行第二步,在使用 Get 方法的时候可能不会出现问题,但在进行 POST 请求时很可能会不成功。
在这个过程中,我还遇到过返回 401 状态码的情况,甚至连方法体都无法进入。
我发现在进行 POST 请求的时候,会先有一个 Options 请求,但这个请求却会报 404 错误。于是,我在方法上加上了 [HttpOptions] 特性,然而偶然间发现这样会导致方法被执行两次,并且实体参数无法获取到值。后来,我在 Controller 上加上 ApiController 特性,再去掉 HttpOptions,才最终成功解决了这个问题。需要强调的是,上面所说的内容可能并不全面,具体的情况可以一个一个进行尝试……
此外,我还遇到了一个问题,那就是我发现当在 Session 中存储了值后,下次却无法获取到这个值。经过在网上查询,发现需要去掉 Startup 中 ConfigureServices 方法的这段代码:
services.Configure
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
在此,我将这些问题和解决方法记录下来,以便日后遇到类似情况时可以快速参考和解决。