How to enables CORS for Service Fabric Stateless Web API?
See original GitHub issueHello,
I have been trying for hours now to properly configure CORS on Service Fabric Stateless Web API. But having no success.
This is the response I get (for the OPTIONS pre-flight) whenever I try to POST anything to the API endpoint.
XMLHttpRequest cannot load https://api-dev.myservice.com/tokens/verify. Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://tenant-dev.myservice.com:8000’ is therefore not allowed access. The response had HTTP status code 503.
Here is how my current setup looks like:
Program.cs
internal static class Program
{
private static void Main()
{
try
{
ServiceRuntime.RegisterServiceAsync("MyService_IdentityServiceType",
context => new Web(context)).GetAwaiter().GetResult();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof (Web).Name);
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
Web.cs
internal sealed class Web : StatelessService
{
public Web(StatelessServiceContext context)
: base(context)
{
}
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(serviceContext =>
new OwinCommunicationListener(
Startup.ConfigureApp, serviceContext, ServiceEventSource.Current,
Context.CodePackageActivationContext.GetConfigurationPackageObject("Config"),
"ServiceEndpoint", "identity"))
};
}
}
OwinCommunicationListener.cs
internal class OwinCommunicationListener : ICommunicationListener
{
private readonly string _appRoot;
private readonly ConfigurationPackage _configurationPackage;
private readonly string _endpointName;
private readonly ServiceEventSource _eventSource;
private readonly ServiceContext _serviceContext;
private readonly Action<IAppBuilder, ConfigurationPackage> _startup;
private string _listeningAddress;
private string _publishAddress;
private IDisposable _webApp;
public OwinCommunicationListener(Action<IAppBuilder, ConfigurationPackage> startup,
ServiceContext serviceContext,
ServiceEventSource eventSource, ConfigurationPackage configurationPackage, string endpointName)
: this(startup, serviceContext, eventSource, configurationPackage, endpointName, null)
{
}
public OwinCommunicationListener(Action<IAppBuilder, ConfigurationPackage> startup,
ServiceContext serviceContext, ServiceEventSource eventSource, ConfigurationPackage configurationPackage,
string endpointName, string appRoot)
{
if (startup == null)
{
throw new ArgumentNullException(nameof(startup));
}
if (serviceContext == null)
{
throw new ArgumentNullException(nameof(serviceContext));
}
if (endpointName == null)
{
throw new ArgumentNullException(nameof(endpointName));
}
if (eventSource == null)
{
throw new ArgumentNullException(nameof(eventSource));
}
if (configurationPackage == null)
{
throw new ArgumentNullException(nameof(configurationPackage));
}
_startup = startup;
_serviceContext = serviceContext;
_endpointName = endpointName;
_eventSource = eventSource;
_configurationPackage = configurationPackage;
_appRoot = appRoot;
}
public bool ListenOnSecondary { get; set; }
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
var serviceEndpoint = _serviceContext.CodePackageActivationContext.GetEndpoint(_endpointName);
var port = serviceEndpoint.Port;
if (_serviceContext is StatefulServiceContext)
{
var statefulServiceContext = _serviceContext as StatefulServiceContext;
_listeningAddress = string.Format(
CultureInfo.InvariantCulture,
"https://+:{0}/{1}{2}/{3}/{4}",
port,
string.IsNullOrWhiteSpace(_appRoot)
? string.Empty
: _appRoot.TrimEnd('/') + '/',
statefulServiceContext.PartitionId,
statefulServiceContext.ReplicaId,
Guid.NewGuid());
}
else if (_serviceContext is StatelessServiceContext)
{
_listeningAddress = string.Format(
CultureInfo.InvariantCulture,
"https://+:{0}/{1}",
port,
string.IsNullOrWhiteSpace(_appRoot)
? string.Empty
: _appRoot.TrimEnd('/') + '/');
}
else
{
throw new InvalidOperationException();
}
_publishAddress = _listeningAddress.Replace("+", FabricRuntime.GetNodeContext().IPAddressOrFQDN);
try
{
_eventSource.ServiceMessage(_serviceContext, "Starting web server on " + _listeningAddress);
_webApp = WebApp.Start(_listeningAddress,
appBuilder => _startup.Invoke(appBuilder, _configurationPackage));
_eventSource.ServiceMessage(_serviceContext, "Listening on " + _publishAddress);
return Task.FromResult(_publishAddress);
}
catch (Exception ex)
{
_eventSource.ServiceMessage(_serviceContext, "Web server failed to open. " + ex);
StopWebServer();
throw;
}
}
public Task CloseAsync(CancellationToken cancellationToken)
{
_eventSource.ServiceMessage(_serviceContext, "Closing web server");
StopWebServer();
return Task.FromResult(true);
}
public void Abort()
{
_eventSource.ServiceMessage(_serviceContext, "Aborting web server");
StopWebServer();
}
private void StopWebServer()
{
if (_webApp == null) return;
try
{
_webApp.Dispose();
}
catch (ObjectDisposedException)
{
// no-op
}
}
}
Startup.cs
public static class Startup
{
public static void ConfigureApp(IAppBuilder appBuilder, ConfigurationPackage configurationPackage)
{
appBuilder.UseCors(CorsOptions.AllowAll);
var configurationManager =
new ConfigurationManager(configurationPackage.Settings.Sections["IdentityService"].Parameters);
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
ConfigureFormatters(config.Formatters);
var container = new Container();
RegisterDependencies(container, config, configurationManager);
appBuilder.UseWebApi(config);
}
private static void ConfigureFormatters(MediaTypeFormatterCollection formatters)
{
foreach (var formatter in formatters.OfType<JsonMediaTypeFormatter>())
{
formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
formatter.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
formatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
break;
}
}
private static void RegisterDependencies(Container container, HttpConfiguration config,
ConfigurationManager configurationManager)
{
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
container.RegisterWebApiControllers(config);
container.RegisterSingleton<IConfigurationManager>(configurationManager);
container.Verify();
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
}
}
I must be missing something here. If someone can please point me into the right direction, it would be really great 😃
Issue Analytics
- State:
- Created 7 years ago
- Reactions:1
- Comments:12 (1 by maintainers)
Top GitHub Comments
@munjalpatel Can you please share with us what you changed to fix the issue? Another seeker in the same path!
@vturecek
I have the following evidence to suggest this is a Service Fabric issue:
So the CORS issue seems to be related to the use of the service fabric reverse proxy. Are there specific configuration settings for the SF reverse proxy that need to be set up for this to work?
Thanks Chris