Создание обработчика команды управления компонентами на сервере¶
Каждый компонент может поддерживать свой набор команд, однако базовыми для всех являются PublishKey
, CheckKey
и DeleteKey
. Рассмотрим, как этому набору команд можно добавить новую команду на примере web-сервера TESSA. Для лучшей иллюстративности добавим поддержку на стороне сервера команды Ping
, поддержка которой на клиентской стороне была рассмотрена ранее.
В проекте Tessa.Extensions.Server
в директории ComponentCommandHandlers
необходимо создать файл PingDiscoveryReceiverCommandHandler.cs
следующего содержания:
#nullable enable
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tessa.Discovery;
using Tessa.Platform.Storage;
namespace Tessa.Extensions.Server.ComponentCommandHandlers
{
/// <summary>
/// Sample Ping command server component handler
/// </summary>
public sealed class PingDiscoveryReceiverCommandHandler :
IDiscoveryReceiverCommandHandler
{
#region Fields
private const string CommandType = "Ping";
private const string CommandScope = "ping";
#endregion
#region IDiscoveryReceiverCommandHandler Implementation
/// <inheritdoc/>
public string HandledCommandType => CommandType;
/// <inheritdoc/>
public ValueTask<DiscoveryCommandResponse> HandleAsync(DiscoveryCommandRequest request, CancellationToken cancellationToken = default)
{
var response = new DiscoveryCommandResponse();
// check command execution rights
if (request.Scopes is null || !request.Scopes.Contains(CommandScope, StringComparer.Ordinal))
{
response.Result = "ERROR";
response.Text = $"Command doesn't have scope \"{CommandScope}\"";
return ValueTask.FromResult(response);
}
// check the message
var message = request.Arguments.TryGet<string>("Say");
if (string.IsNullOrWhiteSpace(message))
{
// response for empty message
response.Result = "WARNING";
response.Text = "Pong: Nothing to say...";
return ValueTask.FromResult(response);
}
// normal response
response.Result = "OK";
response.Text = $"Pong: Say {message}";
return ValueTask.FromResult(response);
}
#endregion
}
}
Каждый серверный обработчик на стороне компонента применяется только лишь к одной команде. Чтобы класс считался обработчиком он должен реализовывать интерфейс IDiscoveryReceiverCommandHandler
.
Тип команды задаётся в значении свойства HandledCommandType
.
Основная обработка команды происходит в методе HandleAsync
. Каждый обработчик должен вначале проверить необходимые команде права выполнения, поскольку именно обработчик, выполняя команду, знает какие права необходимы для её выполнения. Если нужные права не заданы, возвращается ошибка. Допустимо также генерировать исключение, однако в этом случае в тексте результата будет также указан тип исключения, помимо смысловой нагрузки, по какой причине оно произошло.
После проверки прав, команда пытается извлечь текст сообщения из аргументов по ключу Say
(напомним, что сообщение было установлено именно по этому ключу). В случае, если сообщение не было получено, команда выполняется с предупреждением, т.к. это не штатный режим работы, но по дизайну выполнения было принято решение не считать такую ситуацию ошибочной в строгом смысле.
Наконец, если текст сообщения был успешно получен, команда выполняется в нормальном режиме.
Теперь зарегистрируем рассмотренный обработчик. Для этого необходимо создать файл Registrator.cs
в той же директории, что и только что созданный класс, со следующим содержимым:
#nullable enable
using Tessa.Discovery;
using Unity;
using Unity.Lifetime;
namespace Tessa.Extensions.Server.ComponentCommandHandlers
{
public class Registrator : RegistratorBase
{
public override void RegisterUnity()
{
this.UnityContainer
// Register handlers
.RegisterType<IDiscoveryReceiverCommandHandler, PingDiscoveryReceiverCommandHandler>(nameof(PingDiscoveryReceiverCommandHandler),
new ContainerControlledLifetimeManager());
;
}
}
}
На этом процедура создания серверного обработчика команды для компонента завершена.