Visual Studio: ASP.NET Core project (empty oder WebAPI)
empty:
Startup.cs:
ConfigureServices: services.AddMvc()
Configure: app.UseMvc()
„Controllers“ Ordner erzeugen
New item: API Controller Class, Base class auf ControllerBase ändern (weniger Intellisync)
ROUTING
Class Routing: [Route("bli/bla")] als Controllerklassenattribut wird allen Actions vorgestellt. [Controller] in der Route wird durch Klassenname (muss mit „Controller“ enden) ohne „Controller“ ersetzt.
Atrribute Routing: [HttpGet/Post/Put/Delete("route")] vor Methode.
{bli} = Parameter „bli“ in Methode wird aus Route geholt
{bla:int} = Parameter „bla“ in Methode wird als int aus Route geholt (nur dann wenns auch ein int ist)
{bla:string:minlength(7)} = Parameter „blu“ in Methode wird als string aus Route geholt, String muss mind. 7 Zeichen lang sein
{foo?} = optionaler Parameter
{bar=4711} = Defaultwert (sonst 0 bei int und „“ bei string)
{wtf:regex(^abc…xyz$)} = Route greift nur wenn regular Expression matched
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing
MODEL BINDING
Parameter in Action vorangestellt [FromRoute] (default), [FromBody], [FromQuery], [FromForm], [FromHeader] zwingt ASP.NET Core gleichnamigen Parameter aus dieser Quelle zu beziehen.
Immer ModelState.IsValid prüfen um sicherzustellen dass alle Bindings und Contraints erfolgreich ausgewertet wurden.
DataAnnoations bei Properties von komplexen Typen (z.B. [MinLength(7)] public string Bli { get; set; } in einer Klasse) werden für ModelState ausgewertet (public und get/set sind notwendig!).
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation
ACTION RESULTS
Actions liefern IActionResult anstatt Basistypen, ermöglicht clientseitig besseres Ergebnismanagement (v.a. im Fehlerfall – Exception würde sonst immer 500 liefern anstatt sinnvoller Informationen).
return NotFound() = 404
return BadRequest() oder BadRequest(ModelState) = 400, alternativ mit Zusatzinformation (Validation-Fehler z.B.)
return Ok() oder Ok(„whatever“) oder Ok(new { bli=“bla“, blu=“blu“}) = 200 mit Wert im Body (Object wird zu JSON)
https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types
SECURITY – TRANSPORT
Für MVC kann man verschiedenste Filter einfügen, zwei der wichtigsten (Antiforgery Token (eigentlich nicht Transport aber he…) und Require HTTPS gehen so) – in ConfigureServices():
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add(new RequireHttpsAttribute());
});
In der Configure Methode der Startup-Klasse (Startup.cs) gibts vielfache Möglichkeiten die Transportsicherheit zu erhöhen:
app.UseHttpsRedirection() = offensichtlich
app.UseHsts() = offensichtlich
Das ist aber eher dürftig, viele zusätzliche Securityheader bekommt man über diverse Nugets:
Nwebsec.AspnetCore.Middleware (https://docs.nwebsec.com/en/latest/nwebsec/NWebsec.AspNetCore.Middleware.html)
app.UseHsts(options => options.MaxAge(days: 365).IncludeSubdomains()) = offensichtlich aber mit Parameter
app.UseXXssProtections(options => options.EnableWithBlockMode()) = Cross Side Scripting Header, mit Parameter
app.UseXcontentTypeOptions() = X-Content-Type-Options Header
app.UseXfo(options => options.Deny()) = X-Frame-Options Header
app.UseReferrerPolicy(opts => opts.NoReferrer()) = Referrer Policy
app.UseCsp(…crazy zeug…) = Content Security Policy, siehe Doku, vielfältigste Möglichkeiten sich selbst die Knie wegzuschießen
NetEscapades.AspnetCore.SecurityHeaders (https://andrewlock.net/adding-default-security-headers-in-asp-net-core/)
app.UseSecurityHeaders(new HeaderPolicyCollection().AddDefaultSecurityHeaders()) = für die die etwas schreibfauler sind 😀
SECURITY – AUTHENTICATION UND AUTHORIZATION
Am Beispiel ADFS – in ConfigureServices() Authentication enablen:
services.AddAuthentication(sharedOptions => { sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme; }).AddWsFederation(options => { options.MetadataAddress = "https://myadfs.mydomain.com/FederationMetadata/2007-06/FederationMetadata.xml"; options.Wtrealm = "https://myrelyingpartytrust/"; }).AddCookie(options => { options.AccessDeniedPath = new PathString("/api/mycontroller/myaccessdeniedaction"); });Wobei der Relying Party Trust im ADFS natürlich entsprechend eingetragen werden muss (WS-Federation) und die entsprechenden Claims issued werden müssen. Wtrealm ist der Relying Party Trust Identifier.
Authorization kann schon am ADFS passieren (Issuance Authorization Rules) oder auf WebAPI Seite mit dem
[Authorize]oder[AllowAnonymous]Attribut gesetzt auf Klasse (Controller) oder Methode (Action). Gemeinsame Regeln kann man in Policies inConfigureService()mit Name erzeugenservices.AddAuthorization(options => { options.AddPolicy("MyPolicy", policy => policy.RequireClaim("http://schemas.xmlsoap.org/claims/Group", "MyAdminGroup")); });und dann im Klassen/Methodenattribute verwenden (
[Authorize(Policy="MyPolicy")])