1、前言
现在流行前后端分离的开发模式,在最终部署项目时,前端项目和后端项目可能会被部署在不同的域名下,这就会导致跨域问题。而由于同源策略的约束,在一般情况下,浏览器不会允许一个域名中的网页去访问另一个域名中的资源,因此我们需要在Web API
项目中进行跨域设置,下面开始介绍。
2、搭建前后端项目
2.1、搭建后台控制器
后台项目URL
为https://localhost:5001
,新建一个控制器HomeController
,添加一个Get
和一个Post
方法,代码如下,:
using Microsoft.AspNetCore.Mvc;
using System;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public string Get()
{
return "Hello World";
}
[HttpPost]
public string Post()
{
return $"当前服务器时间:{DateTime.Now}";
}
}
}
2.2、搭建前端页面
前端页面访问后台资源,最常用的就是GET
请求和POST
请求。在这里引入jQuery
,然后搭建一个简易的前端页面,URL
为http://127.0.0.1:5500/index.html
,代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试网页</title>
<script src="jquery/jquery.min.js"></script>
</head>
<body>
<button id="get">GET模式</button>
<button id="post">POST模式</button>
<script>
$('#get').click(function () {
$.ajax({
type: 'get',
url: 'https://localhost:5001/api/Home/Get',
cache: false,
success: function (res) {
window.alert(res);
},
error: function (err) {
window.alert(err.status);
}
});
});
$('#post').click(function () {
$.ajax({
type: 'post',
url: 'https://localhost:5001/api/Home/Post',
cache: false,
success: function (res) {
window.alert(res);
},
error: function (err) {
window.alert(err.status);
}
});
});
</script>
</body>
</html>
如果直接点击按钮,会发现弹出提示框显示status
为0
,如下图所示。状态值为0
就表示产生了跨域错误。
3、添加跨域策略
下面就来给后台的Web API
添加一下跨域策略,还是老套路,我们需要在ConfigureServices
方法中添加一下跨域服务,代码如下:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace App
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 添加跨域
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
由于跨域访问涉及到http
请求的相关操作,因此在Configure
方法中需要添加一行app.UseCors()
。现在再运行一下前端项目,发现能够正常访问后台资源,如下图所示:
如果希望只有指定的前端项目能够访问,则可以做一系列的约束操作,代码如下:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace App
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 添加跨域
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder =>
{
builder.WithOrigins("http://127.0.0.1:5500")
.WithMethods("GET", "POST")
.AllowAnyHeader()
.AllowCredentials();
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
在上面的代码中,规定了只有http://127.0.0.1:5500
可以访问后台资源,同时也规定了后台只接受GET
和POST
请求模式。
4、创建自定义中间件实现跨域
上面介绍的是最常用的方法,其实我们也可以通过自定义的中间件来实现跨域设置。前面的博客已经说过:Configure
方法是用来配置http
请求管道的。这就给我们提供了一个思路:通过自定义中间件实现跨域。首先创建一个CorsMiddleware.cs
,代码如下:
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace App
{
public class CorsMiddleware
{
private readonly RequestDelegate next;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="next">下一个处理者</param>
public CorsMiddleware(RequestDelegate next)
{
this.next = next;
}
/// <summary>
/// 实现Invoke方法
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
context.Response.Headers.Add("Access-Control-Allow-Headers", "*");
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
if (context.Request.Method.ToUpper() != "OPTIONS")
{
await next(context);
}
}
}
}
自定义中间件都包含一个RequestDelegate
类型成员,同时需要实现一个异步的Invoke
方法,如果http
请求验证失败,则跳到下一个中间件。最后在Configure
方法中启用自定义中间件即可,代码如下:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace App
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseMiddleware<CorsMiddleware>(); // 启用自定义中间件
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
运行一下前端项目,发现能够正确访问后台资源,如下图所示:
5、结语
本文主要介绍了ASP.NET Core
中跨域设置的相关操作。在实际开发过程中,我们可以对跨域做出更严格的控制,如对http
的请求头header
做出相应限制,具体就是利用WithHeaders
方法进行限制。由于跨域涉及到安全性问题,大家还是需要根据实际情况进行综合考量。