Windows Service hosting Web API 2 via Owin Starts and Immediately Stops with no Exceptions Thrown

3

The title sums up my issue. I have try-catch blocks around the service's OnStart method and the Main method in the service, and no exception is ever thrown. The service starts and immediately stops, with the following message:

The Foo service on Local Computer started and then stopped. Some services stop automatically if they are not in use by other services or programs.

The same behavior can be observed on the test server, so it is not machine-dependent. I've verified that the configuration file is present and contains the appropriate settings.

I cannot see any obvious reason why this would be occurring. Can someone please take a look at this and shed some light on it? I've consulted other, similar questions and none of them shed any light on this particular service.

Startup.cs:

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

WebApiService.cs:

public partial class WebApiService : ServiceBase
{
    private readonly IConfig config;
    private IDisposable webApp;

    public WebApiService()
    {
        try
        {
            InitializeComponent();
            this.config = UnityBootstrapper.Initialize().Resolve<IConfig>();
        }
        catch (Exception e)
        {
            new ApplicationEventLog(new Config()).WriteError(e);
        }
    }

    protected override void OnStart(string[] args)
    {
        try
        {
            var baseUrl = $"http://*:{this.config.WebApiHttpPort}";

            this.webApp = WebApp.Start<Startup>(baseUrl);
        }
        catch (Exception e)
        {
            new ApplicationEventLog(this.config).WriteError(e);
        }
    }

    protected override void OnStop()
    {
        this.webApp?.Dispose();
        this.webApp = null;
    }

    protected override void OnShutdown()
    {
        this.webApp?.Dispose();
        this.webApp = null;
    }
}

WebApiService.Designer.cs

partial class WebApiService
{
    /// <summary> 
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Component Designer generated code

    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        components = new System.ComponentModel.Container();
        this.ServiceName = "Foo";
    }

    #endregion
}

Program.cs:

public static class Program
{
    public static bool IsConsoleApp = false;
    public static bool LogRequests = false;

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    public static void Main(params string[] args)
    {
        try
        {
            if (args.Length > 0 && args.Any(a => a.ToLower().In("l", "-l", "/l")))
            {
                LogRequests = true;
            }

            if (args.Length > 0
                && args[0].ToLower().In("c", "-c", "/c"))
            {
                RunAsApp();
            }
            else
            {
                RunAsService();
            }
        }
        catch (Exception e)
        {
            new ApplicationEventLog(new Config()).WriteError(e);
        }
    }

    private static void RunAsService()
    {
        Program.IsConsoleApp = false;

        var servicesToRun = new ServiceBase[]
                                      {
                                          new WebApiService()
                                      };
        ServiceBase.Run(servicesToRun);
    }

    private static void RunAsApp()
    {
        Program.IsConsoleApp = true;

        var config = UnityBootstrapper.Initialize().Resolve<IConfig>();
        var url = $"http://*:{config.WebApiHttpPort}";

        Console.WriteLine($@"Running. Hosted at: {url}");
        Console.WriteLine(@"Press ENTER to exit.");

        using (WebApp.Start<Startup>(url))
        {
            while (Console.ReadKey().Key != ConsoleKey.Enter)
            {
                Thread.Sleep(0);
            }
        }

        Program.IsConsoleApp = false;
    }
}

LogController.cs:

[RoutePrefix("api/log")]
public class LogController : ApiController
{
    private readonly ILogger logger;
    private readonly IContextFactory contextFactory;
    private readonly IConfig config;

    public LogController() : base()
    {
        var container = UnityBootstrapper.Initialize();

        this.logger = container.Resolve<ILogger>();
        this.config = container.Resolve<IConfig>();
        this.contextFactory = container.Resolve<IContextFactory>();
    }

    [HttpGet]
    [Route("Status")]
    public string Status()
    {
    }

    [HttpPost]
    [Route("Message")]
    public string Write([FromBody] LogMessage logMessage)
    {
        // Elided for ... reasons
    }

    [HttpPost]
    [Route("Performance")]
    public string Write([FromBody] PerformanceMessage performanceMessage)
    {
        // Elided for ... reasons
    }
}

Edit: Added WebApiService.Designer.cs, as the service shuts down when it returns from InitializeComponent.

Edit 2: I've traced it to WebApp.Start<Startup>(baseUrl) in WebApiService.cs. A TargetInvocationException is thrown, with an HttpListenerException as the inner exception. The message on the HttpListenerException is "Access is denied."

System.Reflection.TargetInvocationException occurred
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Owin.Hosting.ServerFactory.ServerFactoryAdapter.Create(IAppBuilder builder)
   at Microsoft.Owin.Hosting.Engine.HostingEngine.StartServer(StartContext context)
   at Microsoft.Owin.Hosting.Engine.HostingEngine.Start(StartContext context)
   at Microsoft.Owin.Hosting.Starter.DirectHostingStarter.Start(StartOptions options)
   at Microsoft.Owin.Hosting.Starter.HostingStarter.Start(StartOptions options)
   at Microsoft.Owin.Hosting.WebApp.StartImplementation(IServiceProvider services, StartOptions options)
   at Microsoft.Owin.Hosting.WebApp.Start(StartOptions options)
   at Microsoft.Owin.Hosting.WebApp.Start[TStartup](StartOptions options)
   at Microsoft.Owin.Hosting.WebApp.Start[TStartup](String url)
   at Foo.Logging.WebApi.WebApiService.OnStart(String[] args) in C:\Dev\Foo\Foo.WebApi\WebApiService.cs:line 45

Inner Exception 1:
HttpListenerException: Access is denied

I've researched possible causes for this, and it could be the URL or an ACL. I changed the URL to http://+:{this.config.WebApiHttpPort}, to no avail. I then created an ACL as follows:

netsh http add urlacl http://+:8089/ user=Everyone

The error still occurs.

c#
asp.net-web-api2
owin
asked on Stack Overflow Oct 12, 2017 by Mike Hofer • edited Oct 12, 2017 by Mike Hofer

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0