Beveiligen van je IHostedService ( of IHosedLifecycleService) is een enkele voorbeeld.
Inleiding
Dit is een domme kleine artikel omdat ik wat verward was over hoe ik mijn IHostedService Ik dacht dat het een beetje ingewikkelder was dan het werkelijk was.
In de Vorige artikel, hebben we behandeld hoe je een achtergronddienst kunt creëren met behulp van de IHostedService interface for sending emails. Dit artikel gaat over hoe je ervoor zorgt dat je IHostedService is een enkel voorbeeld.
Dit is misschien duidelijk voor sommigen, maar het is niet voor anderen.
Waarom is dit een probleem?
Nou, het is een probleem omdat de meeste artikels deze omhullen hoe je een IHostedService Maar het gaat niet over hoe je ervoor zorgt dat de dienst één voorbeeld is.
Wat bedoel ik?? Nou, in ASP is de manier om een IHostedService of IHosedlifeCycleService te registreren (in principe hetzelfde met meer overrides voor levenscyclusmanagement.
services.AddHostedService(EmailSenderHostedService);
Dat roept naar deze backendcode.
public static IServiceCollection AddHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>());
return services;
}
Dat is prima en leuk, maar wat als je een nieuw bericht direct naar deze dienst wil sturen van een Controller actie?
public class ContactController(EmailSenderHostedService sender,ILogger<BaseController> logger) ...
{
[HttpPost]
[Route("submit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Submit([Bind(Prefix = "")] ContactViewModel comment)
{
ViewBag.Title = "Contact";
//Only allow HTMX requests
if(!Request.IsHtmx())
{
return RedirectToAction("Index", "Contact");
}
if (!ModelState.IsValid)
{
return PartialView("_ContactForm", comment);
}
var commentHtml = commentService.ProcessComment(comment.Comment);
var contactModel = new ContactEmailModel()
{
SenderEmail = string.IsNullOrEmpty(comment.Email) ? "Anonymous" : comment.Email,
SenderName = string.IsNullOrEmpty(comment.Name) ? "Anonymous" : comment.Name,
Comment = commentHtml,
};
await sender.SendEmailAsync(contactModel);
return PartialView("_Response",
new ContactViewModel() { Email = comment.Email, Name = comment.Name, Comment = commentHtml });
return RedirectToAction("Index", "Home");
}
}
Ofwel moet je een interface creëren die zichzelf implementeert. IHostedService Dan roep je in de methode op dat of je moet ervoor zorgen dat het service een enkel voorbeeld is. Dit laatste is de makkelijkste manier om dit te doen.
IHostedService
Je ' merkt hier op dat het de dienst als een IHostedService, dit heeft te maken met het levenscyclusmanagement van deze dienst, omdat de ASP-framework .NET deze registratie zal gebruiken om de gebeurtenissen van die dienst aan te pakken.StartAsync en StopAsync voor IHostedService). Zie onder IHostedlifeCycleService is slechts een meer gedetailleerde versie van IHostedService.
/// <summary>
/// Defines methods for objects that are managed by the host.
/// </summary>
public interface IHostedService
{
/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Start operation.</returns>
Task StartAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Stop operation.</returns>
Task StopAsync(CancellationToken cancellationToken);
}
namespace Microsoft.Extensions.Hosting
{
/// <summary>
/// Defines methods that are run before or after
/// <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" /> and
/// <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
/// </summary>
public interface IHostedLifecycleService : IHostedService
{
/// <summary>
/// Triggered before <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StartingAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered after <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StartAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StartedAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered before <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StoppingAsync(CancellationToken cancellationToken);
/// <summary>
/// Triggered after <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" />.
/// </summary>
/// <param name="cancellationToken">Indicates that the stop process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous operation.</returns>
Task StoppedAsync(CancellationToken cancellationToken);
}
}
Hoe zorg je ervoor dat je IHostedService een enkel voorbeeld is?
Interface Approach
De interface-aanpak kan eenvoudiger zijn afhankelijk van je scenario. Hier voeg je een interface toe die van IHostedService Dan voeg je een methode toe aan die interface waarmee je vanuit je controller kunt bellen.
NOTA: Je moet het nog steeds als 'HostedService' in ASP toevoegen.
public interface IEmailSenderHostedService : IHostedService, IDisposable
{
Task SendEmailAsync(BaseEmailModel message);
}
Alles wat we dan nodig hebben, is dit als een singleton te registreren en dan in onze controller te gebruiken.
services.AddSingleton<IEmailSenderHostedService, EmailSenderHostedService>();
services.AddHostedService<IEmailSenderHostedService>(provider => provider.GetRequiredService<IEmailSenderHostedService>());
ASP.NET zal zien dat dit de correcte interface is versinkt en deze registratie gebruikt om het internet te runnen. IHostedService.
Werksmethode Approach
Een andere om ervoor te zorgen dat je IHostedService is een enkel voorbeeld om de AddSingleton methode om je dienst te registreren en dan de IHostedService Registreren als een 'fabrieksmethode'. Dit zal ervoor zorgen dat slechts één voorbeeld van je dienst wordt gecreëerd en gebruikt gedurende de hele levensduur van de toepassing.
-
- factorie. methode is gewoon een fancieuze manier om een methode te zeggen die een voorbeeld van een object creëert.
services.AddSingleton<EmailSenderHostedService>();
services.AddHostedService(provider => provider.GetRequiredService<EmailSenderHostedService>());
Zoals je hier ziet, registreer ik eerst mijn IHostedService (of IHostedLifeCycleService) als een enkelton en dan gebruik ik de AddHostedService methode om de dienst te registreren als een fabrieksmethode. Dit zal ervoor zorgen dat slechts één voorbeeld van de dienst wordt gecreëerd en gebruikt gedurende de levensduur van de applicatie
In slotting:
Zoals gewoonlijk is er een paar manieren om een kat te kweken... De fabrieksmethode-aanpak is ook een goede manier om ervoor te zorgen dat je dienst één voorbeeld is. IHostedService is een enkel voorbeeld.