WebAPI self-hosting: How to communicate with host? - c#

I'm self-hosting WebAPI on a Windows service with the goal of being able to communicate with the Windows service.
However, although I'm able to connect to my web service with no difficulty at a basic level, I'm very unclear on the relationship between the web service and it's host. If my ultimate goal is to expose information from the Windows service through the Web service, how can I share information and communicate between them? What does the web service code have access to that's in the windows service code?
EDIT: Hmmm, too broad... how to narrow this down?
Here is my code. What I want to do is to call "api/strings/0" and have it return "One". However, the List indexOfStrings lives in the Windows Service host. How does the web service controller access the information?
public class StringsController : ApiController
{
/// <summary>
/// Get one entry from indexOfStrings
/// </summary>
public string Get(int listIndex)
{
// How to return indexOfStrings[listIndex]?
return "";
}
}
public class TestWindowsService : ServiceBase
{
public const string ServiceAddress = "Address and port number";
private HttpSelfHostServer _server; // This is the server you're hosting WebAPI on
private HttpSelfHostConfiguration _config; // WebAPI config
private List<string> indexOfStrings = new List<string>();
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
TestWindowsService service = new TestWindowsService() { AutoLog = false };
ServiceBase.Run(service);
}
public TestWindowsService()
{
indexOfStrings.Add("One");
indexOfStrings.Add("Two");
indexOfStrings.Add("Three");
//Create a host configuration
_config = new HttpSelfHostConfiguration(ServiceAddress);
//Setup the routes
_config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
// Create the server
_server = new HttpSelfHostServer(_config);
}
protected override void OnStart(string[] args)
{
_server.OpenAsync();
}
protected override void OnStop()
{
_server.CloseAsync().Wait();
_server.Dispose();
}
}

You can do this by plugging in a Dependency Injection container framework like Unity, Ninject, Autofac, Structure Map, etc and registering a service in the container and then injecting that service into your controller. There are Nuget packages for most of these DI frameworks to support Web API.
There is an example using Autofac here
However, using the same basic principle you can expose a service level object to controllers methods without using a DI Framework.
The idea is that you use a MessageHandler to insert your object instances to the Request Properties collection and then in the controller method pull it back out.
public class MyServiceHandler : DelegatingHandler
{
private readonly MyService _service;
public MyServiceHandler(MyService service)
{
_service = service;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Properties["MyService_Key"] = _service;
return base.SendAsync(request, cancellationToken);
}
}
In your configuration code you can add your message handler to the pipeline using,
config.MessageHandlers.Add(new MyServiceHandler(new MyService()));
and in your controller you can access the service like this,
public class StringsController : ApiController
{
/// <summary>
/// Get one entry from indexOfStrings
/// </summary>
public string Get(int listIndex)
{
var myService = Request.Properties["MyService_Key"] as MyService;
// How to return indexOfStrings[listIndex]?
var mystring = myService.GetMyString(99);
return "";
}
}
Ideally you should wrap up the access to your service in a helper method or a ApiController extension method, but those are just cosmetic details.
Now, you need to be careful, because MyService can now be called simultaneously on different threads. You need to ensure that you either use Concurrent collections for storing data or you use locks where necessary.

Related

Can I add and remove health checks when application is running via a http request (asp.net core)

Is there a possibility for health checks in aspnet core to be configured after the application starts via a http request. All the documentation and examples I have gone through currently configure the health checks in Startup.cs Configure method only.
For example, I would like to define a controller HealthChecksController which has action methods to add remove health checks in my application via an external application.
public class HealthChecksController : Controller
{
[HttpPost]
public Customer CreateHealthCheck(HealthCheck healthCheck)
{
//Add to existing health checks
}
[HttpDelete]
public void DeleteHealthCheck(int id)
{
//Remove from existing health checks
}
}
The reason I am asking this question is I want to develop a monitoring system for checking the status of certain services which are running on my server and the existing health checks framework looks like a proper fit for my requirement rather than reinventing the wheel. The services to be monitored are not known during application development stage and have to be configured after the application is deployed. Hence the need to configure them whenasp.net application is running.
Is this even possible with existing health checks, if not is there any other viable solution?
Have you seen Health Checks docs
How are you checking the status of the dynamic services? I'm going to assume they are APIs (for simplicity of my example)
One solution could be to create a service to store the URLs to check for health status:
NOTE: This example contains no error checking or concurrency protection.
public class HealthCheckService
{
private readonly List<string> _urls = new List<string>();
public void Add(string url)
{
_urls.Add(url);
}
public void Remove(string url)
{
_urls.Remove(url);
}
public IEnumerable<string> GetServices()
{
return _urls;
}
}
Register this as a singleton in your startup.
services.AddSingleton<HealthCheckService>();
You can inject this into your controller and add the URLs
[ApiController]
[Route("/api/health")]
public class HealthCheckController : ControllerBase
{
private readonly HealthCheckService _service;
public HealthCheckController(HealthCheckService service)
{
_service = service;
}
[HttpPost]
public IActionResult Add(string url)
{
_service.Add(url);
return Ok();
}
[HttpDelete]
public IActionResult Remove(string url)
{
_service.Remove(url);
return Ok();
}
}
Then you need to create a class that inherits from IHealthCheck
public class MyHealthChecks : IHealthCheck
{
private readonly HealthCheckService _service;
public MyHealthChecks(HealthCheckService service)
{
_service = service;
}
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
foreach(var svc in _service.GetServices())
{
// call the URL to verify?
// var response = await httpClient.SendAsync(url);
//
// do something with response and store it in results[svc]
// if (!response.IsSuccessStatusCode)
// {
// return Task.FromResult(new HealthCheckResult(
// HealthStatus.Unhealthy, svc));
// }
}
return Task.FromResult(new HealthCheckResult(
HealthStatus.Healthy));
}
}
You'll need to modify the code in CheckHealthAsync to call your services and respond appropriately.
Finally register the health check class during startup:
services.AddHealthChecks()
.AddCheck<MyHealthChecks>("CustomHealthChecks");
You can add a custom response writer if you want to return more detailed health status information.

Send messages via SignalR HubContext from method in project outside where Hubs are located

I have a WebAPI project where the API, Service and Data layers are all in separate projects of the same solution. As part of a method in my Service project, I want to send a message to the connected clients of a hub in the API project. So far all of the examples I have found have everything in a single project and use a controller as the example sending a message via a hub.
I've tried dependency injection (Autofac) however I am unable to get a reference to the MessageHub.
[HubName("messages")]
public class MessageHub : Hub
{
public void ShowNewMessage(string message)
{
Clients.All.showMessageOnPage(message);
}
}
My attempt at Injecting can be seen here: Inject SignalR IHubContext into service layer with Autofac
Please review this option:
Define generic hub interface in your Service (or better Domain) Layer project. Something like IMessageBroker.
Inside your Presentation Layer (WebAPI) project implement this interface and use IConnectionManager for HubContext retrieving.
Register the interface in an IoC Container (Autofac) in the Presentation Layer
Inject the interface inside App Service
Pseudo Code:
Domain Layer:
public interface IMessageBroker
{
void ShowNewMessage(string message)
}
Service Layer:
public class NotificationService: INotificationService
{
private readonly IMessageBroker _messageBroker;
public NotificationService(IMessageBroker messageBroker)
{
_messageBroker = messageBroker;
}
public void RunNotification(string message)
{
_messageBroker.ShowNewMessage(message);
}
}
Presentation Layer:
[HubName("messages")]
public class MessageHub: Hub
{
public void ShowNewMessage(string message)
{
Clients.All.showMessageOnPage(message);
}
}
public class MessageBroker: IMessageBroker
{
private readonly IConnectionManager _connectionManager;
public MessageBroker(IConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
public void ShowNewMessage(string message)
{
var hub = _connectionManager.GetHubContext<MessageHub>();
// Use Hub Context and send message
}
}
Autofac Registration (Presentation Layer):
// Register Hubs
builder.RegisterHubs(Assembly.GetExecutingAssembly());
// Register Autofac resolver into container to be set into HubConfiguration later
builder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
// Register ConnectionManager as IConnectionManager so that you can get hub context via IConnectionManager injected to your service
builder.RegisterType<ConnectionManager>().As<IConnectionManager>().SingleInstance();
// Register interface
builder.RegisterType<MessageBroker>().As<IMessageBroker>();
Also similar SO topic is available here.

Get Current Domain from Unity Configuration

I have a Service that makes API calls to an external App using the executing domain. So if I am in test mode, I call "http://localhost" and if I am in Production I would like it call a different address. So far I have
public interface IMyService{
void DoStuff();
}
private string _url;
public class MyService : IMyService
{
public MyService(string ExecutingDomainAddress)
{
_url = ExecutingDomainAddress;
}
public void DoStuff()
{
var destination = _url + "/GetCustomers";
}
}
In my Unity Configuration I have defined my Service and Contract as
container.RegisterType<IMyService, MyService>();
I would like to inject the current url/address into the Service constructor. Can that be done from here?
So for example:
var theCurrenDomain = "http://localhost/MySite"; //I want this to be dynamically generated e.g. Request.Url.Authority
container.RegisterInstance<string>("ExecutingDomainAddress", theCurrenDomain, new PerThreadLifetimeManager());
container.RegisterType<IMyService, MyService>(new InjectionConstructor(new ResolvedParameter<string>("ExecutingDomainAddress")));
How do I get the url from here and apply it to the "theCurrentDomain" variable?
I tend to create a class to represent these settings, then register that class as a type. Then any classes that depend on those settings can request an object of that type via constructor injection.
container.RegisterType<FileSystemPricerStagingDirectorySettings>(new InjectionConstructor(ConfigurationManager.AppSettings["PricerStagingDirectory"]));
container.RegisterType<IPricerStagingRepository, FileSystemPricerStagingRepository>();
public FileSystemPricerStagingRepository(FileSystemPricerStagingDirectorySettings pricerStagingDirectorySettings)
{
// now I can get what I need from pricerStagingDirectorySettings
}

Pass ServiceBase class instance to ApiController hosted on it

I'm developing a Windows Service with a RESTFul web service hosted on it. I'm going to communicate with the windows service throught the windows service.
This is my project structure:
These are my classes implementation.
namespace WindowsService_HostAPI
{
public partial class SelfHostService : ServiceBase
{
private int _value;
private HttpSelfHostServer _server;
private static readonly ILog _log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public int Value
{
get { return _value; }
set { _value = value; }
}
public SelfHostService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
name: "API",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
protected override void OnStop()
{
if (_server != null)
_server.CloseAsync().Wait();
}
}
}
And ValuesController:
namespace WindowsService_HostAPI.Controllers
{
public class ValuesController : ApiController
{
[HttpGet]
[Route("api/Value/")]
public HttpResponseMessage GetValue()
{
HttpResponseMessage response = null;
return response;
}
[HttpPost]
[Route("api/Value/{value}")]
public HttpResponseMessage SetValue(int value)
{
HttpResponseMessage response = null;
return response;
}
}
}
This is only an example, but I need to communicate the ApiController with the Windows Service class.
I have to modify SelfHostService.Value property when someone do a Post (method SetValue) setting value passed. How can I do that?
I need to create a class instance and keep it alive until Windows
Service stops
The right way to do what you want with Web API is to use IDependencyResolver. Here's the sample (it uses Unity, but you can use any container).
The main concept is that you build up a contract for dependency, e.g.:
public interface IValueProvider
{
public int Value { get; set; }
}
then implement this contract somewhere (you can even implement it in SelfHostService, but, actually, you shouldn't), configure DI-container to use your implementation, and use it from controller:
public class ValuesController : ApiController
{
private readonly IValueProvider _valueProvider;
public ValuesController(IValueProvider valueProvider)
{
_valueProvider = valueProvider;
}
// the rest of code here
}
Note, that usually DI-containers allow to build parent/child hierarchy.
To reflect this, Web API DI approach uses scopes for this (see IDependencyResolver.BeginScope method).
There are global scope and child scopes. The Web API host creates global scope when it starts. This scope lives until host listens for requests. Host creates child scopes, when request is received, and host needs to create a controller to respond.
DI containers differ a little, when manage lifetime of objects, that were created using container. But the common is to place dependencies to the scope, where they needed. So, if you want to build some "global" dependency, then you need to place it into global scope.

Passing a unique value to all classes using Dependency Injection?

The following code shows the flow I’m currently trying to implement within a WCF service. The service on startup calls the Bootstrapper class which uses Unity to register and resolve the required types. The Gateway class contains the public method which then kicks off the main flow of processing a message (there are many more levels to the code than is shown below).
public static class Bootstrapper
{
public static IGateway InitializeGateway()
{
IUnityContainer resolver = new UnityContainer();
resolver.RegisterType<IGateway, Gateway>();
resolver.RegisterType<ITranslator, Translator>();
resolver.RegisterType<IFormatter, IFormatter>();
return resolver.Resolve<IGateway>();
}
}
public class Gateway : IGateway
{
private readonly ITranslator translator;
private readonly IFormatter formatter;
public Gateway(ITranslator translator, IFormatter formatter)
{
this.translator = translator;
this.formatter = formatter;
}
public string ProcessMessage(string requestMessage)
{
// Create a new GUID for use in main flow for logging
Guid messageGuid = Guid.NewGuid();
requestMessage = this.translator.TranslateMessage(requestMessage);
requestMessage = this.formatter.FormatMessage(requestMessage);
return requestMessage;
}
}
Now what I’m trying to achieve is take the GUID (created for each message) and pass this down within the flow of the service such that each class has a reference to it for logging purposes.
I have tried to find a way of using DI and constructor injection but don’t know if this can be done as the GUID is created on receipt of a message by the gateway (after the bootstrapper call). What I’m trying to get away from is passing the GUID into each method as a parameter.
Any suggestions?
Instead of rolling your own solution to this problem with DI, I would recommend you use the thread-static property Trace.CorrelationManager.ActivityId for this purpose.
Take a look at this article on WCF End-To-End Tracing.

Categories