0

According to this question I cannot immplement IHttpModule when self hsoting a web api on a windows service

Is there a way to add an httpModule when webApi is running with the HttpSelfHostServer?

However, I still need to add this code somewhere in my self hosted web api. I found this blog about how to fix that: http://www.silver-it.com/node/182

The code is as follows, however I can not have an IhttpModule implemented on aself hosted API

public class CORSPreflightModule : IHttpModule
    {
        private const string OPTIONSMETHOD = "OPTIONS";
        private const string ORIGINHEADER = "ORIGIN";
        private const string ALLOWEDORIGIN = "https://yourspodomain.sharepoint.com";
        void IHttpModule.Dispose()
        {

        }
        void IHttpModule.Init(HttpApplication context)
        {                 
            context.PreSendRequestHeaders += (sender, e) =>
            {
                var response = context.Response;

                if (context.Request.Headers[ORIGINHEADER] == ALLOWEDORIGIN)
                {
                    response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");                    
                    response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");                                        
                }
                if (context.Request.HttpMethod.ToUpperInvariant() == OPTIONSMETHOD && context.Request.Headers[ORIGINHEADER] == ALLOWEDORIGIN)
                {
                    response.Headers.Clear();
                    response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
                    response.Headers.Add("Access-Control-Allow-Origin", "https://yourspodomain.sharepoint.com");
                    response.Headers.Add("Access-Control-Allow-Credentials", "true");
                    response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
                    response.Clear();
                    response.StatusCode = (int)HttpStatusCode.OK;
                }                                                             
            };

        }        

    }

My self hosted web api is like this:

Program.cs

static void Main()
{
    try
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new APIServiceTest()
        };
        ServiceBase.Run(ServicesToRun);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}


class Startup
    {
        //  Hack from https://stackoverflow.com/a/17227764/19020 to load controllers in 
        //  another assembly.  Another way to do this is to create a custom assembly resolver
        //Type valuesControllerType = typeof(OWINTest.API.ValuesController);

        // This code configures Web API. The Startup class is specified as a type
        // parameter in the WebApp.Start method.
        public void Configuration(IAppBuilder appBuilder)
        {
            try
            {
                //Debugger.Launch();
                // Configure Web API for self-host. 
                HttpConfiguration config = new HttpConfiguration();

                config.MessageHandlers.Add(new CustomHeaderHandler());
                var corsAttr = new EnableCorsAttribute(System.Configuration.ConfigurationManager.AppSettings["DominioSharePoint"].ToString(), "*", "*");
                config.EnableCors(corsAttr);

                //  Enable attribute based routing
                //  http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
                config.MapHttpAttributeRoutes();

                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );

                appBuilder.UseWebApi(config);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
    }

My controller:

 [EnableCors(origins: "https://xx.sharepoint.com", headers: "*", methods: "*")]
    public class CuentasCobroController : ApiController
    {

However because its self hosted I cant implement an IHttpModule there as explained above, but I can create a custom handler how can I implemente the code above from the blog in the custom handler?

public class CustomHeaderHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            return base.SendAsync(request, cancellationToken)
                .ContinueWith((task) =>
                {
                    HttpResponseMessage response = task.Result;
                    response.Headers.Add("Access-Control-Allow-Origin", "*");
                    return response;
                });
        }
    }

Question is, how can I integrate first code, into the startup of my windows service?

Community
  • 1
  • 1
Luis Valencia
  • 32,619
  • 93
  • 286
  • 506

2 Answers2

3

You are almost there by using DelegatingHandler instead of IHttpModule.

config.MessageHandlers.Add(new CorsHeaderHandler());

DelegatingHandler.SendAsync can access both request and response.

public class CorsHeaderHandler : DelegatingHandler
{
    private const string OPTIONSMETHOD = "OPTIONS";
    private const string ORIGINHEADER = "ORIGIN";
    private const string ALLOWEDORIGIN = "https://yourspodomain.sharepoint.com";
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            var allowedOrigin = request.Headers.Any(t => t.Key == ORIGINHEADER && t.Value.Contains(ALLOWEDORIGIN));
            if (allowedOrigin == false) return task.Result;

            if (request.Method == HttpMethod.Options)
            {
                var emptyResponse = new HttpResponseMessage(HttpStatusCode.OK);
                emptyResponse.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
                emptyResponse.Headers.Add("Access-Control-Allow-Origin", ALLOWEDORIGIN);
                emptyResponse.Headers.Add("Access-Control-Allow-Credentials", "true");
                emptyResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
                return emptyResponse;
            }
            else
            {
                task.Result.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
                task.Result.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
                return task.Result;
            }
        });
    }
}
Tommy
  • 3,044
  • 1
  • 14
  • 13
1

Simply put, you can't use IHttpModule with self-hosted Web API, or anything that is not IIS. IHttpModule is an IIS concept only.

What you can do instead, as you have discovered, is you can modify the Web API pipeline and insert your code (or the Web API equivalent) there instead. This could be done with a DelegatingHandler, or an action filter.

The simplest solution, however, would be to simply use the Microsoft.AspNet.WebApi.Cors NuGet package. With an HttpConfiguration object, call .EnableCors(...) and pass in an EnableCorsAttribute object as per the instructions here from Microsoft.

This is what you've already done in your code above, but you seem to be trying to also add in the custom CORS code from your HTTP module. If you remove the EnableCors attribute from your controller, and remove your CustomHeaderHandler, it should work as you would expect.

yaakov
  • 5,552
  • 35
  • 48