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 in ConfigureService() mit Name erzeugen

services.AddAuthorization(options =>
    {
       options.AddPolicy("MyPolicy", policy => policy.RequireClaim("http://schemas.xmlsoap.org/claims/Group", "MyAdminGroup"));
    });

und dann im Klassen/Methodenattribute verwenden ([Authorize(Policy="MyPolicy")])