226 lines
8.5 KiB
Markdown
226 lines
8.5 KiB
Markdown
# EnvelopeGenerator.Server — Publish & Deployment Guide
|
||
|
||
## Inhaltsverzeichnis
|
||
|
||
1. [Unterschied zu einer normalen ASP.NET Core API](#unterschied-zu-einer-normalen-aspnet-core-api)
|
||
2. [Warum Self-Contained Publish?](#warum-self-contained-publish)
|
||
3. [Publish-Befehl (Terminal)](#publish-befehl-terminal)
|
||
4. [IIS-Konfiguration](#iis-konfiguration)
|
||
5. [Verzeichnisstruktur nach dem Publish](#verzeichnisstruktur-nach-dem-publish)
|
||
6. [Häufige Fehler](#häufige-fehler)
|
||
|
||
---
|
||
|
||
## Unterschied zu einer normalen ASP.NET Core API
|
||
|
||
`EnvelopeGenerator.Server` ist **keine** gewöhnliche ASP.NET Core Web API. Es handelt sich um eine **Blazor Auto (Server + WebAssembly Hybrid)**-Anwendung.
|
||
|
||
| Merkmal | Normale ASP.NET Core API | EnvelopeGenerator.Server (Blazor Auto) |
|
||
|---|---|---|
|
||
| Projekttyp | `Microsoft.NET.Sdk.Web` | `Microsoft.NET.Sdk.Web` + WASM Client |
|
||
| Frontend | Keins / Razor Pages | Blazor Server + Blazor WASM |
|
||
| WASM-Komponente | Nein | Ja (`EnvelopeGenerator.Server.Client`) |
|
||
| Framework-DLL-Bindung | Tolerant gegenüber Runtime-Versionen | **Strikt**: WASM erwartet exakte Assembly-Versionen |
|
||
| Publish ohne .NET auf Zielserver | Nicht nötig (FDD reicht meist) | **Self-Contained Pflicht** empfohlen |
|
||
| IIS Application Pool | `.NET CLR v4.0` oder `No Managed Code` | **Zwingend: `No Managed Code`** |
|
||
| Publish-Paketgröße | ~5–20 MB | **~500 MB** (enthält .NET Runtime) |
|
||
| `web.config processPath` | `dotnet` + `.dll` | **`.\EnvelopeGenerator.Server.exe`** |
|
||
|
||
### Warum die WASM-Komponente den Unterschied macht
|
||
|
||
Die WASM-Seite der Anwendung (`EnvelopeGenerator.Server.Client`) bindet Assemblies wie
|
||
`Microsoft.Extensions.DependencyInjection.Abstractions` in einer **fest definierten Version**.
|
||
Bei einem **Framework-Dependent Deployment** werden diese Assemblies nicht mitgeliefert und
|
||
müssen auf dem Zielserver vorhanden sein — in der exakt passenden Version.
|
||
|
||
Fehlt die passende .NET-Runtime auf dem Zielserver, erscheint folgender Fehler beim Start:
|
||
|
||
```
|
||
Unhandled exception. System.IO.FileNotFoundException:
|
||
Could not load file or assembly
|
||
'Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0'
|
||
```
|
||
|
||
---
|
||
|
||
## Warum Self-Contained Publish?
|
||
|
||
Beim **Self-Contained Deployment** werden **alle benötigten .NET Runtime-DLLs** in das
|
||
Ausgabeverzeichnis kopiert. Die Anwendung ist damit vollständig unabhängig von der auf dem
|
||
Zielserver installierten .NET-Version.
|
||
|
||
| | Framework-Dependent | Self-Contained |
|
||
|---|---|---|
|
||
| .NET auf Zielserver nötig | Ja | **Nein** |
|
||
| Paketgröße | ~20 MB | ~500 MB |
|
||
| `runtimeconfig.json` | `frameworkVersion` vorhanden | `includedFrameworks` (Runtime eingebettet) |
|
||
| Fehleranfälligkeit auf Fremd-PC | Hoch | Minimal |
|
||
|
||
---
|
||
|
||
## Publish-Befehl (Terminal)
|
||
|
||
### Empfohlener Befehl (Self-Contained, win-x64)
|
||
|
||
```bat
|
||
dotnet publish EnvelopeGenerator.Server\EnvelopeGenerator.Server\EnvelopeGenerator.Server.csproj ^
|
||
-c Release ^
|
||
-f net8.0 ^
|
||
--self-contained true ^
|
||
--runtime win-x64 ^
|
||
-o .\publish-output
|
||
```
|
||
|
||
> Dieser Befehl muss vom **Solution-Root-Verzeichnis** aus ausgefuehrt werden.
|
||
> Alternativ: `publish.bat` im selben Verzeichnis wie diese README ausfuehren.
|
||
|
||
### Parameter-Erklaerung
|
||
|
||
| Parameter | Bedeutung |
|
||
|---|---|
|
||
| `-c Release` | Release-Konfiguration (optimiert, kein Debug-Code) |
|
||
| `-f net8.0` | Ziel-Framework explizit angeben (Pflicht, da `<TargetFrameworks>` mehrere Werte haben kann) |
|
||
| `--self-contained true` | Alle .NET Runtime-DLLs ins Ausgabeverzeichnis kopieren |
|
||
| `--runtime win-x64` | Zielplattform: Windows 64-Bit |
|
||
| `-o .\publish-output` | Ausgabeverzeichnis |
|
||
|
||
### Überprüfung nach dem Publish
|
||
|
||
Nach erfolgreichem Publish folgende Dateien im Ausgabeverzeichnis prüfen:
|
||
|
||
```powershell
|
||
# Diese Dateien MÜSSEN vorhanden sein (Self-Contained-Nachweis):
|
||
Test-Path ".\publish-output\hostfxr.dll" # .NET Host
|
||
Test-Path ".\publish-output\coreclr.dll" # .NET Core Runtime
|
||
Test-Path ".\publish-output\Microsoft.Extensions.DependencyInjection.Abstractions.dll"
|
||
Test-Path ".\publish-output\EnvelopeGenerator.Server.exe"
|
||
Test-Path ".\publish-output\web.config"
|
||
```
|
||
|
||
Alle Ergebnisse müssen `True` sein.
|
||
|
||
---
|
||
|
||
## IIS-Konfiguration
|
||
|
||
> **WICHTIG:** Diese Einstellungen unterscheiden sich von einer normalen ASP.NET Core API
|
||
> und sind zwingend erforderlich.
|
||
|
||
### 1. Application Pool — `No Managed Code`
|
||
|
||
ASP.NET Core (und damit auch Blazor) verwaltet seinen eigenen Runtime-Lifecycle.
|
||
IIS darf **keinen** .NET CLR-Managed-Code-Kontext aktivieren.
|
||
|
||
**Einstellung:**
|
||
|
||
```
|
||
IIS Manager
|
||
→ Application Pools
|
||
→ [Pool-Name der Anwendung] → Basic Settings
|
||
→ .NET CLR Version: "No Managed Code" ← ZWINGEND
|
||
```
|
||
|
||
> **Fehler bei falscher Einstellung:** HTTP 500.30 — ASP.NET Core app failed to start
|
||
> (sc-win32-status: 574 in IIS-Logs)
|
||
|
||
### 2. ASP.NET Core Module V2
|
||
|
||
Das IIS-Modul `AspNetCoreModuleV2` muss installiert sein.
|
||
Es wird über das **.NET Hosting Bundle** mitgeliefert.
|
||
|
||
Prüfen:
|
||
```
|
||
IIS Manager → Modules → "AspNetCoreModuleV2" vorhanden?
|
||
```
|
||
|
||
Falls nicht installiert: [.NET 8 Hosting Bundle herunterladen](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
||
und nach der Installation IIS neu starten:
|
||
|
||
```cmd
|
||
net stop was /y && net start w3svc
|
||
```
|
||
|
||
### 3. web.config — Korrekte Konfiguration für Self-Contained
|
||
|
||
Nach dem Publish wird `web.config` automatisch generiert. Für Self-Contained muss sie so aussehen:
|
||
|
||
```xml
|
||
<?xml version="1.0" encoding="utf-8"?>
|
||
<configuration>
|
||
<location path="." inheritInChildApplications="false">
|
||
<system.webServer>
|
||
<handlers>
|
||
<add name="aspNetCore" path="*" verb="*"
|
||
modules="AspNetCoreModuleV2" resourceType="Unspecified" />
|
||
</handlers>
|
||
<aspNetCore processPath=".\EnvelopeGenerator.Server.exe"
|
||
stdoutLogEnabled="false"
|
||
stdoutLogFile=".\logs\stdout"
|
||
hostingModel="inprocess" />
|
||
</system.webServer>
|
||
</location>
|
||
</configuration>
|
||
```
|
||
|
||
**Kritische Unterschiede zu Framework-Dependent:**
|
||
|
||
| Eigenschaft | Framework-Dependent (FALSCH) | Self-Contained (RICHTIG) |
|
||
|---|---|---|
|
||
| `processPath` | `dotnet` | `.\EnvelopeGenerator.Server.exe` |
|
||
| `arguments` | `.\EnvelopeGenerator.Server.dll` | *(leer oder weggelassen)* |
|
||
|
||
### 4. Berechtigungen für das `logs`-Verzeichnis
|
||
|
||
Wenn `stdoutLogEnabled="true"` gesetzt wird (zur Fehlerdiagnose), muss das `logs`-Verzeichnis
|
||
existieren und der IIS-Prozess muss Schreibrechte haben:
|
||
|
||
```powershell
|
||
New-Item -ItemType Directory -Path "C:\inetpub\wwwroot\<App-Pfad>\logs" -Force
|
||
icacls "C:\inetpub\wwwroot\<App-Pfad>\logs" /grant "IIS_IUSRS:(OI)(CI)F"
|
||
```
|
||
|
||
> Ohne dieses Verzeichnis kann die Anwendung bei aktiviertem Logging **nicht starten**.
|
||
|
||
### 5. Application Pool Recycle nach Deployment
|
||
|
||
Nach jedem Deployment den Application Pool neu starten:
|
||
|
||
```cmd
|
||
# IIS Manager → Application Pools → [Pool] → Recycle
|
||
# oder per Kommandozeile (als Administrator):
|
||
%windir%\system32\inetsrv\appcmd recycle apppool /apppool.name:"<Pool-Name>"
|
||
```
|
||
|
||
---
|
||
|
||
## Verzeichnisstruktur nach dem Publish
|
||
|
||
```
|
||
publish-output\
|
||
├── EnvelopeGenerator.Server.exe ← Startpunkt (Self-Contained)
|
||
├── EnvelopeGenerator.Server.dll ← Managed Assembly
|
||
├── EnvelopeGenerator.Server.runtimeconfig.json
|
||
├── EnvelopeGenerator.Server.deps.json
|
||
├── web.config ← IIS-Konfiguration (auto-generiert)
|
||
├── hostfxr.dll ← .NET Host (Self-Contained-Nachweis)
|
||
├── coreclr.dll ← .NET Core Runtime
|
||
├── Microsoft.Extensions.*.dll ← Framework-DLLs (jetzt enthalten!)
|
||
├── DevExpress.*.dll ← UI-Komponenten
|
||
├── wwwroot\ ← Statische Web-Assets
|
||
│ ├── _framework\ ← WASM-Binaries
|
||
│ └── ...
|
||
└── logs\ ← Stdout-Logs (manuell anlegen!)
|
||
```
|
||
|
||
---
|
||
|
||
## Häufige Fehler
|
||
|
||
| Fehler | Ursache | Lösung |
|
||
|---|---|---|
|
||
| `FileNotFoundException: Microsoft.Extensions.DependencyInjection.Abstractions` | Framework-Dependent Publish auf Server ohne .NET 8 | Self-Contained Publish verwenden |
|
||
| `HTTP 500.30` in IIS | App startet nicht | Application Pool auf `No Managed Code` setzen |
|
||
| `HTTP 500.30` + `sc-win32-status: 574` | App Pool falsch oder `AspNetCoreModuleV2` fehlt | Pool prüfen + Hosting Bundle installieren |
|
||
| App startet per `.exe`, aber nicht in IIS | `web.config` hat noch `processPath="dotnet"` | `web.config` auf `.\EnvelopeGenerator.Server.exe` korrigieren |
|
||
| Logs-Verzeichnis fehlt → App startet nicht | `stdoutLogEnabled="true"` aber `logs\` existiert nicht | `logs\`-Ordner anlegen + IIS_IUSRS Schreibrecht geben |
|