Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Примечание.
Это не последняя версия этой статьи. Актуальная версия — см. версию этой статьи для .NET 9.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. Актуальная версия — см. версию этой статьи для .NET 9.
Это важно
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Корпорация Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых в отношении информации, предоставленной здесь.
Актуальная версия — см. версию этой статьи для .NET 9.
В этой статье объясняется, как обеспечить возможность облачных Blazor WebAssembly развертываний в средах, которые блокируют загрузку и выполнение DLL-файлов.
Примечание.
В этом руководстве рассматриваются среды, которые блокируют загрузку и выполнение DLL. В .NET 8 или более поздней версии Blazor используется формат файла Webcil для решения этой проблемы. Дополнительные сведения см. в статье Размещение и развертывание ASP.NET Core Blazor WebAssembly. Многопартийное объединение с помощью экспериментального пакета NuGet, описанного в этой статье, не поддерживается для Blazor приложений в .NET 8 или более поздней версии. Инструкции из этой статьи можно использовать для создания собственного пакета NuGet для .NET 8 или более поздней версии.
Blazor WebAssembly приложениям требуется функция библиотек динамических ссылок (DLL), но некоторые среды блокируют загрузку и выполнение БИБЛИОТЕК DLL клиентами. В подмножестве этих сред изменение расширения имени файла DLL(.dll
) достаточно для обхода ограничений безопасности, но продукты безопасности часто могут сканировать содержимое файлов, просматривающих сеть и блокировать или карантин DLL-файлы. В этой статье описывается один из подходов к активации Blazor WebAssembly приложений в этих средах, где создается многокомпонентный файл пакета из библиотек DLL приложения, чтобы библиотеки DLL можно было скачать вместе с целью обхода ограничений безопасности.
Размещенное Blazor WebAssembly приложение может настроить опубликованные файлы и упаковку библиотек DLL приложений с помощью следующих функций:
- Инициализаторы JavaScript , позволяющие настраивать Blazor процесс загрузки.
- Расширяемость MSBuild для преобразования списка опубликованных файлов и определения Blazor расширений публикации. Blazor Расширения публикации — это файлы, определенные во время процесса публикации, которые предоставляют альтернативное представление для набора файлов, необходимых для запуска опубликованного Blazor WebAssembly приложения. В этой статье создается расширение публикации Blazor, которое создает многосоставной пакет, содержащий все DLL библиотеки приложения, упакованные в один файл, чтобы их можно было скачать вместе.
Подход, показанный в этой статье, служит отправной точкой для разработчиков для разработки собственных стратегий и пользовательских процессов загрузки.
Предупреждение
Любой подход, принятый для обхода ограничения безопасности, должен тщательно рассматриваться для его последствий безопасности. Перед принятием подхода в этой статье рекомендуется продолжить изучение темы с специалистами по безопасности сети в вашей организации. Альтернативы, которые следует рассмотреть, включают:
- Включите устройства безопасности и программное обеспечение безопасности, чтобы разрешить сетевым клиентам загружать и использовать точные файлы, необходимые приложению Blazor WebAssembly .
- Переключитесь с Blazor WebAssembly модели размещения на модель размещенияBlazor Server, которая поддерживает весь код приложения на сервере и не требует загрузки DLL на клиентские устройства. Blazor Server кроме того, предоставляет преимущества обеспечения конфиденциальности кода C#, не требуя использования приложений веб-API для конфиденциальности кода C# с приложениями Blazor WebAssembly .
Экспериментальный пакет NuGet и пример приложения
Подход, описанный в этой статье, используется экспериментальнымMicrosoft.AspNetCore.Components.WebAssembly.MultipartBundle
пакетом (NuGet.org) для приложений, предназначенных для .NET 6 или более поздней версии. Пакет содержит таргеты MSBuild для настройки Blazor выходных данных публикации и инициализатор JavaScript для использования пользовательского загрузчика стартовых ресурсов, каждый из которых упоминается более подробно далее в этой статье.
Предупреждение
Экспериментальные и предварительные версии функции предоставляются для сбора отзывов и не поддерживаются для использования в рабочей среде.
Далее в этой статье описано, как настроить Blazor WebAssembly процесс загрузки с помощью раздела пакета NuGet с тремя подразделами, которые содержат подробные объяснения конфигурации и кода в пакете Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle
. Подробные объяснения важны для понимания при создании собственной стратегии и пользовательского процесса загрузки для Blazor WebAssembly приложений. Чтобы использовать опубликованный экспериментальный пакет NuGet без настройки в качестве локальной демонстрации, выполните следующие действия:
Используйте существующее размещенное Blazor WebAssembly решение или создайте новое решение из Blazor WebAssembly шаблона проекта с помощью Visual Studio или передайте
-ho|--hosted
параметр вdotnet new
команду (dotnet new blazorwasm -ho
). Дополнительные сведения см. в статье Инструментарий для ASP.NET Core Blazor.Добавьте в проект Client экспериментальный пакет
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle
.Примечание.
Рекомендации по добавлению пакетов в приложения .NET см. в статьях, приведенных в разделе Установка пакетов и управление ими в рабочий процесс использования пакетов (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.
Server В проекте добавьте конечную точку для обслуживания файла пакета (
app.bundle
). Пример кода можно найти в разделе Раздача пакета из приложения-хост сервера этой статьи.Опубликуйте приложение в конфигурации Release.
Blazor WebAssembly Настройка процесса загрузки с помощью пакета NuGet
Предупреждение
Руководство в этом разделе с тремя подразделами относится к созданию пакета NuGet с нуля для реализации собственной стратегии и пользовательского процесса загрузки.
ЭкспериментальныйMicrosoft.AspNetCore.Components.WebAssembly.MultipartBundle
пакет (NuGet.org) для .NET 6 и 7 основан на рекомендациях в этом разделе. При использовании предоставленного пакета в локальной демонстрации подхода к загрузке пакета с несколькими частями вам не нужно следовать инструкциям в этом разделе. Инструкции по использованию предоставленного пакета см. в разделе "Экспериментальный пакет NuGet" и пример приложения .
BlazorРесурсы приложения упаковываются в файл пакета с несколькими частями и загружаются браузером с помощью пользовательского инициализатора JavaScript (JS). Для приложения, потребляющего пакет с инициализатором JS , приложению требуется только то, что файл пакета обслуживается при запросе. Все остальные аспекты этого подхода обрабатываются прозрачно.
Четыре настройки необходимы для загрузки опубликованного Blazor по умолчанию приложения:
- Задача MSBuild для преобразования файлов публикации.
- Пакет NuGet с целями MSBuild, который включается в процесс публикации, преобразует выходные данные и определяет один или несколько файлов расширения публикации (в данном случае единственный пакет).
- Инициализатор JS для обновления обратного Blazor WebAssembly вызова загрузчика ресурсов, чтобы загрузить пакет и предоставить приложению отдельные файлы.
- Вспомогательный элемент в хост-приложении Server, обеспечивающий обслуживание пакета клиентам по запросу.
Создание задачи MSBuild для настройки списка опубликованных файлов и определения новых расширений
Создайте задачу MSBuild в качестве общедоступного класса C#, который можно импортировать как часть компиляции MSBuild и который может взаимодействовать со сборкой.
Для класса C# требуется следующее:
- Новый проект библиотеки классов.
- Целевая платформа проекта
netstandard2.0
. - Ссылки на пакеты MSBuild:
Примечание.
Пакет NuGet для примеров в этой статье назван в честь пакета, предоставленного корпорацией Майкрософт, Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle
. Рекомендации по именованию и созданию собственного пакета NuGet см. в следующих статьях NuGet:
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.csproj
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="{VERSION}" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="{VERSION}" />
</ItemGroup>
</Project>
Определите последние версии пакетов для плейсхолдеров {VERSION}
на NuGet.org.
Чтобы создать задачу MSBuild, создайте открытый класс C#, расширяющий Microsoft.Build.Utilities.Task (не System.Threading.Tasks.Task) и объявите три свойства:
-
PublishBlazorBootStaticWebAsset
: список файлов для публикации для приложения Blazor. -
BundlePath
: путь, в котором записывается пакет. -
Extension
: новые расширения для публикации, которые нужно включить в сборку.
Следующий пример BundleBlazorAssets
класса является отправной точкой для дальнейшей настройки:
- В методе
Execute
пакет создается из следующих трех типов файлов:- Файлы JavaScript (
dotnet.js
) - файлы WASM (
dotnet.wasm
) - Библиотеки DLL приложений (
.dll
)
- Файлы JavaScript (
-
multipart/form-data
Создается пакет. Каждый файл добавляется в пакет с соответствующими описаниями с помощью заголовка Content-Disposition и заголовка Content-Type. - После создания пакета пакет записывается в файл.
- Сборка настроена для расширения. Следующий код создает элемент расширения и добавляет его в
Extension
свойство. Каждый элемент расширения содержит три части данных:- Путь к файлу расширения.
- Путь URL-адреса относительно корневого Blazor WebAssembly каталога приложения.
- Имя расширения, которое группировало файлы, созданные заданным расширением.
После достижения предыдущих целей задача MSBuild создается для настройки выходных данных публикации Blazor.
Blazor заботится о сборе расширений и в том, чтобы убедиться, что расширения копируются в правильное расположение в папке выходных данных публикации (например, bin\Release\net6.0\publish
). Те же оптимизации (например, сжатие) применяются к файлам JavaScript, WASM и DLL так же, как Blazor применяется к другим файлам.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks/BundleBlazorAssets.cs
:
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks
{
public class BundleBlazorAssets : Task
{
[Required]
public ITaskItem[]? PublishBlazorBootStaticWebAsset { get; set; }
[Required]
public string? BundlePath { get; set; }
[Output]
public ITaskItem[]? Extension { get; set; }
public override bool Execute()
{
var bundle = new MultipartFormDataContent(
"--0a7e8441d64b4bf89086b85e59523b7d");
foreach (var asset in PublishBlazorBootStaticWebAsset)
{
var name = Path.GetFileName(asset.GetMetadata("RelativePath"));
var fileContents = File.OpenRead(asset.ItemSpec);
var content = new StreamContent(fileContents);
var disposition = new ContentDispositionHeaderValue("form-data");
disposition.Name = name;
disposition.FileName = name;
content.Headers.ContentDisposition = disposition;
var contentType = Path.GetExtension(name) switch
{
".js" => "text/javascript",
".wasm" => "application/wasm",
_ => "application/octet-stream"
};
content.Headers.ContentType =
MediaTypeHeaderValue.Parse(contentType);
bundle.Add(content);
}
using (var output = File.Open(BundlePath, FileMode.OpenOrCreate))
{
output.SetLength(0);
bundle.CopyToAsync(output).ConfigureAwait(false).GetAwaiter()
.GetResult();
output.Flush(true);
}
var bundleItem = new TaskItem(BundlePath);
bundleItem.SetMetadata("RelativePath", "app.bundle");
bundleItem.SetMetadata("ExtensionName", "multipart");
Extension = new ITaskItem[] { bundleItem };
return true;
}
}
}
Создание пакета NuGet для автоматического преобразования выходных данных публикации
Создайте пакет NuGet с целевыми объектами MSBuild, которые автоматически включаются при ссылке на пакет:
- Razor Создайте проект библиотеки классов (RCL).
- Создайте целевой файл после соглашений NuGet для автоматического импорта пакета в потребляющих проектах. Например, создайте
build\net6.0\{PACKAGE ID}.targets
, где{PACKAGE ID}
— это идентификатор пакета. - Соберите выходные данные из библиотеки классов, содержащей задачу MSBuild, и убедитесь, что выходные данные упакованы в нужное расположение.
- Добавьте необходимый код MSBuild для подключения к Blazor конвейеру и вызовите задачу MSBuild для создания пакета.
Подход, описанный в этом разделе, использует только пакет для доставки целевых объектов и содержимого, который отличается от большинства пакетов, в которых пакет включает библиотеку DLL.
Предупреждение
В примере пакета, описанном в этом разделе, показано, как настроить процесс публикации Blazor . Пример пакета NuGet предназначен только для использования в качестве локальной демонстрации. Использование этого пакета в рабочей среде не поддерживается.
Примечание.
Пакет NuGet для примеров в этой статье назван по аналогии с пакетом, предоставленным корпорацией Майкрософт, Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle
. Рекомендации по именованию и созданию собственного пакета NuGet см. в следующих статьях NuGet:
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.csproj
:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<NoWarn>NU5100</NoWarn>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Description>
Sample demonstration package showing how to customize the Blazor publish
process. Using this package in production is not supported!
</Description>
<IsPackable>true</IsPackable>
<IsShipping>true</IsShipping>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<None Update="build\**"
Pack="true"
PackagePath="%(Identity)" />
<Content Include="_._"
Pack="true"
PackagePath="lib\net6.0\_._" />
</ItemGroup>
<Target Name="GetTasksOutputDlls"
BeforeTargets="CoreCompile">
<MSBuild Projects="..\Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks\Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.csproj"
Targets="Publish;PublishItemsOutputGroup"
Properties="Configuration=Release">
<Output TaskParameter="TargetOutputs"
ItemName="_TasksProjectOutputs" />
</MSBuild>
<ItemGroup>
<Content Include="@(_TasksProjectOutputs)"
Condition="'%(_TasksProjectOutputs.Extension)' == '.dll'"
Pack="true"
PackagePath="tasks\%(_TasksProjectOutputs.TargetPath)"
KeepMetadata="Pack;PackagePath" />
</ItemGroup>
</Target>
</Project>
Примечание.
Свойство <NoWarn>NU5100</NoWarn>
в предыдущем примере подавляет предупреждение о сборках, помещенных в папку tasks
. Дополнительные сведения см. в разделе "Предупреждение NuGet NU5100".
.targets
Добавьте файл для подключения задачи MSBuild к конвейеру сборки. В этом файле выполняются следующие цели:
- Импортируйте задачу в процесс сборки. Обратите внимание, что путь к библиотеке DLL соответствует конечному расположению файла в пакете.
- Свойство
ComputeBlazorExtensionsDependsOn
присоединяет пользовательский целевой объект к конвейеру Blazor WebAssembly . - Захватите свойство в выводе задачи
Extension
и добавьте его вBlazorPublishExtension
для информирования Blazor о расширении. Вызов задачи в целевом объекте создает пакет. Список опубликованных файлов предоставляется конвейером Blazor WebAssembly вPublishBlazorBootStaticWebAsset
группе элементов. Путь к пакету определяется с помощьюIntermediateOutputPath
(обычно внутриobj
папки). В конечном счете пакет копируется автоматически в правильное расположение в выходной папке публикации (например,bin\Release\net6.0\publish
).
При ссылке на пакет создается комплект файлов Blazor во время публикации.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/build/net6.0/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.targets
:
<Project>
<UsingTask
TaskName="Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.BundleBlazorAssets"
AssemblyFile="$(MSBuildThisProjectFileDirectory)..\..\tasks\Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.dll" />
<PropertyGroup>
<ComputeBlazorExtensionsDependsOn>
$(ComputeBlazorExtensionsDependsOn);_BundleBlazorDlls
</ComputeBlazorExtensionsDependsOn>
</PropertyGroup>
<Target Name="_BundleBlazorDlls">
<BundleBlazorAssets
PublishBlazorBootStaticWebAsset="@(PublishBlazorBootStaticWebAsset)"
BundlePath="$(IntermediateOutputPath)bundle.multipart">
<Output TaskParameter="Extension"
ItemName="BlazorPublishExtension"/>
</BundleBlazorAssets>
</Target>
</Project>
Автоматическая загрузка Blazor из пакета
Пакет NuGet использует инициализаторы JavaScript (JS) для автоматической загрузки Blazor WebAssembly приложения из пакета вместо использования отдельных DLL-файлов. JS инициализаторы используются для изменения Blazorзагрузчика ресурсов загрузки и использования пакета.
Чтобы создать JS инициализатор, добавьте JS файл с именем {NAME}.lib.module.js
wwwroot
в папку проекта пакета, где {NAME}
заполнитель является идентификатором пакета. Например, файл для пакета Майкрософт называется Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js
. Экспортированные функции beforeWebAssemblyStart
и afterWebAssemblyStarted
обрабатывают загрузку.
Инициализаторы JS :
- Определите, доступно ли расширение публикации, проверив наличие
extensions.multipart
, которое соответствует имени расширения (ExtensionName
), указанного в разделе "Создание задачи MSBuild для настройки списка опубликованных файлов и определения новых расширений". - Скачайте пакет и синтаксический анализ содержимого в карту ресурсов с помощью URL-адресов созданных объектов.
- Обновите загрузчик ресурсов загрузки (
options.loadBootResource
) с помощью пользовательской функции, которая разрешает ресурсы с помощью URL-адресов объектов. - После запуска приложения отмените URL-адреса объектов, чтобы освободить память в
afterWebAssemblyStarted
функции.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/wwwroot/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js
:
const resources = new Map();
export async function beforeWebAssemblyStart(options, extensions) {
if (!extensions || !extensions.multipart) {
return;
}
try {
const integrity = extensions.multipart['app.bundle'];
const bundleResponse =
await fetch('app.bundle', { integrity: integrity, cache: 'no-cache' });
const bundleFromData = await bundleResponse.formData();
for (let value of bundleFromData.values()) {
resources.set(value, URL.createObjectURL(value));
}
options.loadBootResource = function (type, name, defaultUri, integrity) {
return resources.get(name) ?? null;
}
} catch (error) {
console.log(error);
}
}
export async function afterWebAssemblyStarted(blazor) {
for (const [_, url] of resources) {
URL.revokeObjectURL(url);
}
}
Чтобы создать JS инициализатор, добавьте файл JS с именем {NAME}.lib.module.js
в папку wwwroot
проекта пакета, где {NAME}
является заполнителем для идентификатора пакета. Например, файл для пакета Майкрософт называется Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js
. Экспортированные функции beforeStart
и afterStarted
обрабатывают загрузку.
Инициализаторы JS :
- Определите, доступно ли расширение публикации, проверив
extensions.multipart
имя расширения (ExtensionName
), указанное в задаче "Создание MSBuild", чтобы настроить список опубликованных файлов и определить новый раздел расширений . - Скачайте пакет и синтаксический анализ содержимого в карту ресурсов с помощью URL-адресов созданных объектов.
- Обновите загрузчик ресурсов загрузки (
options.loadBootResource
) с помощью пользовательской функции, которая распознает ресурсы с помощью URL-адресов объектов. - После запуска приложения отмените URL-адреса объектов, чтобы освободить память в
afterStarted
функции.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/wwwroot/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js
:
const resources = new Map();
export async function beforeStart(options, extensions) {
if (!extensions || !extensions.multipart) {
return;
}
try {
const integrity = extensions.multipart['app.bundle'];
const bundleResponse =
await fetch('app.bundle', { integrity: integrity, cache: 'no-cache' });
const bundleFromData = await bundleResponse.formData();
for (let value of bundleFromData.values()) {
resources.set(value, URL.createObjectURL(value));
}
options.loadBootResource = function (type, name, defaultUri, integrity) {
return resources.get(name) ?? null;
}
} catch (error) {
console.log(error);
}
}
export async function afterStarted(blazor) {
for (const [_, url] of resources) {
URL.revokeObjectURL(url);
}
}
Обслуживание пакета из приложения сервера узла
Из-за ограничений безопасности ASP.NET Core не обслуживает app.bundle
файл. Вспомогательное средство обработки запросов требуется для обслуживания файла при запросе клиентами.
Примечание.
Поскольку те же самые оптимизации прозрачно применяются к расширениям публикации, которые применяются к файлам приложения, файлы активов app.bundle.gz
и сжатые файлы app.bundle.br
автоматически создаются при публикации.
Поместите код C# в проект Server в Program.cs
непосредственно перед строкой, которая задает резервный файл в index.html
(app.MapFallbackToFile("index.html");
), чтобы ответить на запрос файла пакета (например, app.bundle
):
app.MapGet("app.bundle", (HttpContext context) =>
{
string? contentEncoding = null;
var contentType =
"multipart/form-data; boundary=\"--0a7e8441d64b4bf89086b85e59523b7d\"";
var fileName = "app.bundle";
var acceptEncodings = context.Request.Headers.AcceptEncoding;
if (Microsoft.Net.Http.Headers.StringWithQualityHeaderValue
.StringWithQualityHeaderValue
.TryParseList(acceptEncodings, out var encodings))
{
if (encodings.Any(e => e.Value == "br"))
{
contentEncoding = "br";
fileName += ".br";
}
else if (encodings.Any(e => e.Value == "gzip"))
{
contentEncoding = "gzip";
fileName += ".gz";
}
}
if (contentEncoding != null)
{
context.Response.Headers.ContentEncoding = contentEncoding;
}
return Results.File(
app.Environment.WebRootFileProvider.GetFileInfo(fileName)
.CreateReadStream(), contentType);
});
Тип контента соответствует типу, определенному ранее в задаче сборки. Конечная точка проверяет кодировки содержимого, принятые браузером, и служит оптимальным файлом, Brotli (.br
) или Gzip (.gz
).
ASP.NET Core