Blazor WASM/PWA - Configurazione AppSettings per più clienti
Obiettivo
Gestire pubblicazioni diverse della stessa app Blazor WebAssembly/PWA per più clienti, evitando di modificare manualmente appsettings.json dopo la pubblicazione.
Questo è importante perché nelleNelle app Blazor WASM/PWA i file pubblicati vengono inseritigestiti nelanche dal service worker e dal manifest degli asset, ad esempio:asset:
service-worker-assets.js
Se si modifica manualmente un file statico dopo la pubblicazione, comead esempio appsettings.json, gli hashsi possono noncreare corrispondere più e la PWA può generare erroriproblemi di integrità/cache.cache, integrità o caricamento della PWA.
La soluzione corretta è:
1. mantenere un appsettings.json comune
2. creare un file appsettings per ogni cliente
3. tenere i file cliente fuori da wwwroot
4. usare un profilo di pubblicazione per cliente
5. fare copiare al .csproj solo il file del cliente pubblicato
1. Struttura cartelle consigliata
Nel progetto Blazor WASM, ad esempio Soluziona.Apps.Magazzino, usare questa struttura:
Soluziona.Apps.Magazzino
├── SettingClienti
│ ├── Comside
│ │ └── appsettings.Comside.json
│ └── Demo
│ └── appsettings.Demo.json
│
├── Properties
│ └── PublishProfiles
│ ├── Comside.pubxml
│ └── Demo.pubxml
│
└── wwwroot
├── appsettings.json
├── appsettings.Development.json
├── index.html
├── manifest.webmanifest
├── service-worker.js
└── ...
Regola
Dentro wwwroot devono rimanere solo:
appsettings.json
appsettings.Development.json
I file dei clienti devono stare fuori da wwwroot, dentro:
SettingClienti\<NomeCliente>\appsettings.<NomeCliente>.json
Esempio:
SettingClienti\Comside\appsettings.Comside.json
SettingClienti\Demo\appsettings.Demo.json
Non mettere dentro wwwroot:
appsettings.Comside.json
appsettings.Demo.json
altrimenti rischiano di essere pubblicati tutti insieme.
2. appsettings.json comune
Il file:
wwwroot/wwwroot\appsettings.json
deve contenere solo i valori comuni a tutti i clienti.
Esempio:
{
"App": {
"NomeApp": "Liste di Carico",
"SottoTitoloApp": "Gestione preparazione ordini",
"Versione": "1.0.6",
"Build": "2026.09.09.006",
"Ambiente": "Production",
"PaginaIniziale": "/calendario"
},
"Api": {
"TimeoutSecondi": 30
},
"Offline": {},
"SyncListeDiCarico": {
"IntervalloSecondi": 30,
"GiorniIndietro": 15,
"GiorniAvanti": 15,
"EseguiAllAvvio": true
}
}
Nota
In questo file può mancare:
"BaseUrl"
perché viene valorizzato dal file cliente, ad esempio:
appsettings.Comside.json
A runtime Blazor caricherà:
appsettings.json
+
appsettings.Comside.json
3. appsettings.Development.json
Il file:
wwwroot/wwwroot\appsettings.Development.json
viene usato per lo sviluppo locale.
Esempio:
{
"App": {
"IdApplicazione": "17630f17-7626-416f-961b-486fbd64fd4f",
"CodiceApplicazione": "APPLISTEDICARICO",
"CodiceAzienda": "3",
"Ambiente": "Development"
},
"Api": {
"BaseUrl": "https://localhost:7021/",
"TimeoutSecondi": 30
}
}
Questo file non deve essere pubblicato in produzione.
4. SettingsAppSettings per cliente
Demo
Percorso:
SettingClienti\Demo\appsettings.Demo.json
Contenuto:
{
"App": {
"IdApplicazione": "17630f17-7626-416f-961b-486fbd64fd4f",
"CodiceApplicazione": "APPLISTEDICARICO",
"CodiceAzienda": "3",
"Ambiente": "Production"
},
"Api": {
"BaseUrl": "https://soluzionacoreapi.soluziona.cloud:443/",
"TimeoutSecondi": 30
}
}
Comside
Percorso:
SettingClienti\Comside\appsettings.Comside.json
Contenuto:
{
"App": {
"IdApplicazione": "90208117-20b4-446c-a771-42bce3fb63c5",
"CodiceApplicazione": "ListeDiCarico",
"CodiceAzienda": "59",
"Ambiente": "Production"
},
"Api": {
"BaseUrl": "https://192.168.232.122:4433/",
"TimeoutSecondi": 30
}
}
5. Modifica del .csproj
Aprire il file .csproj del progetto Blazor WASM, ad esempio:
Soluziona.Apps.Magazzino.csproj
5.1 Escludere SettingClienti dagli item automatici
Nel primo PropertyGroup aggiungere:
<DefaultItemExcludes>$(DefaultItemExcludes);SettingClienti\**\*</DefaultItemExcludes>
Esempio:
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
<!-- Esclude i file cliente dagli item automatici del .NET SDK -->
<DefaultItemExcludes>$(DefaultItemExcludes);SettingClienti\**\*</DefaultItemExcludes>
</PropertyGroup>
Questa riga impedisce al SDK Blazor di trattare automaticamente i file cliente come asset statici.
5.2 Non usare Content Include diretto
Non usare questa forma:
<Content Include="SettingClienti\Comside\appsettings.Comside.json" />
perché può generare errori del tipo:
More than one compatible asset found for target path 'appsettings.Comside#[.{fingerprint}]?.json'
5.3 Aggiungere il Target di pubblicazione
Aggiungere questo blocco prima di:
</Project>
<ItemGroup>Target <NoneName="AggiungiAppSettingsClienteAllaPubblicazione"
Include=BeforeTargets="SettingClienti\**\*.json" />
</ItemGroup>
<ItemGroupComputeFilesToPublish"
Condition="'$(WasmApplicationEnvironmentName)' != ''
and '$(WasmApplicationEnvironmentName)' != 'Development'
and Exists('$(MSBuildProjectDirectory)\SettingClienti\$(WasmApplicationEnvironmentName)\appsettings.$(WasmApplicationEnvironmentName).json')">
<ContentMessage Include=Importance="High"
Text="Pubblico appsettings cliente: SettingClienti\$(WasmApplicationEnvironmentName)\appsettings.$(WasmApplicationEnvironmentName).json" Link=/>
<ItemGroup>
<ResolvedFileToPublish Include="$(MSBuildProjectDirectory)\SettingClienti\$(WasmApplicationEnvironmentName)\appsettings.$(WasmApplicationEnvironmentName).json">
<RelativePath>wwwroot\appsettings.$(WasmApplicationEnvironmentName).json"json</RelativePath>
CopyToOutputDirectory="PreserveNewest"<CopyToPublishDirectory>Always</CopyToPublishDirectory>
CopyToPublishDirectory="PreserveNewest" </ResolvedFileToPublish>
</ItemGroup>
</Target>
Cosa
fa6. questo.csproj blocco
completo .csproj Quandodi si pubblica con ambiente:
Comside<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
<!-- Esclude i file cliente dagli item automatici del .NET SDK -->
<DefaultItemExcludes>$(DefaultItemExcludes);SettingClienti\**\*</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DevExpress.Blazor" Version="26.1.4" />
<PackageReference Include="DevExpress.Blazor.Themes" Version="26.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.11" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.11" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Soluziona.Apps.UI\Soluziona.Apps.UI.csproj" />
<ProjectReference Include="..\Soluziona.Apps.Utility\Soluziona.Apps.Core.csproj" />
<ProjectReference Include="..\SoluzionaApps.ApiClient\Soluziona.Apps.ApiClient.csproj" />
</ItemGroup>
<ItemGroup>
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\web.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\appsettings.Development.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
<Target Name="AggiungiAppSettingsClienteAllaPubblicazione"
BeforeTargets="ComputeFilesToPublish"
Condition="'$(WasmApplicationEnvironmentName)' != ''
and '$(WasmApplicationEnvironmentName)' != 'Development'
and Exists('$(MSBuildProjectDirectory)\SettingClienti\$(WasmApplicationEnvironmentName)\appsettings.$(WasmApplicationEnvironmentName).json')">
<Message Importance="High"
Text="Pubblico appsettings cliente: SettingClienti\$(WasmApplicationEnvironmentName)\appsettings.$(WasmApplicationEnvironmentName).json" />
<ItemGroup>
<ResolvedFileToPublish Include="$(MSBuildProjectDirectory)\SettingClienti\$(WasmApplicationEnvironmentName)\appsettings.$(WasmApplicationEnvironmentName).json">
<RelativePath>wwwroot\appsettings.$(WasmApplicationEnvironmentName).json</RelativePath>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
il .csproj prende:
SettingClienti\Comside\appsettings.Comside.jsone lo pubblica come:
wwwroot\appsettings.Comside.jsonQuando si pubblica con ambiente:
Demoil .csproj prende:
SettingClienti\Demo\appsettings.Demo.jsone lo pubblica come:
wwwroot\appsettings.Demo.json6.7. Profili di pubblicazione
I profili vanno creati da Visual Studio con:
Tasto destro sul progetto
→ Publish
→ New profile
→ Folder
Dopo la creazione, modificare i file .pubxml.
Profilo Demo
Percorso:
Properties\PublishProfiles\Demo.pubxml
Contenuto:
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>C:\Repos\Pubblicati\AppListeDiCarico\Demo</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<WasmApplicationEnvironmentName>Demo</WasmApplicationEnvironmentName>
</PropertyGroup>
</Project>
Profilo Comside
Percorso:
Properties\PublishProfiles\Comside.pubxml
Contenuto:
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<DeleteExistingFiles>true</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>C:\Repos\Pubblicati\AppListeDiCarico\Comside</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<WasmApplicationEnvironmentName>Comside</WasmApplicationEnvironmentName>
</PropertyGroup>
</Project>
7.8. Proprietà fondamentale: WasmApplicationEnvironmentName
La riga più importante del profilo è questa:
<WasmApplicationEnvironmentName>Comside</WasmApplicationEnvironmentName>
oppure:
<WasmApplicationEnvironmentName>Demo</WasmApplicationEnvironmentName>
Questa proprietà decide quale file ambiente Blazor deve caricare.usare.
Esempio:
WasmApplicationEnvironmentName = Comside
→ carica appsettings.json
→ carica appsettings.Comside.json
WasmApplicationEnvironmentName = Demo
→ carica appsettings.json
→ carica appsettings.Demo.json
Il valore deve combaciare esattamente con:
nome cartella cliente
nome file appsettings
Esempio corretto:
Comside
→ SettingClienti\Comside\appsettings.Comside.json
Esempio errato:
COMSIDE
→ SettingClienti\Comside\appsettings.Comside.json
9. Nota su App:Ambiente
Dentro gli appsettings cliente si può lasciare:
"Ambiente": "Production"
Questo valore è una configurazione applicativa interna.
Non decide quale file appsettings.*.json viene caricato.
Il file caricato viene deciso da:
<WasmApplicationEnvironmentName>Comside</WasmApplicationEnvironmentName>
non da:
"Ambiente": "Production"