Table of Contents

Разработка плагинов для веб-сервера

В этой статье рассмотрим написание плагина для WEB-версии ПОЛИНОМ:MDM.

Расположение ключевых элементов

Полный код версии проекта плагина располагается по пути: \SDK\Web\Samples\Server\Samples.WebApi.Plugin, а также будет приведён в конце статьи

Производить сборку плагина нужно в ту директорию, где у вас располагается файл PolynomWebServer.exe для запуска веб-сервера. Рядом с этим exe-файлом находятся библиотеки, которые необходимы для компиляции плагина:

  • Ascon.Polynom.Api.dll
  • Ascon.Polynom.Web.Api.Core.dll
  • Ascon.Polynom.Web.Api.Plugins.Basic.dll

Предположим, у Вас это директория C:\POLYNOM\WebServer

В этой директории же в итоге должны будут появиться файлы созданного плагина, при запуске сервера плагины подключатся и будут доступны в Swagger'е.

В файле настроек проекта (в рассматриваемом примере это файл Samples.WebApi.Plugin.csproj) нужно указать пути до вышеупомянутых библиотек. Замените значения в тегах <HintPath>...</HintPath> на свои:

<ItemGroup>
     <Reference Include="Ascon.Polynom.Api">
          <HintPath>C:\POLYNOM\WebServer\Ascon.Polynom.Api.dll</HintPath>
          <ExcludeAssets>All</ExcludeAssets>
          <PrivateAssets>All</PrivateAssets>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
     </Reference>
     <Reference Include="Ascon.Polynom.Web.Api.Core">
          <HintPath>C:\POLYNOM\WebServer\Ascon.Polynom.Web.Api.Core.dll</HintPath>
          <ExcludeAssets>All</ExcludeAssets>
          <PrivateAssets>All</PrivateAssets>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
     </Reference>
     <Reference Include="Ascon.Polynom.Web.Api.Plugins.Basic">
          <HintPath>C:\POLYNOM\WebServer\Ascon.Polynom.Web.Api.Plugins.Basic.dll</HintPath>
          <ExcludeAssets>All</ExcludeAssets>
          <PrivateAssets>All</PrivateAssets>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
     </Reference>
</ItemGroup>

В том же файле Samples.WebApi.Plugin.csproj по аналогии нужно указать, куда будет помещён плагин после сборки (значение в теге <OutputPath>) :

<PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <OutputType>Library</OutputType>

    <ImplicitUsings>disable</ImplicitUsings>
    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
    <OutputPath>C:\POLYNOM\WebServer</OutputPath>
</PropertyGroup>

Настройка методов и входных данных в Swagger'е

В основном файле проекта будут написаны методы, которые мы увидим в Swagger'е после запуска сервера.

Рассмотрим детально пример плагина из SDK (файл SampleController.cs):

/// <summary>
/// Возвращает статистику хранилища данных.
/// </summary>
/// <param name="referenceName">Если указано имя справочника, то статистика будет выдана только по этому справочнику.</param>
/// <param name="cancellationToken">Токен отмены.</param>
[HttpGet(nameof(GetStatistics))]
public async Task<ActionResult<CountInfo>> GetStatistics(string referenceName, CancellationToken cancellationToken)

Сначала идёт отображаемое описание метода: Возвращает статистику хранилища данных.

Далее описания для входных параметров: <param name="referenceName">Если указано имя справочника, то статистика будет выдана только по этому справочнику.</param>

Далее задаётся отображаемое имя метода: GetStatistics, оно также дублируется в названии функции в следующей строке

Task<ActionResult<...>> - базовая обёртка для выходных данных, а CountInfo - непосредственно полезное возвращаемое содержимое (в данном примере класс, описанный в файле Data\CountInfo.cs)

И в конце перечисляются входные атрибуты (в данном случае referenceName и cancellationToken)

Запуск и проверка

Теперь выполним сборку плагина. Для рассматриваемого примера результатом будет 4 файла, которые будут созданы в папке Bin\WebServer:

  • Sample.Web.Api.Plugin.deps
  • Sample.Web.Api.Plugin.dll
  • Sample.Web.Api.Plugin.pdb
  • Sample.Web.Api.Plugin.xml

Затем выполняем запуск сервера из той же директории, как результат у нас должен появиться новый раздел Sample в Swagger'е, который содержит метод get-statistics

Параметр api-version добавлен автоматически Swagger'ом и может быть проигнорирован.

Все файлы проекта плагина

Samples.WebApi.Plugin.csproj - файл настроек проекта:

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <OutputType>Library</OutputType>

        <ImplicitUsings>disable</ImplicitUsings>
        <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
        <OutputPath>..\..\..\..\..\Bin\WebServer</OutputPath>
    </PropertyGroup>
    
    <PropertyGroup>        
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>

    <PropertyGroup>
        <RootNamespace>Sample.Web.Api.Plugin</RootNamespace>
        <AssemblyName>Sample.Web.Api.Plugin</AssemblyName>
    </PropertyGroup>

    <PropertyGroup>
        <Title>Пример дополнения к WEB API ПОЛИНОМ:MDM</Title>
        <AssemblyTitle>$(Title)</AssemblyTitle>
        <AssemblyDescription>$(Title)</AssemblyDescription>
        <DocumentationFile>$(AssemblyName).xml</DocumentationFile>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
    </ItemGroup>

    <ItemGroup>
        <Reference Include="Ascon.Polynom.Api">
            <HintPath>..\..\..\..\..\Bin\WebServer\Ascon.Polynom.Api.dll</HintPath>
            <ExcludeAssets>All</ExcludeAssets>
            <PrivateAssets>All</PrivateAssets>
            <Private>false</Private>
            <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="Ascon.Polynom.Web.Api.Core">
            <HintPath>..\..\..\..\..\Bin\WebServer\Ascon.Polynom.Web.Api.Core.dll</HintPath>
            <ExcludeAssets>All</ExcludeAssets>
            <PrivateAssets>All</PrivateAssets>
            <Private>false</Private>
            <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="Ascon.Polynom.Web.Api.Plugins.Basic">
            <HintPath>..\..\..\..\..\Bin\WebServer\Ascon.Polynom.Web.Api.Plugins.Basic.dll</HintPath>
            <ExcludeAssets>All</ExcludeAssets>
            <PrivateAssets>All</PrivateAssets>
            <Private>false</Private>
            <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
    </ItemGroup>

    <ItemGroup>
      <None Remove="Sample.Web.Api.Plugin.xml" />
      <Content Include="Sample.Web.Api.Plugin.xml">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Content>
    </ItemGroup>

</Project>

Controllers\SampleController.cs - основной файл с методами для Swagger'а и логикой исполнения

using Ascon.Polynom.Web.Api.Core.Interfaces;
using Ascon.Polynom.Web.Api.Plugins.Basic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Sample.Web.Api.Plugin.Data;

namespace Sample.Web.Api.Plugin.Controllers
{
    /// <summary>
    /// Пример контроллера расширения.
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    [Authorize]
    public class SampleController : WebApiPluginController
    {
        /// <summary>
        /// Инициализирует экземпляр класса.
        /// </summary>
        public SampleController(IPolynomAuthorizationService authorizationService,
            IServiceProvider services) : base(authorizationService, services)
        {
        }

        /// <summary>
        /// Возвращает статистику хранилища данных.
        /// </summary>
        /// <param name="referenceName">Если указано имя справочника, то статистика будет выдана только по этому справочнику.</param>
        /// <param name="cancellationToken">Токен отмены.</param>
        [HttpGet(nameof(GetStatistics))]
        public async Task<ActionResult<CountInfo>> GetStatistics(string referenceName, CancellationToken cancellationToken)
        {
            return await Task.Run( async () =>
               {
                   var result = new CountInfo();

                   var session = await GetPolynomApiSession(cancellationToken);
                   if (session is null)
                       return new JsonResult(result);

                   // Получение полного списка справочников.
                   var references = session.Objects.AllReferences;
                   foreach (var reference in references)
                   {
                       if (!string.IsNullOrEmpty(referenceName) && referenceName != reference.Name)
                           continue;

                       result.ReferenceCount++;
                       result.DocumentCatalogCount++;
                       result.ViewpointCatalogCount++;
                       foreach (var catalog in reference.Catalogs)
                       {
                           result.CatalogCount++;
                       }
                   }

                   // Получение списка понятий в корне.
                   var concepts = session.Objects.RootConcepts;
                   foreach (var concept in concepts)
                   {
                       result.RootConceptCount++;
                       // Получение списка вложенных понятий.
                       var innerConcepts = concept.AllSubConcepts;
                       foreach (var innerConcept in innerConcepts)
                       {
                           result.InnerConceptCount++;
                       }
                   }

                   // Получение списка групп определений свойств.
                   var propDefGroups = session.Objects.PropDefCatalog.PropDefGroups;
                   foreach (var propDefGroup in propDefGroups)
                   {
                       ProcesPropDefGroup(result, propDefGroup);
                   }

                   return new JsonResult(result);
               }, cancellationToken
            );
        }

        private void ProcesPropDefGroup(CountInfo countInfo, Ascon.Polynom.Api.IPropDefGroup propDefGroup)
        {
            countInfo.PropertyDefinitionGroupCount++;

            // Получение списка определений свойств в группе.
            foreach (var propDef in propDefGroup.PropertyDefinitions)
            {
                countInfo.PropertyDefinitionCount++;
            }

            // Получение списка вложенных групп определений свойств.
            foreach (var innerPropDefGroup in propDefGroup.PropDefGroups)
            {
                ProcesPropDefGroup(countInfo, innerPropDefGroup);
            }
        }
    }
}

Data\CountInfo.cs - вспомогательный файл, описывает выходную модель данных

namespace Sample.Web.Api.Plugin.Data;

/// <summary>
/// Представляет статистику хранилища.
/// </summary>
public class CountInfo
{
    /// <summary>
    /// Возвращает или задает количество справочников.
    /// </summary>
    public int ReferenceCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество каталогов.
    /// </summary>
    public int CatalogCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество каталогов документов.
    /// </summary>
    public int DocumentCatalogCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество каталогов представлений.
    /// </summary>
    public int ViewpointCatalogCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество понятий в корне.
    /// </summary>
    public int RootConceptCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество вложенных понятий.
    /// </summary>
    public int InnerConceptCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество групп определений свойств.
    /// </summary>
    public int PropertyDefinitionGroupCount { get; set; }

    /// <summary>
    /// Возвращает или задает количество определений свойств.
    /// </summary>
    public int PropertyDefinitionCount { get; set; }
}

Properties\launchSettings.json - вспомогательный файл проекта, изменения не требуются

{
  "profiles": {
    "Samples.WebApi.Plugin": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "https://localhost:59080;http://localhost:59081"
    }
  }
}