11

I have a .net core application running in Windows and Linux as well (I use .net core runtime >= 2.1). To get better insights I'd like to expose a metrics endpoint (simple HTTP GET endpoint) for Prometheus publishing some internal stats of my application.

Searching through the WWW and SO I always ended up on using asp.net core. Since I only want to add a quite simple HTTP GET endpoint to an existing .net core app it seems a little bit overkill, to port the whole application to asp.net.

The alternative I already considered was to write my own handler based on HttpListener. This is quite straight forward when it comes to a simple HTTP endpoint, but since all information I found regarding SSL and Linux was, this is not supported at the moment and I should go with asp.net. (https://github.com/dotnet/runtime/issues/33288#issuecomment-595812935)

So I'm wondering what I missunderstood! Am I the only one? Is there already a good library providing a simple http(s) server for .net core?

EDIT: [SOLVED] As @ADyson mentioned in the comments below the existing application does not need to be ported to asp.net core!

Project files generated with dotnet new web in version 2.1 automatically added references to "Microsoft.AspNetCore.App" and "Microsoft.AspNetCore.Razor.Design"

When I referenced my asp.net core project from a .net core project and executed the code hosting the web service I ended up with an System.IO.FileNotFoundException stating it "Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Core'".

Microsoft.AspNetCore.App is a metapackage also referencing said Microsoft.AspNetCore.MVC! Thus, the executing assembly also has to reference this metapackage. This observation missled me that using asp.net core renders my whole application to be built around Microsoft.AspNetCore.App.

After removing these references and adding only a reference to "Microsoft.AspNetCore" everything works as expected.

After checking the generated project files from dotnet new web in version 3.1 these references were not added. This is not a problem for folks using newer versions of dotnet!

pocketbroadcast
  • 169
  • 1
  • 1
  • 8
  • 3
    you don't need to port the whole app to asp.net. Have you tried using OWIN to self-host within another app? Ultimately though the bit you write which serves the web application will almost certainly be relying on asp.net libraries - that is .NET's way of providing HTTP functionality. You don't need to create a whole MVC setup or anything like that though. – ADyson Apr 02 '20 at 09:58
  • With latest .net core they added `WorkerServices` Have not worked with them yet, but they look like what you need. For example [Host ASP.NET Core in a Windows Service](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-3.1&tabs=visual-studio). I would suggest checking this [related answer](https://stackoverflow.com/questions/48522849/what-replaces-wcf-in-net-core) – Cleptus Apr 02 '20 at 10:08
  • @ADyson I had a look at OWIN. While the concepts and separation of conecerns sounds really promising in its design, I could not find a way to use it in .net core apps since it is only available for the full .net framework (https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Hosting/Microsoft.Owin.Hosting.csproj) – pocketbroadcast Apr 02 '20 at 11:39
  • 1
    @pocketbroadcast That's not true, [as far as I can tell](https://www.google.com/search?q=owin+.net+core) . Here's a [working example](https://github.com/jlucansky/Quartzmin/blob/master/Source/Quartzmin.SelfHost/QuartzminPlugin.cs) of self-hosting using .NET Core / .NET Standard. Remember that OWIN is just a standard, and that ASP.NET Core implements a lot of the OWIN standard natively without needing as many extra libraries. – ADyson Apr 02 '20 at 12:47
  • 1
    @ADyson yes you're completely right! It was my fault. I added the worng nuget package to my project, thus leading me to a asp.net repository not to the appropriate asp.net core repo. Should have better double checked. Thank you for pointing this out. – pocketbroadcast Apr 02 '20 at 14:49

3 Answers3

16

As mentioned by @ADyson, OWIN is the way to go. You can easily self-host a HTTP endpoint in your existing application. Here is a sample to self-host it in a .Net Core 3.1 console application. It exposes a simple endpoint listening on port 5000 for GET requests using a controller. All you need is to install the Microsoft.AspNetCore.Owin Nuget package.

The code files structure is as follows:

.
├── Program.cs
├── Startup.cs
├── Controllers
    ├── SayHi.cs

Program.cs

using Microsoft.AspNetCore.Hosting;

namespace WebApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseUrls("http://*:5000")
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

Startup.cs

using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace WebApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

SayHi.cs

using Microsoft.AspNetCore.Mvc;

namespace WebApp.Controllers
{
    public class SayHi : ControllerBase
    {
        [Route("sayhi/{name}")]
        public IActionResult Get(string name)
        {
            return Ok($"Hello {name}");
        }
    }
}

Then a simple dotnet WebApp.dll would start the app and web server. As you can see, the sample uses Kestrel. The default web server. You can check Microsoft's related documentation.

For more configuration and routing options you can check Microsoft's documentation.

Anouar
  • 2,050
  • 1
  • 15
  • 25
  • Thank you for posting this PoC for a web app hosted in a .net core console app and highlighting that there is no need for other NuGet packages than `Microsoft.AspNetCore.Owin`. Since installing this package failed in .net core 2.1, it made me check the generated project files in dotnet 2.1 and dotnet 3.1 which finally led to the solution stated in the EDIT section of my question. – pocketbroadcast Apr 02 '20 at 16:05
  • Happy to help and great that you got it all figured out :) – Anouar Apr 02 '20 at 16:44
  • This works great and you can do it in a WinForms app as well since that is now supported on .NET 5.0. Just make sure you execute the `host.Run()` call in the above code in a separate thread, then on the main thread call `Application.Run(new Form1())` to run your main form. You can't run both `Application.Run()` and `host.Run()` on the same thread since they are blocking calls. – Eric Mutta Dec 27 '20 at 02:29
  • @Anouar please include all the packages you added to the console app... – Seabizkit Mar 12 '21 at 06:47
  • its so annoying that i cant get the vs profile to be on use IIS express, which then does the `WebHostBuilder` in another thread, and use Kestrel, all ports are different, so annoying – Seabizkit Mar 12 '21 at 14:34
  • Easiest way to self host a HTTP endpoint with only Microsoft.AspNetCore.Owin, thanks for the help ! – SylvainB2347 Apr 09 '21 at 07:40
4

One option is to use EmbeddIo

https://unosquare.github.io/embedio/

I find the documentation is not always the best, especially as they recently upgrade and many samples etc. are not valid. But you can get there!

You can self host a REST API like this:

 WebServer ws = new WebServer(o => o
        .WithUrlPrefix(url)
        .WithMode(HttpListenerMode.EmbedIO))
        .WithWebApi("/api", m => m
        .WithController<ApiController>());

this.Cts = new CancellationTokenSource();
var task = Webserver.RunAsync(Cts.Token);

Then define your API Controller like this.

class ApiController : WebApiController
 {
        public ApiController() : base()
        {

        }

        [Route(HttpVerbs.Get, "/hello")]
        public async Task HelloWorld()
        {
            string ret;
            try
            {
                ret = "Hello from webserver @ " + DateTime.Now.ToLongTimeString();
            }
            catch (Exception ex)
            {
                //
            }
            await HttpContext.SendDataAsync(ret);

        }
}
jason.kaisersmith
  • 8,712
  • 3
  • 29
  • 51
  • Thank you for pointing me to NancyFx. The framework looks great! Especially the part with `.WithController` makes it quite easy to follow which controller handles which endpoint. I'll definitely have a look at it! – pocketbroadcast Apr 02 '20 at 15:56
2

Project files generated with dotnet new web in version 2.1 automatically added references to "Microsoft.AspNetCore.App" and "Microsoft.AspNetCore.Razor.Design" which, when referenced by a .net core project and executed ended up with an System.IO.FileNotFoundException stating it "Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Core'".

Creating a project with dotnet new web in version 3.1 does not reference these, thus the project can be referenced and executed from a .net core application.

-> Using asp.net core is a viable solution for me (again)!

pocketbroadcast
  • 169
  • 1
  • 1
  • 8