Создание веб-приложения ASP.NET Core, использующего API TESSA
Создание веб-приложения ASP.NET Core, использующего API TESSA¶
В этом примере мы рассмотрим создание независимого веб-приложения ASP.NET Core, которое может использоваться для обработки REST-запросов и вывода статических или динамических сайтов. Подход, описанный в этом разделе, может задействоваться для разработки кастомизированного веб-клиента или веб-приложения для мобильных устройств.
Откройте Visual Studio проектного решения Source\Tessa.Extensions.sln
. Создайте папку Services
в контекстном меню Solution Explorer
. Далее создайте проект ASP.NET Core Empty
через контекстное меню в папке, который поместите в подпапку Source\Services
, указав путь в поле Location
.
Откройте Visual Studio и создайте проект ASP.NET Core Web Application
.
Укажите реализацию для .NET 5.0
.
Откройте для редактирования файл проекта .csproj
и замените его содержимое таким образом:
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="$(ProjectDir)../../Tessa.targets" />
<Import Project="$(ProjectDir)../../Tessa.Extensions.targets" />
<Import Project="$(ProjectDir)../../Tessa.Runtime.targets" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RestoreSources>$(RestoreSources);../../Bin/packages;https://api.nuget.org/v3/index.json</RestoreSources>
<NoWarn>$(NoWarn);1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>9</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-x64'">
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
<ItemGroup>
<Content Update="app.json" CopyToOutputDirectory="PreserveNewest" />
<Content Update="app-*.json" CopyToOutputDirectory="PreserveNewest" />
<Content Update="applocal-*.json" CopyToOutputDirectory="PreserveNewest" />
<Content Update="extensions.xml" CopyToOutputDirectory="PreserveNewest" />
<Content Update="NLog.config" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
Перейдите в диалог выбора NuGet-пакетов, для этого в контекстном меню на узле Solution '...'
в панели инструментов Solution Explorer
выберите пункт Manage NuGet Packages
.
Перейдите на вкладку Browse и последовательно установите пакеты Tessa.Linux
, Tessa.Server
(в сборке 3.4.0 или ранее назывался Tessa.Compilation
), Tessa.PostgreSql
и Tessa.Web
, выбрав версию пакета, соответствующую версии вашей сборки TESSA. Например, для версии сборки TESSA 3.6.0 с патчем 1 укажите версию пакетов также 3.6.0.1. Не обновляйте пакеты до более новых версий до того, как будет обновлена инсталляция всей платформы (вместе с конфигурацией, базой данных и расширениями).
Note
Для логирования ошибок ASP.NET Core, возникающих в запросах, не связанных с TESSA и её расширениями, таких как Swagger, добавьте NuGet-пакет
NLog.Web.AspNetCore
актуальной версии. Это требуется для использования метода .UseNLog()
в коде ниже.
В файле Program.cs
в методе Main
добавьте вызовы методов инициализации перед тем, как будет вызван метод CreateWebHostBuilder()
.
using System;
using System.Linq;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog;
using NLog.Web;
using Tessa.Platform;
using Tessa.Web;
using LogLevel = NLog.LogLevel;
namespace TessaWebApp
{
public class Program
{
public static async Task Main(string[] args)
{
try
{
try
{
await TessaPlatform.InitializeFromConfigurationAsync();
await WebHelper.InitializeWebServerAsync();
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
TessaLoggers.ServiceHost.LogException("Service host failed to initialize", ex, LogLevel.Fatal);
throw;
}
try
{
await CreateHostBuilder(args).Build().RunAsync();
}
catch (OperationCanceledException)
{
// ignored
}
catch (Exception ex)
{
TessaLoggers.ServiceHost.LogException("Service host has stopped with critical exception", ex, LogLevel.Fatal);
throw;
}
}
finally
{
LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
WebHelper.ParseUrlsFromCommandLine(ref args, out string[] urls, out string[] sockets);
WebListenEndPoint[] endPoints = urls.Select(WebHelper.ParseUrlToEndPoint).ToArray();
return Host.CreateDefaultBuilder(args)
.UseNLog()
.ConfigureWebHostDefaults(
webBuilder =>
{
webBuilder
.ConfigureKestrel(options =>
{
options.AddServerHeader = false;
var environment = options.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
var logger = options.ApplicationServices.GetRequiredService<ILogger<IWebHostBuilder>>();
var serverLimits = options.ApplicationServices.GetRequiredService<IOptions<WebServerLimits>>().Value;
serverLimits.Apply(options.Limits);
var serverOptions = options.ApplicationServices.GetRequiredService<IOptions<WebServerOptions>>().Value;
X509Certificate2 httpsCertificate = endPoints.Any(x => x.Protocol == WebHelper.HttpsProtocol)
? WebHelper.LoadCertificate(serverOptions, environment.IsDevelopment())
: null;
TessaLoggers.ServiceHost.Info("Web server Options: {0}", serverOptions);
TessaLoggers.ServiceHost.Info("Web server Limits: {0}", serverLimits);
if (serverOptions.EnforceTls12)
{
options.ConfigureHttpsDefaults(listenOptions => listenOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13);
}
EndPointsResult endPointsResult = options.UseEndPoints(endPoints, serverOptions, httpsCertificate, logger);
if (!endPointsResult.HasHttps)
{
serverOptions.HttpsRedirect = HttpsRedirectMode.Disabled;
}
if (OperatingSystem.IsLinux())
{
options.UseSystemd();
foreach (string socket in sockets)
{
options.ListenUnixSocket(socket);
}
if (sockets.Length > 0)
{
endPointsResult = new EndPointsResult(true);
}
}
if (!endPointsResult.HasAny)
{
const string message = "Kestrel has no endpoints to listen to. Check https certificate availability.";
logger.LogCritical(message);
throw new InvalidOperationException(message);
}
});
webBuilder
.UseTessaConfiguration(args)
.UseStartup<Startup>();
});
}
}
}
В файле Startup.cs
выполните конфигурацию сервисов TESSA в методе ConfigureServices
и конфигурацию приложения в методе Configure
.
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Tessa.Platform.Runtime;
using Tessa.Web;
using Tessa.Web.Services;
using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions;
namespace TessaWebApp
{
public class Startup :
WebStartupBase
{
public Startup(IWebHostEnvironment hostEnvironment)
: base(hostEnvironment)
{
}
public void ConfigureServices(IServiceCollection services)
{
services
.AddTessaServices()
.AddTessaResponseCompression()
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
.ConfigureTessaMvc();
// если используем проверку работоспособности сервиса (health checks)
services
.AddHttpClient()
.AddOptions()
.AddTessaHealthChecks()
.ConfigureWebOptions();
// если используем Swagger для документирования REST-методов
if (RuntimeHelper.SwaggerDocIsEnabled)
{
services
.AddSwaggerGen(c =>
{
c.SwaggerDocForTessa("My REST API", "My API Description");
// указываем одну или несколько сборок, в которых присутствуют описываемые контроллеры
c.IncludeXmlCommentsUsingActualLocation(Assembly.GetExecutingAssembly(), includeControllerXmlComments: true);
c.IncludeXmlCommentsFromInheritDocs();
c.UseAllOfToExtendReferenceSchemas();
c.AddEnumsWithValuesFixFilters(o =>
{
o.ApplySchemaFilter = true;
o.ApplyParameterFilter = true;
o.ApplyDocumentFilter = true;
o.IncludeDescriptions = true;
o.IncludeXEnumRemarks = true;
o.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments;
o.IncludeXmlCommentsForTessa();
});
})
.AddTessaSwaggerGenNewtonsoftSupport();
}
}
public void Configure(
IApplicationBuilder app,
IHostApplicationLifetime applicationLifetime,
IOptions<WebOptions> options,
IOptions<WebServerOptions> serverOptions)
{
bool isDevelopment = this.HostEnvironment.IsDevelopment();
if (isDevelopment)
{
app.UseDeveloperExceptionPage();
}
var forwardedHeaders = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
ForwardLimit = 10,
};
forwardedHeaders.KnownNetworks.Clear();
forwardedHeaders.KnownProxies.Clear();
app
.UseForwardedHeaders(forwardedHeaders)
.UseTessaHttpsRedirection(serverOptions.Value, isDevelopment)
.UsePathBaseIfSpecified(options.Value.PathBase)
.UseResponseCompression();
app.UseStaticFiles();
// если используем Swagger для документирования REST-методов
if (RuntimeHelper.SwaggerDocIsEnabled)
{
app
.UseSwagger()
.UseSwaggerUIForTessa("My custom API v1");
}
// вызовы UseRouting, UseAuthorization и UseEndpoints должны быть в указанном порядке
// разделены по разным операторам (точкой с запятой), чтобы анализатор .NET при сборке не выводил предупреждения
app
.UseTessaApplication()
.UseRouting();
app
.UseAuthentication()
.UseAuthorization();
app
.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/hcheck");
endpoints.MapControllers();
})
.ConfigureTessaApplication();
applicationLifetime.RegisterTessaLifetime(app);
app.Run(context => context.HandleNotFoundAsync());
}
}
}
Note
Метод app.UsePathBaseIfSpecified() доступен, начиная со сборки 3.5.0.
В файле TestController.cs
приведён пример REST-контроллера, который выполняет некоторые бизнес-требования. Файл рекомендуется создать в подпапке Controllers
проекта сервиса.
Note
Для создания REST API укажите атрибут [ApiController]
на классе контроллера, и воспользуйтесь рекомендациями из MSDN: https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-5.0
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Tessa.Cards;
using Tessa.Platform;
using Tessa.Platform.Runtime;
using Tessa.Platform.Storage;
using Tessa.Platform.Validation;
using Tessa.Roles;
using Tessa.Web;
namespace TessaWebApp.Controllers
{
[Route("test"), ApiController, AllowAnonymous]
[ProducesErrorResponseType(typeof(PlainValidationResult))]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public class TestController :
Controller
{
public TestController(
ITessaServerSettings serverSettings,
ILoginService loginService,
ISessionService sessionService,
ICardRepository cardRepository)
{
this.serverSettings = serverSettings;
this.loginService = loginService;
this.sessionService = sessionService;
this.cardRepository = cardRepository;
}
private readonly ITessaServerSettings serverSettings;
private readonly ILoginService loginService;
private readonly ISessionService sessionService;
private readonly ICardRepository cardRepository;
[HttpGet("system")]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<CardGetResponse>> GetSystemUserText(CancellationToken cancellationToken = default)
{
// объект PlainValidationResult в формате application/json возвращается в случае необработанных исключений
await using var _ = SessionContext.Create(Session.CreateSystemToken(SessionType.Server, this.serverSettings));
return await this.GetSystemUserTextWithoutSession(cancellationToken);
}
[HttpGet("method"), SessionMethod]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<CardGetResponse>> GetSystemUserTextWithoutSession(CancellationToken cancellationToken = default)
{
CardGetResponse response = await this.cardRepository.GetAsync(
new CardGetRequest
{
CardID = Session.SystemID,
CardTypeID = RoleHelper.PersonalRoleTypeID,
}, cancellationToken);
return this.TypedJson(response);
}
public sealed class Credentials
{
public string Login { get; set; }
public string Password { get; set; }
}
[HttpPost("login")]
[Consumes(MediaTypeNames.Application.Json)]
[Produces(MediaTypeNames.Text.Xml, MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<string>> PostLogin(
[FromBody] Credentials credentials,
CancellationToken cancellationToken = default) =>
this.Ok(await this.loginService.OpenSessionAsync(
SessionClientParameters.CreateCurrent(),
ApplicationIdentifiers.Other,
credentials.Login,
credentials.Password,
cancellationToken: cancellationToken));
[HttpPost("logout"), SessionMethod]
[Consumes(MediaTypeNames.Text.Xml)]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> PostLogout([FromBody, SessionToken] string token = null)
{
await this.sessionService.CloseSessionWithTokenAsync(token);
return this.NoContent();
}
}
}
Компонент Swagger может использоваться для генерации страницы с автоматической документацией по REST-методам в ваших контроллерах. Для этого укажите регистрации Swagger в методах ConfigureServices
и Configure
(см. выше). В коде ваших контроллеров используйте символ тройного слэша ///
для добавления описаний для класса контроллера и его REST-методов. Также включите генерацию xml-файла документации в том проекте (или проектах), где расположены классы контроллеров, отредактировав файл проекта .csproj
и добавив строку GenerateDocumentationFile
(уже должна присутствовать в файле .csproj
, отредактированном ранее):
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
...
<GenerateDocumentationFile>true</GenerateDocumentationFile>
...
</PropertyGroup>
...
</Project>
Просмотреть документацию можно будет после публикации приложения, перейдя по адресу вида https://localhost/tessa/app/swagger
(где app
- имя вашего приложения).
Также в папке приложения нужен ряд конфигурационных файлов для настройки расширений, логирования и методов подключения к базе данных.
Добавьте файл extensions.xml
(Add -> New item -> XML File
), в свойствах файлах укажите Content
и Copy if newer
. Содержимое файла приведено ниже:
<?xml version="1.0" encoding="utf-8" ?>
<extensions xmlns="http://syntellect.ru/tessa/include">
<include file="Tessa.Extensions.Default.Shared.dll" />
<include file="Tessa.Extensions.Default.Server.dll" serverOnly="true" />
<include file="Tessa.Extensions.Shared.dll" />
<include file="Tessa.Extensions.Server.dll" serverOnly="true" />
<include file="Tessa.Extensions.PostgreSql.Server.dll" serverOnly="true" />
</extensions>
Добавьте файл NLog.config
(Add -> New item -> XML File
), в свойствах файлах укажите Content
и Copy if newer
. Содержимое файла приведено ниже:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets async="true">
<target name="file" xsi:type="File" encoding="utf-8" writeBom="true" fileName="${basedir}/log.txt" />
<target name="queries" xsi:type="File" encoding="utf-8" writeBom="true" fileName="${basedir}/queries.txt" layout="--${longdate}${newline}${message}${newline}GO${newline}" />
<target name="null" xsi:type="Null" formatMessage="false" />
</targets>
<rules>
<logger name="SqlQueries" minlevel="Off" writeTo="queries" final="true" />
<logger name="SqlQueries" minlevel="Trace" writeTo="null" final="true" />
<logger name="*" minlevel="Info" writeTo="file" />
</rules>
</nlog>
Добавьте файл app.json
(Add -> New item -> JSON File
), в свойствах файлах укажите Content
и Copy if newer
.
Содержимое вместе со строкой подключения и файлом лицензии мы рекомендуем скопировать из файла app.json
вашей инсталляции, удалив из него ключи: WebControllers
(обязательно!), GuyFawkesAuth
, WinAuth
, UserWallpaperName
, WallpaperSizeKb
, MultipartBodyLengthLimit
, SAML
, Themes
.
Пример содержимого файла приведён ниже (для тестового приложения можно скопировать его):
{
"ConnectionStrings": {
"default": [ "Host=localhost; Database=tessa; Integrated Security=false; User ID=postgres; Password=Master1234; Pooling=true; MaxPoolSize=100", "Npgsql" ]
},
"DataProviders": {
"Npgsql": "Npgsql.NpgsqlFactory, Npgsql"
},
".if": [
"linux",
{
"Settings": {
"PlatformDependencies": "Tessa.Platform.LinuxTessaPlatformDependencies, Tessa.Linux"
}
}
],
".include": [
"localization.json",
"patch*.json",
"app-*.json",
"applocal-*.json"
],
"Settings": {
// Autogenerated
"SignatureKey": "l5pthoEIB/s8LuiNnG1OUA+3FO1fSW5/nX4zilQnlLM013Z33Q/L7hErHKptUpfOQ6ZPG35mVkwMGu0oF8/JpA==",
"CipherKey": "SKW2d3hgQUeM61FRKV6TafWGjmqCCAQErVTk0g4YViY=",
// Basic options
"ServerCode": "tessa",
"LicenseFile": "@*.?lic",
"Redis": "",
"EnableInterprocessCache": true,
"ProbingPath": "extensions",
"ServerDependencies": "Tessa.Server.TessaServerDependencies, Tessa.Server",
"WebControllers": [ "extensions/Tessa.Extensions.Default.Server.Web.dll", "extensions/Tessa.Extensions.Server.Web.dll" ],
"WebRazorReferences": [ "extensions" ],
// Web client options
"PathBase": "",
"GuyFawkesAuth": "",
"WinAuthIsEnabled": false,
"WinAuth": "",
"WinAutoLogin": true,
"PreviewPdfEnabled": true,
"CryptoProPluginEnabled": false,
// Kerberos auth
"Kerberos.Enabled": false,
"Kerberos.Keytab": "*.keytab",
"Kerberos.DisableRealmCheck": false,
// Security options
"Configuration.Sealed": false,
"Configuration.StrictSecurity": false,
"HealthCheckIsEnabled": true,
"SwaggerDocIsEnabled": true,
"CookiesSameSite": "Strict",
"CheckPlatformVersion": true,
"SessionExpirationTimeSpan": "7.00:00:00",
"CipherKeyRotationInterval": "10.00:00:00",
"ViewAccessCacheTimeSpan": "0.01:00:00",
"AllowedRefererValues": [ ],
"ResponseHeaders": {
"X-Frame-Options": "sameorigin",
"X-XSS-Protection": "1; mode=block"
},
// Misc
"ExtensionTracingMode": "Off",
"RoleTimeoutTimeSpan": "0.00:30:00",
"RolesLockTimeoutSeconds": 300,
"LimitMaxThreads": true,
"MultipartBodyLengthLimit": -1,
"UserWallpaperName": "Wallpaper",
"WallpaperSizeKb": 600,
"WebServer": {
"HttpsRedirect": "Enabled",
"HttpsRedirectPort": null,
"HstsMaxAgeDays": 365,
"CertificateFile": "@*.cer",
"CertificateKeyFile": "@*.key",
"CertificatePassword": "",
"CertificateStoreName": "My",
"CertificateStoreLocation": "CurrentUser",
"CertificateStoreSubject": "localhost",
"Http2Disabled": false,
"EnforceTls12": false,
"DataProtectionKeysPath": "",
"DataProtectionCertificateFile": "@*.cer",
"DataProtectionCertificatePassword": ""
},
"WebServerLimits": {
"MaxResponseBufferSizeBytes": 65536,
"MaxRequestBufferSizeBytes": 1048576,
"MaxRequestLineSizeBytes": 8192,
"MaxRequestHeadersTotalSizeBytes": 32768,
"MaxRequestHeaderCount": 100,
"MaxRequestBodySizeBytes": 30000000,
"KeepAliveTimeoutSeconds": 120,
"RequestHeadersTimeoutSeconds": 30,
"MaxConcurrentConnections": null,
"MaxConcurrentUpgradedConnections": null,
"MinRequestBodyDataRateBytesPerSecond": 240.0,
"MinRequestBodyDataRateGraceSeconds": 5,
"MinResponseDataRateBytesPerSecond": 240.0,
"MinResponseDataRateGraceSeconds": 5
},
"LDAP": {
"Enabled": false,
"UseSsl": false,
"Url": "localhost",
"Port": 10389,
"TimeoutMilliseconds": null,
"BindDn": "uid=admin,ou=system",
"BindCredentials": "secret",
"SearchBase": "dc=example,dc=com",
"SearchFilter": "(&(objectClass=person)(cn={0}))"
}
}
}
Добавьте ваш файл лицензии .jlic
или .tlic
(Add -> Existing Item
), в свойствах файлах укажите Content
и Copy if newer
.
Также вам потребуется копировать в выходную папку файлы расширений, собранные для вашего проекта:
-
Tessa.Extensions.Server.dll
-
Tessa.Extensions.Shared.dll
-
Tessa.Extensions.Default.Server.dll
-
Tessa.Extensions.Default.Shared.dll
Все искомые файлы можно найти, например, в папке Services\web
внутри сборки TESSA вашей версии (в примере это 3.6.0). Добавьте эти файлы (Add -> Existing Item
), в свойствах файлов укажите None
и Copy if newer
.
Для сборок расширений должны быть установлены все их зависимости, которые обычно указываются в файлах проектов .csproj
для исходных файлах проектов расширений: Tessa.Extensions.Default.Server.csproj
, Tessa.Extensions.Default.Shared.csproj
, и любые зависимости в проектах вашего решения Tessa.Extensions.Server.csproj
и Tessa.Extensions.Shared.csproj
.
В платформе версии 3.6.0.14 это один дополнительный NuGet-пакет DocumentFormat.OpenXml v2.20.0
. Установим его:
Мы рекомендуем добавлять такое веб-приложение как проект в solution проектных расширений Tessa.Extensions.sln
. Тогда вместо ссылок на .dll
и дополнительных зависимостей (DocumentFormat.OpenXml
) можно добавить ссылку на проект, при этом выходной файл проектов с расширениями будет скопирован в сборку, а все зависимые NuGet-пакеты - автоматически установлены.
Веб-приложение перед использованием должно быть опубликовано. Для этого добавьте скрипт publish.bat
(Add -> New item -> Text File
), в свойствах файлах оставьте None
и Do Not Copy
.
Укажите следующее содержимое скрипта, причём в текстовом редакторе задайте кодировку UTF-8 без BOM (по умолчанию в Visual Studio создаётся файл в кодировке UTF-8 + BOM, что приводит к некорректному выполнению скрипта).
@echo off
set BuildPath=bin\Publish
rd /S /Q "%BuildPath%">nul 2>&1
dotnet publish -c Release -r win-x64 -o "%BuildPath%"
del /Q "%BuildPath%\*.Development.json"
for %%a in (cs, de, es, fr, it, ja, ko, pl, pt-BR, ru, tr, zh-Hans, zh-Hant) do rd /S /Q "%BuildPath%\%%a"
pause
Note
Для публикации приложения для Linux замените win-x64
на linux-x64
в этом примере. Убедитесь, что вы предварительно установили NuGet-пакет Tessa.Linux
.
Запустите скрипт publish.bat
двойным кликом и дождитесь окончания его выполнения. Предварительно собирать проект в Visual Studio не требуется.
В папке bin\Publish
будет расположен собранный проект, который в дальнейшем можно будет настроить, например, в IIS по аналогии с настройкой веб-сервиса web
в Руководстве по установке.
Для проверки сервиса без установки, запустите exe-файл в папке публикации, например, TessaWebApp.exe
. Вы увидите окно с указанием локальных портов, по которым доступно приложение:
Перейдите в браузере по адресу https://localhost:5001/check, и при корректном соединении с базой данных в app.json
, где уже установлена TESSA этой же версии, должно быть отображено окно следующего вида:
Теперь в веб-приложении можно добавлять любые зависимости как NuGet-пакеты и контроллеры по аналогии с тем, как они добавляются в проект Tessa.Extensions.Server.Web
.
В рамках примера используем метод контроллера TestController.cs
, созданного ранее.
Опубликуем приложение (перед публикацией его надо закрыть, нажав Ctrl+C
в окне консоли, если приложение было запущено). Теперь запустим приложение TessaWebApp.exe
и откроем в браузере ссылку https://localhost:5001/test/system
При успешном выполнении должна быть загружена карточка сотрудника “System”, и для него выведена структура объекта с карточкой CardGetResponse
.
Теперь откройте ссылку https://localhost:5001/test/method
Поскольку этот метод с атрибутом [SessionMethod]
, то для его выполнения требуется аутентификация, и на экране будет отображён JSON для класса PlainValidationResult
с сообщением об ошибке: Session is required to call operation /test/method
Для успешной работы такого метода сначала должен быть вызван POST-запрос по адресу /test/login
, потом результат вызова должен быть проброшен в HTTP-заголовок Tessa-Session
этого метода и всех последующих методов в пределах сессии. Для закрытия сессии выполняется POST-запрос по адресу /test/logout
(токен параметром передавать не требуется, он будет заполнен из HTTP-заголовка). Для реального приложения, конечно, адрес контроллера будет отличен от /test
.
Таким образом, мы создали, выполнили сборку и публикацию веб-приложения, которое может использоваться в сценариях обработки REST-запросов независимо от основного веб-приложения платформы, при этом мы можем использовать полноценное серверное API платформы TESSA без ограничений.