Add Publish & Deployment Guide to README.md
Added a detailed "Publish & Deployment Guide" to the README for the `EnvelopeGenerator.Server` project. The guide includes: - Table of contents for easy navigation. - Explanation of differences between `EnvelopeGenerator.Server` and a standard ASP.NET Core API. - Justification for self-contained publishing and its benefits. - Recommended `dotnet publish` command with parameter explanations. - IIS configuration steps, including Application Pool settings, `web.config` adjustments, and module requirements. - Verification checklist for published output files. - Instructions for setting up logging and recycling the IIS Application Pool. - Directory structure overview post-publish. - Troubleshooting common deployment errors with solutions. This guide ensures developers and administrators can successfully publish and deploy the application.
This commit is contained in:
225
EnvelopeGenerator.Server/EnvelopeGenerator.Server/README.md
Normal file
225
EnvelopeGenerator.Server/EnvelopeGenerator.Server/README.md
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
# 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 |
|
||||||
|
|
||||||
|
### Doğrulama 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 |
|
||||||
Reference in New Issue
Block a user