1、前言

现在流行前后端分离的开发模式,在最终部署项目时,前端项目和后端项目可能会被部署在不同的域名下,这就会导致跨域问题。而由于同源策略的约束,在一般情况下,浏览器不会允许一个域名中的网页去访问另一个域名中的资源,因此我们需要在Web API项目中进行跨域设置,下面开始介绍。

2、搭建前后端项目

2.1、搭建后台控制器

后台项目URLhttps://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,然后搭建一个简易的前端页面,URLhttp://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>

如果直接点击按钮,会发现弹出提示框显示status0,如下图所示。状态值为0就表示产生了跨域错误。

ASP.NET Core 3.1系列(5)——Web API的跨域设置_跨域

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()。现在再运行一下前端项目,发现能够正常访问后台资源,如下图所示:

ASP.NET Core 3.1系列(5)——Web API的跨域设置_Startup_02


如果希望只有指定的前端项目能够访问,则可以做一系列的约束操作,代码如下:

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可以访问后台资源,同时也规定了后台只接受GETPOST请求模式。

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();
            });
        }
    }
}

运行一下前端项目,发现能够正确访问后台资源,如下图所示:

ASP.NET Core 3.1系列(5)——Web API的跨域设置_Startup_03

5、结语

本文主要介绍了ASP.NET Core中跨域设置的相关操作。在实际开发过程中,我们可以对跨域做出更严格的控制,如对http的请求头header做出相应限制,具体就是利用WithHeaders方法进行限制。由于跨域涉及到安全性问题,大家还是需要根据实际情况进行综合考量。