0

Application

I’m working on an IIoT project. I need to be able to receive simple messages (json payload) on a piece of equipment whose HMI runs on Win7. We use 3rd party MQTT for this in some cases but for this situation I’m looking at HTTP, specifically a tiny http server in C# (.NET Framework 4.0). There doesn’t need to be a front end, just an api with a single endpoint.

Approach

I’ve found System.Net.HttpListener and got it doing what I want in a console app for initial testing. Problem is I’m using the GetContext method which blocks the main thread. Intuitively, this is not what I want, but I don’t know a more reasonable approach. I see there is a built in async method, HttpListener.BeginGetContext, but is that enough to do what I want? Ultimately looking to wrap this up into a service using the TopShelf NuGet package.

Not looking for a code-complete solution, but the proper conceptual approach.

scottmwyant
  • 305
  • 1
  • 2
  • 9

1 Answers1

0

you may use the Microsoft.Owin package to host http controllers in the console application

The idea is that you do not need the IIS to be installed in such case. And you may host the application as windows service (using the TopShelf) if required.

However you are getting the ASP controllers working and should not bother with HttpListeners any more.

There are a lot of how-to guides available, like here

In few words you have to

add Microsoft.AspNet.WebApi.OwinSelfHost package

Configure the Owin in the code like

public class SelfHostStart 
{
    private readonly WebSettings config;

    public SelfHostStart(WebSettings config)
    {
        this.config = config;
    }

    void Start<T>(string hostAddress, string message)
    {
        if (string.IsNullOrEmpty(hostAddress))
            throw new ArgumentException("hostAddress", $"no value to start host {message}");

        var oh = new OwinHost();
        server = oh.Start<T>(hostAddress, message);
    }


    public void Start()
    {
        if (config?.Enabled == true)
        {
            Start<Startup>(config.HostAddress, "webServer");
        }
    }
}              

    public class WebSettings 
    {
        public bool Enabled { get; set; }

        public string HostAddress { get; set; }
    }

then you may to create Startup.cs to configure http hosting like below:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        HttpConfiguration config = GetHttpConfig(appBuilder);

        ConfigureWebApi(appBuilder, config);
DEBUG
        config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
if
        appBuilder.UseCors(CorsOptions.AllowAll);
        appBuilder.UseWebApi(config);
    }

    public HttpConfiguration GetHttpConfig(IAppBuilder appBuilder)
    {
        var config = new HttpConfiguration();
// you may override some default behavior here
        //config.Services.Replace(typeof(IHttpControllerActivator), new UnityControllerActivator());
        //config.Services.Add(typeof(System.Web.Http.ExceptionHandling.IExceptionLogger), new ExceptionLog(appBuilder.CreateLogger<ExceptionLog>()));
        //config.Services.Replace(typeof(System.Web.Http.ExceptionHandling.IExceptionHandler), new CustomExceptionHandler()); // to make ExceptionHandler works 

        //config.MessageHandlers.Add(new LoggingHandler(appBuilder.CreateLogger("rest")));

        return config;
    }

    public void ConfigureWebApi(IAppBuilder app, HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

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

        config.EnsureInitialized();
    }
}

and controller (class with methods that are available for clients of a service):

[AllowAnonymous]
public class StatusController : ApiController
{
    public StatusController()
    {
    }

    [Route("status")]
    public StatusDto Get()
    {
        var res = new StatusDto
        {
            DbVersion = "123",
            LocalCurrency = "USD",
            SwiftCode = "12345", // just sample
        };

        return res;
    }
}

public class StatusDto
{
    public string DbVersion { get; set; }
    public string LocalCurrency { get; set; }
    public string SwiftCode { get; set; }
}

and start the http server in your console

var config = new WebSettings() { HostAddress = "http://localhost:8080" };
var start = new SelfHostStart(config);
start.Start();

You should provide permissions for your console to listen a http url using the netsh http add urlacl command like

netsh http add urlacl url=http://+:8080/ user=everyone

Then you can browse the http://localhost:8080/status and should get report from the http console server.

You may extend you service by creating new controllers with methods

oleksa
  • 3,688
  • 1
  • 29
  • 54