ASP.NET WebApi - 为多个来源启用 CORS

Posted

技术标签:

【中文标题】ASP.NET WebApi - 为多个来源启用 CORS【英文标题】:ASP.NET WebApi - Enable CORS for multiple Origins 【发布时间】:2017-02-22 23:58:21 【问题描述】:

我一直在尝试为我的 API 启用多个来源的 CORS,但没有成功。

这是我所做的。

创建了 CORS 策略

 [AttributeUsage(AttributeTargets.All, AllowMultiple =false, Inherited =true)]
    public class TCCorsPolicyProvider : Attribute, ICorsPolicyProvider
    
        private CorsPolicy _policy;

        public TCCorsPolicyProvider()
        
            _policy = new CorsPolicy
            
                SupportsCredentials = true
            ;
            string[] allowedOrigins = "john.doe,ava.wise".Split(',');
            string[] allowedMethods = "GET,POST,PUT,OPTIONS".Split(',');
            string[] allowedHeaders = "Content-Type,Origin,Authorization,Accept".Split(',');
            // Add allowed origins.
            foreach (string origin in allowedOrigins)
                _policy.Origins.Add(origin);
            foreach (string method in allowedMethods)
                _policy.Methods.Add(method);
            foreach (string header in allowedHeaders)
                _policy.Headers.Add(header);
        

        public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        
            return Task.FromResult(_policy);
        
    

创建工厂

public class TCCorsPolicyProviderFactory : ICorsPolicyProviderFactory
    
        ICorsPolicyProvider _provider;
        public TCCorsPolicyProviderFactory()
        
            _provider = new TCCorsPolicyProvider();
        
        public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
        
            return _provider;
        
    

在 WebApiConfig.cs 类中启用 Cors

config.SetCorsPolicyProviderFactory(new TCCorsPolicyProviderFactory());
            config.EnableCors();

确保在 Global.asax Application_Start 中进行了适当的注册

 GlobalConfiguration.Configure(WebApiConfig.Register);

当上述方法不起作用时,我什至手动将策略属性应用于所有其他控制器继承的基本控制器

[TCCorsPolicyProvider]
    public class BaseApiController : ApiController
    
        public string IpAddress
        
            get  return ContextHelper.GetIpAddress(); 
        

        private bool _disposed;
        protected virtual void Dispose(bool disposing, Action disposeAction)
        
            if (!_disposed)
            
                if (disposing)
                
                    disposeAction();
                
            
            _disposed = true;
        
    

但我收到以下错误(Invoking Api from Angular)

XMLHttpRequest 无法加载 hqidwtcdwa01/api/localizations/reloadCache。 请求中不存在“Access-Control-Allow-Origin”标头 资源。因此,Origin 'ava.wise' 不允许访问。这 响应的 HTTP 状态代码为 401。

hqidwtcdwa01 是目的地,ava.wise 是起点。

到目前为止,我发现 xmlhttp 响应中的 http 响应标头不包含 Access-Control-Allow-Origin。但是,当我使用 HttpCient 时,我可以看到标题。

【问题讨论】:

【参考方案1】:

我相信 cors 理论上允许多个来源,但实际上不允许。对于我的站点,我创建了一个新的 CORS 属性并根据传入返回一个允许的来源,这比使用 *

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
            AllowMultiple = false)]
public class GlobalEnableCorsAttribute :


 Attribute, ICorsPolicyProvider
    
        public Boolean SupportsCredentials = true;
        public CorsHandler handle = new CorsHandler();
        public async Task<CorsPolicy> GetCorsPolicyAsync(
          HttpRequestMessage request, CancellationToken cancellationToken)
        
             var corsRequestContext = request.GetCorsRequestContext();
            var originRequested = corsRequestContext.Origin;


            string approvedOrigin = handle.approveCorsOrigin(originRequested);

            if(!string.IsNullOrEmpty(approvedOrigin))
            
                // Grant CORS request
                var policy = new CorsPolicy
                
                    AllowAnyHeader = true,
                    AllowAnyMethod = true,
                    SupportsCredentials = true
                ;

                // add headers
                policy.Headers.Add("content-type");
                policy.Headers.Add("withcredentials");
                policy.Headers.Add("Access-Control-Allow-Headers");
                policy.Headers.Add("Access-Control-Allow-Origin");
                policy.Headers.Add("Origin");
                policy.Headers.Add("Accept");
                policy.Headers.Add("X-Requested-With");
                policy.Headers.Add("Access-Control-Request-Method");
                policy.Headers.Add("Access - Control - Request - Headers");


                policy.Origins.Add(approvedOrigin);
                return policy;
            
            else
            
                // Reject CORS request
                return null;
            
        



    

来源搜索从服务器配置值中获取允许的来源

 public class CorsHandler

    public string approveCorsOrigin(string providedOrigin)
    
        // load list of web.config origins
        string fullList = Properties.Settings.Default.CORSOriginPermittedSite;

        if (!string.IsNullOrEmpty(fullList))
        
            string[] originArray = fullList.Split(new char[] ',', StringSplitOptions.RemoveEmptyEntries);

            foreach(string approvedOrigin in originArray)
            
                if (providedOrigin.Trim() == approvedOrigin.Trim())
                
                    return providedOrigin;
                
            
        
        return null;
    

用法如下

 public static void Register(HttpConfiguration config)
    
        if (Properties.Settings.Default.CORSOriginPermittedSite != null && !string.IsNullOrWhiteSpace(Properties.Settings.Default.CORSOriginPermittedSite))
        
            var cors = new GlobalEnableCorsAttribute();
            config.EnableCors(cors);
        
    

最后,这段代码是针对 webapi 设置的,但应该具有可比性,而且您(如果使用类似的话)还需要在飞行前处理同源搜索。..

【讨论】:

【参考方案2】:

我在使用 owincontext 构建 mvc api 时遇到了非常相似的问题,我必须添加

#if DEBUG //Notice this is only when I'm in debug mode.
 context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[]  "*" );
#endif

但是,当我部署特定于所需来源的添加标头时。 而且由于您的错误相似,它可能有助于添加 Access-Control-Allow-Origin : ava.wise 标头并检查它是否解决。希望它有助于缩小您的问题。

您希望在标题中包含的示例,注意 Options 方法将接受的来源获取到任何“*”。

---更新---

请尝试更新 web.config 添加标题。 Custom Headers 这应该反映在响应中。显然,这在代码中是可能的,但从简单开始。

 <configuration>   <system.webServer>
  <httpProtocol>
     <customHeaders>
        <remove name="Access-Control-Allow-Origin" />
        <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
  </httpProtocol>   
 </system.webServer>
</configuration>

【讨论】:

响应中显然没有添加标头。可能是什么原因造成的? 所以我认为标题的所有其他部分都按照您在策略中的设置正确设置。正确的?我已经更新了答案,以显示您可以在哪里尝试添加提到的标题。您是否能够显示 OPTIONS 方法返回的内容、飞行前请求、屏幕截图? [启用 Cors] (asp.net/web-api/overview/security/…) 如果您还没有尝试过。 配置有效,事实上就是这样。但是我需要切换到代码,因为我不需要将多个来源添加到允许列表中。 Hmmmm...尝试完全注释标题添加部分并添加_policy.AllowAnyHeader = true;只是为了确保您添加的标题与您添加的来源不矛盾.. 好吧,所以我使用 HttpClient 向我的 API 发出请求(在通过代码启用 CORS 之后)。我看到响应具有适当的标头。奇怪的是浏览器无法看到标题。有什么想法吗?【参考方案3】:

我以一种肮脏而艰难的方式做到了,也许不是那么建议,但这让我继续前进(假设你安装了 cors nuget、web api 2.0 和 .NET 4.5):

web.config:

<appSettings>
    <add key="AllowedDomains" value="http://domain0:8009, http://domain1:8009"/>
 </appSettings>

Configs.cs:

public static string AllowedDomains 
        get 
            return @ConfigurationManager.AppSettings["AllowedDomains"].ToString();
        
    

WebApiConfig:

    public static void Register(HttpConfiguration config)
    
        var attribute = new System.Web.Http.Cors.EnableCorsAttribute(Configs.AllowedDomains, "*", "*"); //domains, headers, methods - you could do the same for the other args.
        config.EnableCors(attribute); //global
    

要按控制器/动作过滤,只需在顶部添加属性:

[System.Web.Http.Cors.EnableCors(origins: "http://domain2:8009", headers: "*", methods: "*")]

让我知道任何进展或 cmets。希望这可以帮助。谢谢! :)

【讨论】:

以上是关于ASP.NET WebApi - 为多个来源启用 CORS的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Asp.Net Core 3.0 WebAPI 中启用 CORS

为 ASP .NET MVC 中的静态资源启用 CORS?

在 ASP.Net Core 5 WebAPI 中启用 CORS

使用具有多个来源的 PUT 和 DELETE 请求时如何解决 ASP.NET Web API CORS Preflight 问题?

如何在 ASP.net Core WebAPI 中启用 CORS

如何在 WEB API 的 ASP.NET MVC 控制器级别中启用 CORS? [复制]