Analyse-Seite
Meldet ein Anwender ein Problem, fehlen dem Support meist genau die Angaben, die niemand auswendig
kennt: Browser und Version, Betriebssystem, Bildschirmauflösung, Zeitzone, gesetzte Cookies und
Storage-Schlüssel, aufgetretene JavaScript-Fehler. Das Paket FMH.Komponente.Analyse
liefert dafür eine fertige Seite unter /Analyse, die all das sammelt und über einen
Knopf als Text in die Zwischenablage legt.
Anders als die übrigen Bausteine dieser Dokumentation wird hier nichts in eine eigene View
eingebaut — die Seite kommt mit dem Paket mit. Zu tun ist allein die Paketreferenz auf
FMH.Komponente.Analyse aus dem FMH-Feed und, je nach Sicherheitsrichtlinie der
Anwendung, eine Anpassung der Header.
Die Seite unter /Analyse
Der Rahmen zeigt die echte Seite des Pakets — dieselbe Adresse, nur ohne Navigation und Fusszeile. Die Angaben stammen aus diesem Browser, der Speedtest laeuft beim Oeffnen automatisch.
<!-- Die Komponente kommt als NuGet-Paket. Version einsetzen oder
kuerzer: dotnet add package FMH.Komponente.Analyse -->
<ItemGroup>
<PackageReference Include="FMH.Komponente.Analyse" Version="..." />
</ItemGroup>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="Fmh_Framework_NuGet_Feed"
value="https://pkgs.dev.azure.com/fmhdev/_packaging/Fmh_Framework_NuGet_Feed/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="Fmh_Framework_NuGet_Feed">
<package pattern="FMH.*" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
<?xml version="1.0"?>
<package>
<metadata>
<id>FMH.Komponente.Analyse</id>
<version>$version$</version>
<title>FMH.Komponente.Analyse</title>
<authors>FMH/SIWF</authors>
<owners>FMH</owners>
<licenseUrl>http://fmh.ch</licenseUrl>
<projectUrl>https://fmhdev.visualstudio.com/Framework/_git/WebKomponentenV3</projectUrl>
<iconUrl>http://fmh.ch</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Komponente um Informationen über den Browser, etc. zu erhalten, für spätere Fehlerynalyse</description>
<releaseNotes>Initiales Package für FMH Komponente Analyse</releaseNotes>
<copyright>Copyright © 2025</copyright>
<tags>fmh komponente analyse</tags>
<dependencies>
<group targetFramework="net8.0">
<dependency id="serilog" version="4.4.0" />
<dependency id="MyCSharp.HttpUserAgentParser.AspNetCore" version="3.1.6" />
<dependency id="databinding.WebSecurity.Headers.Attributes" version="2026.7.22.1" />
</group>
</dependencies>
</metadata>
<files>
<file src="bin\Release\net10.0\FMH.Komponente.Analyse.dll" target="lib\net10.0" />
</files>
</package>
// Die Seite nutzt Inline-JavaScript und fragt die oeffentliche IP bei api.ipify.org ab.
// Ohne AddUnsafeInlineScriptSrc() blockiert die Richtlinie das Skript; die Ausnahme fuer
// ipify bringt die Action des Pakets als [HeaderSecurityCspDirective] selbst mit.
var headerCollectionBuilder = new HeaderCollectionBuilder().AddConnectSrcLocalhost()
.AddUnsafeInlineScriptSrc();
app.UseHeaderSecurity(headerCollectionBuilder.Build());
// Mehr braucht es nicht: Controller und View des Pakets werden als ApplicationPart
// automatisch gefunden, die Adresse /Analyse entsteht aus der Standardroute.
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
using databinding.WebSecurity.Headers.Attributes;
using FMH.Komponente.Analyse.Models;
using Microsoft.AspNetCore.Mvc;
using MyCSharp.HttpUserAgentParser;
using Serilog;
namespace FMH.Komponente.Analyse.Controllers
{
public class AnalyseController : Controller
{
[HeaderSecurityCspDirective(CspDirective.ConnectSrc, "https://api.ipify.org")]
public IActionResult Index()
{
Log.Information("Benutzer ruft Seite Analyse auf");
var userAgent = this.Request.Headers["User-Agent"]
.ToString();
var parsed = HttpUserAgentParser.Parse(userAgent);
var model = new BrowserInfoViewModel
{
UserAgent = userAgent,
Browser = parsed.Name,
OS = parsed.Platform.HasValue ? parsed.Platform.Value.Name : "-",
BrowserVersion = parsed.Version,
Type = $"{parsed.Type}",
Device = string.IsNullOrEmpty(parsed.MobileDeviceType)
? "-"
: $"{parsed.MobileDeviceType}".Trim()
};
return this.View(model);
}
[HttpGet]
[Route("/analyse/speedtest-image")]
public IActionResult GetSpeedtestImage()
{
var assembly = typeof(AnalyseController).Assembly;
var resourceName = "FMH.Komponente.Analyse.Resources.speedtest.png";
var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null)
{
return this.NotFound();
}
return this.File(stream, "image/png");
}
}
}
Darstellung
Das Paket FMH.Komponente.Analyse bringt eine fertige Seite unter /Analyse mit. Sie richtet sich an den Anwender im Störungsfall: Er ruft sie auf, drückt «Alle Informationen kopieren» und schickt den Text an den Support. Damit liegen dort auf einen Schlag die Angaben vor, nach denen man sonst einzeln fragen muss.
Einbinden
Die Komponente wird als NuGet-Paket bezogen, nicht als Projektreferenz. Paket-Id ist FMH.Komponente.Analyse (siehe Tab «Paket (nuspec)»):
dotnet add package FMH.Komponente.Analyse
oder von Hand in der .csproj:
<ItemGroup>
<PackageReference Include="FMH.Komponente.Analyse" Version="..." />
</ItemGroup>
Arbeitet das Projekt mit zentraler Paketverwaltung (ManagePackageVersionsCentrally), gehört die Version stattdessen als <PackageVersion Include="FMH.Komponente.Analyse" Version="..." /> in die Directory.Packages.props, und die PackageReference bleibt ohne Version.
Feed: FMH-Pakete liegen nicht auf nuget.org, sondern im Azure-DevOps-Feed Fmh_Framework_NuGet_Feed. Er gehört in die NuGet.config der Projektmappe — zusammen mit dem Package-Source-Mapping, das FMH.* ausschliesslich dort sucht (Tab «NuGet.config»). Ohne den Feed schlägt die Wiederherstellung mit NU1101 fehl; ohne Anmeldedaten am Feed mit 401.
Das Paket bringt mit (Abhängigkeiten laut nuspec):
| Paket | Wofür |
|---|---|
MyCSharp.HttpUserAgentParser.AspNetCore |
Zerlegt den User-Agent in Betriebssystem, Browser, Version und Gerätetyp. |
databinding.WebSecurity.Headers.Attributes |
Liefert [HeaderSecurityCspDirective], mit dem die Seite ihre CSP-Ausnahme für api.ipify.org selbst mitbringt. |
serilog |
Protokolliert den Aufruf der Seite. |
Mehr ist nicht zu tun: Controller und View werden als ApplicationPart automatisch gefunden (Microsoft.NET.Sdk.Razor mit AddRazorSupportForMvc). Es gibt keine AddAnalyse()-Methode und keinen DI-Eintrag — nichts zu registrieren.
Voraussetzungen an die Anwendung
| Voraussetzung | Warum |
|---|---|
Standardroute {controller}/{action} |
Daraus entsteht /Analyse. Der Speedtest-Endpunkt /analyse/speedtest-image kommt über Attribut-Routing. |
| Bootstrap 5 | Die Seite nutzt card, list-group, btn und bringt kein eigenes CSS-Bundle mit. |
| Ein Layout im Host | Die View setzt kein eigenes Layout und erbt es aus Views/_ViewStart.cshtml der Anwendung. |
AddUnsafeInlineScriptSrc() in der CSP |
Die Seite arbeitet mit einem Inline-<script>. Ohne die Ausnahme blockiert die Richtlinie es, und alle clientseitigen Angaben bleiben leer. |
| Serilog konfiguriert | Der Controller protokolliert den Aufruf über das statische Log.Information(...). |
Ausgehender Zugriff auf https://api.ipify.org |
Für die öffentliche IP-Adresse. Die CSP-Ausnahme dafür bringt die Action als [HeaderSecurityCspDirective(CspDirective.ConnectSrc, ...)] selbst mit — vorausgesetzt, die Anwendung ruft app.UseHeaderSecurity(...) auf. |
Was die Seite zeigt
Serverseitig ermittelt, aus dem User-Agent und dem angemeldeten Benutzer:
- Betriebssystem, Browser und Version, Typ (Browser/Robot), mobiler Gerätetyp, roher User-Agent-String
- Alle Claims des angemeldeten Benutzers als Tabelle; ohne Anmeldung steht dort «Nicht angemeldet»
Clientseitig im Browser gesammelt:
| Bereich | Angaben |
|---|---|
| Netz | Öffentliche IP-Adresse, Online-Status, gemessene Downloadgeschwindigkeit |
| Darstellung | Bildschirmgrösse, verfügbare Fläche, Fenstergrösse, DPI, Farbtiefe, Touch-Punkte |
| Umgebung | Sprache, Zeitzone, Cookies aktiviert, DoNotTrack, Referrer, aktuelle Adresse |
| Browser-Cache | Anzahl und Namen der Session-Storage-Schlüssel, Anzahl und Liste der Local-Storage-Schlüssel, alle sichtbaren Cookies der Anwendung |
| Fähigkeiten | ServiceWorker, WebGL, mediaDevices, Anzahl Kerne, Gerätespeicher |
| Fehler | JavaScript-Fehler, die auf der Seite auftreten (über window.onerror) |
Die Seitenspalte verlinkt zusätzlich auf Anleitungen zum Leeren des Browser-Caches und zum Prüfen der Browser-Version.
Geschwindigkeitsmessung
Beim Öffnen lädt die Seite ein rund 1.4 MB grosses Testbild von /analyse/speedtest-image mit cache: 'no-store', misst die Dauer und rechnet daraus Mbit/s. Das Bild liegt als eingebettete Ressource im Paket; ausgeliefert wird es nur für die Messung und danach verworfen.
Datenschutz
Storage-Schlüssel, Cookie-Namen und sämtliche Claims des angemeldeten Benutzers stehen im Klartext auf der Seite und im kopierten Text. Wo Claims fachliche Angaben enthalten, gehört das in die Überlegung, wer die Seite aufrufen und wohin der kopierte Text gehen darf.
Sprachen
Die Beschriftungen kommen aus AnalyseResources und liegen auf Deutsch, Englisch und Französisch vor — auch die Meldungen des Skripts.
Zu dieser Demo
Der Rahmen oben zeigt /Analyse?einbetten=true. Der Parameter setzt ViewData["Einbetten"], worauf das Layout dieser Anwendung Navigation und Fusszeile weglässt — sonst erschienen beide im Rahmen ein zweites Mal. Der Seiteninhalt ist unverändert; über den Link darüber lässt sich dieselbe Seite mit vollem Rahmen in einem eigenen Tab öffnen.