Сборка пакета .portalpart
WebPart пишутся на C# и упаковываются в ZIP-архив .portalpart (manifest + DLL + CSS + опционально JS).
Настройка SDK: Extension SDK с korport.ru/developers.
Быстрый старт
Заголовок раздела «Быстрый старт»Новая WebPart — из шаблона SDK:
# После распаковки portal-sdk-X.Y.Z.zipdotnet new install ./portal-sdk-1.0.0/templates/portal-webpartdotnet new portal-webpart -n MyWidget -o ./my-widgetОтредактируйте manifest.json (см. manifest.md), C#-класс, при необходимости Templates/ и assets/main.css / assets/main.js, затем соберите пакет:
Linux / macOS / Git Bash:
./portal-sdk-1.0.0/scripts/pack-portalpart.sh ./my-widget# → my-widget/dist/my-widget.portalpartWindows (PowerShell):
.\portal-sdk-1.0.0\scripts\pack-portalpart.ps1 .\my-widgetСкрипты pack-portalpart.sh / pack-portalpart.ps1 выполняют dotnet publish и упаковку ZIP (без Docker и без исходников Portal). По умолчанию Release; для отладки с PDB: CONFIGURATION=Debug / -Configuration Debug — см. Отладка расширений.
Чтобы только проверить упаковку на готовом демо из SDK: examples/hello-widget + тот же pack-portalpart.
Файл .csproj — что менять
Заголовок раздела «Файл .csproj — что менять»Шаблон dotnet new portal-webpart -n MyWidget уже подставляет согласованные имена. Обычно .csproj трогать не нужно — достаточно править manifest.json, C#-класс и шаблоны HTML.
| Параметр | Менять? | Зачем |
|---|---|---|
TargetFramework (net10.0) | Нет | Должен совпадать с Portal |
Nullable / ImplicitUsings | По желанию | Стиль проекта, на установку не влияет |
AssemblyName | Только при переименовании DLL | Имя выходной DLL → должно совпадать с entry в manifest (dist/{AssemblyName}.dll) |
RootNamespace | Только при смене namespace | Namespace классов → вместе с именем класса даёт entryType |
PackageReference Portal.WebPart.Sdk Version | Да, при обновлении Portal | Версия SDK = версия Portal |
EmbeddedResource Templates\*.html | Оставить, если есть HTML-шаблоны | Встраивает Templates/*.html в DLL |
Связка с манифестом (пример для -n MyWidget):
.csproj / код | manifest.json |
|---|---|
AssemblyName = MyWidget | "entry": "dist/MyWidget.dll" |
RootNamespace + класс = Contoso.MyWidget.MyWidgetHandler | "entryType": "Contoso.MyWidget.MyWidgetHandler" |
Если меняете AssemblyName, namespace или имя класса — синхронно обновите entry / entryType в manifest.json. Иначе пакет установится, но WebPart не загрузится.
id в manifest (contoso.my-widget) — отдельный ключ каталога; он не обязан совпадать с AssemblyName.
Структура пакета
Заголовок раздела «Структура пакета»Исходники:
my-widget/├── Templates/│ └── widget.html ← embedded в DLL (см. ui-and-api.md)├── MyWidgetHandler.cs├── MyWidget.csproj├── manifest.json└── assets/ ← опционально ├── main.css ← → dist/main.css в ZIP └── main.js ← опциональноАрхив .portalpart (ZIP) после pack-portalpart:
my-widget.portalpart (ZIP)├── manifest.json└── dist/ ├── MyWidget.dll ├── main.v1.0.0.css ← версия из manifest └── main.v1.0.0.js ← опциональноУстановка: Админка → WebPart → загрузить файл.
Обновление WebPart после изменений в коде
Заголовок раздела «Обновление WebPart после изменений в коде»Изменения в исходниках (*.cs, assets/main.css, assets/main.js) не попадают в портал автоматически. Работающий экземпляр берёт DLL, CSS и JS из установленного пакета в хранилище (S3).
Цикл обновления
Заголовок раздела «Цикл обновления»- Внесите изменения в C#-код и/или
assets/main.css,assets/main.js. - Увеличьте версию в
manifest.json(semver, например1.0.1→1.0.2). Это важно для сброса кэша CSS/JS (см. ниже). - Соберите пакет скриптом из SDK (рядом с распакованным
portal-sdk-…):Окно терминала ./portal-sdk-1.0.0/scripts/pack-portalpart.sh ./my-widgetОкно терминала .\portal-sdk-1.0.0\scripts\pack-portalpart.ps1 .\my-widget - Переустановите
.portalpart:- Админка → WebPart — загрузите ZIP с тем же
id(старый пакет будет заменён); - или POST
/api/v1/webparts/packages(multipart, полеpackage, права администратора).
- Админка → WebPart — загрузите ZIP с тем же
- Обновите страницу в браузере с очисткой кэша (
Cmd+Shift+R/Ctrl+Shift+R) или откройте страницу в новой вкладке.
Повторная загрузка с тем же id в manifest.json удаляет предыдущий пакет, выгружает старую DLL из памяти API и регистрирует новую версию. Настройки WebPart на страницах (Property Pane) сохраняются — меняется только код и ассеты.
Что обновляется при переустановке
Заголовок раздела «Что обновляется при переустановке»| Компонент | Где хранится | Поведение при переустановке |
|---|---|---|
| C# / HTML (SSR) | dist/*.dll в S3 | Новая DLL подхватывается при следующем render/action |
| CSS | dist/main.v{version}.css в S3 | Новый путь + ?v=… в URL definition |
| JS | dist/main.v{version}.js в S3 | То же; bind вызывается заново после render |
Кэш CSS/JS (автоматически)
Заголовок раздела «Кэш CSS/JS (автоматически)»При сборке pack-portalpart.sh / .ps1 вызывает stage-versioned-assets (версионирование имён CSS/JS):
- Переименовывает
dist/main.css→dist/main.v{version}.css(и JS аналогично). - Обновляет
styles/scriptsвmanifest.jsonвнутри ZIP.
При установке пакета backend дополнительно добавляет ?v={version} к URL в definition:
/api/v1/webparts/assets/demo.my-widget/dist/main.v1.0.2.css?v=1.0.2Фронтенд (webPartLoader.js) кэширует ассеты по (manifestKey, pathname, version) и снимает старые <style> / <script> при смене версии. Ответ API для URL с ?v= — Cache-Control: public, max-age=31536000, immutable.
В исходниках в manifest.json указывайте обычные имена (dist/main.css); файлы держите в assets/. Версионирование имён — только на этапе pack-portalpart.
Кэш и типичные проблемы
Заголовок раздела «Кэш и типичные проблемы»«Изменил код, на странице старый вид»
- Пакет не переустановлен — выполните шаги 3–4 выше.
- Браузер закэшировал CSS — жёсткое обновление страницы.
- Версия в
manifest.jsonне изменилась — URL ассетов остаётся прежним; увеличьтеversion.
«Работает HTML, но не стили / не JS»
- Проверьте, что
styles/scriptsуказаны вmanifest.jsonи файлы попали в ZIP (unzip -l *.portalpart). - CSS/JS должны быть в пакете (
assets/→dist/при сборке), не вfrontend/public/.
«После обновления JS не срабатывает»
- Ключ в
PortalWebPartClients[…]должен совпадать сidв манифесте. bindвызывается после заменыinnerHTML— не вешайте обработчики только при первой загрузке страницы.