10

I'm trying to enable CORS for certain domains within my .Net Web API application, and am able to do this on Application Start via this code..

public static void Register(HttpConfiguration config)
{
    //below comma separated string is built from database
    var domains = "http://www.myfirstdomain.com,http://www.myseconddomain.co.uk .... about 130 domains more..";
    config.EnableCors(new EnableCorsAttribute(domains, "*", "*"));

However if new domains are added while the application is live, these will not be allowed to post cross domain until the app pool is recycled and this list is built up again.

Is there any way I can update this list during the lifetime of my application? I know I can periodically recycle the app pool but this will cause delays in some requests that I could ideally do without.

I know that I can enable this on the controller method, ie..

[EnableCors("http://domain1.com,http://domain2.com", "*", "*")]
public HttpResponseMessage PostAsync([FromBody] MyRequest myRequest)
{

However again the comma separated parameter has to be declared as a constant, and therefore cannot be dynamic.

Am I missing something blatantly obvious or can anyone think of a decent way of doing this?

EDIT

Here is my attempt at writing my own custom EnableCors attribute..

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class EnableCorsByDomainAttribute : Attribute, ICorsPolicyProvider
{
    private readonly CorsPolicy _policy;

    public EnableCorsByDomainAttribute()
    {
        _policy = new CorsPolicy
        {
            AllowAnyMethod = true,
            AllowAnyHeader = true
        };

        var originsString = "http://www.test1.com,http://www.test2.com";
        if (!String.IsNullOrEmpty(originsString))
        {
            foreach (var origin in originsString.Split(','))
            {
                _policy.Origins.Add(origin);
            }
        }
    }

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

I've then decorated the controller method with

[EnableCorsByDomain]
Kiran
  • 56,921
  • 15
  • 176
  • 161
andrewm
  • 2,552
  • 5
  • 33
  • 63
  • Related: https://stackoverflow.com/questions/49568640/dynamically-updating-cors-registry and https://stackoverflow.com/questions/75103900/how-do-you-update-cors-origins-while-nodejs-server-is-running/75106542 – jub0bs Jan 13 '23 at 12:21

1 Answers1

8

Yes, Web API CORS provides an extensibility point for this kind of scenario. You can take a look at the section called 'Implementing a custom ICorsPolicyProvider' in the following Web API functional spec document for more details.

namespace System.Web.Http.Cors
{
    public interface ICorsPolicyProvider
    {
        Task GetCorsPolicyAsync(HttpRequestMessage request);
    }
}

Note that the ICorsPolicyProvider is async so that we don’t block the thread on I/O.

Sample Here is a custom implementation of ICorsPolicyProvider that loads the origins from web.config.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class EnableCorsAppSettingsAttribute : Attribute, ICorsPolicyProvider
{
    private CorsPolicy _policy;

    public EnableCorsAppSettingsAttribute(string appSettingOriginKey)
    {
        _policy = new CorsPolicy
        {
            AllowAnyMethod = true,
            AllowAnyHeader = true
        };

        // loads the origins from AppSettings
        string originsString = ConfigurationManager.AppSettings[appSettingOriginKey];
        if (!String.IsNullOrEmpty(originsString))
        {
            foreach (var origin in originsString.Split(','))
            {
                _policy.Origins.Add(origin);
            }
        }
    }

    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request)
    {
        return Task.FromResult(_policy);
    }
}

You can apply it on the controller/action just like EnableCorsAttribute.

[EnableCorsAppSettings("internal:origins")]
public class ValuesController : ApiController
{
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    public string Get(int id)
    {
        return "value " + id;
    }
}

And it will read the “internal:origins” appSetting from the web.config.

<appSettings>
  <add key="webpages:Version" value="2.0.0.0" />
  <add key="webpages:Enabled" value="false" />
  <add key="PreserveLoginUrl" value="true" />
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  <add key="internal:origins" value="http://example.com,http://webapisample.azurewebsites.net" />
</appSettings>
Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Kiran
  • 56,921
  • 15
  • 176
  • 161
  • Thanks, although, this documentation needs updated, the latest libraries take different parameters. Regardless, I still can't seem to get this working, I need a way to dynamically load a list of domains from my database when a controller method is hit. – andrewm Nov 13 '13 at 17:33
  • 2
    Did you make changes to this line `config.EnableCors(defaultPolicyProvider: new EnableCorsAttribute(domains, "*", "*"));` ?...if you notice this line is setting the defaultPolicyProvider which is probably overriding your custom policy provider.. – Kiran Nov 13 '13 at 18:55
  • Yeh got this working thanks, the code in my edit actually works, it turns out chrome was caching the invalid call to OPTIONS – andrewm Nov 14 '13 at 10:25
  • What I am looking to do is instead of hardcoding the allow origin in the API I want to save the Url in a Database and use it during runtime. The link is pretty self explanatory but didn't really go into that much detail. – Si8 Feb 14 '17 at 15:07
  • 1
    The link is now dead. – jub0bs Jan 20 '23 at 14:25