Laden...

Habt ihr Erfahrungen mit Authorization aware HATEOAS?

Erstellt von emuuu vor 3 Jahren Letzter Beitrag vor 3 Jahren 455 Views
emuuu Themenstarter:in
286 Beiträge seit 2011
vor 3 Jahren
Habt ihr Erfahrungen mit Authorization aware HATEOAS?

Guten Tag zusammen,

wie der Titel schon sagt beschäftige ich mich momentan mit authorization aware hateoas (tl;dr; anstatt alle möglichen transitions gibt der server nur die im aktuell authorization context gültigen transitions zurück -> Kunde kann Produkte abfragen aber nicht updaten)
und würde das in einem neuen Projekt gerne anwenden. Das Grundprinzip ist ja relativ simpel.
Meine Frage wäre nun, ob wer von euch damit schon Erfahrungen hat und optimalerweise irgendwelche examples hat / kennt.

Mich interessiert dabei vor allem die Projektstruktur und Zugriffsebenen:
Die Links kennt imho nur der Controller die Authorization hingegen liegt um untergeordneten business layer.

Und wie sieht es mit nested objects aus? Wenn ich bsp folgendes Dto habe:


public class ProductDto
{
   public int Id {get;set;}

   [..]

   public IEnumerable<PartDto> Parts {get;set;}
}

Mir ist noch nicht ganz klar, wie ich die Information über die möglichen transitions (die beim PartDto z.B. aus einem anderen Controller kommen als für ProductDto aufgerufen wurde) in die Geschäftslogik und wieder zurück kriege (ohne gegen Konventionen zu verstoßen).

Natürlich wird es hier nicht den einen richtigen Weg geben und es ist immer von den weiteren Anforderungen anhängig. Aber mir fehlts dahingehend momentan an einem Grundgerüst, das man weiterentwickeln kann.

Beste Grüße
emuuu

2+2=5( (für extrem große Werte von 2)

16.807 Beiträge seit 2008
vor 3 Jahren

HATEOAS ist in der Theorie super; hab da entsprechend Erfahrungen im App Bereich, dass wir entsprechende Zugriffslogik über HATEOAS gelöst haben; wie Du sagst: wenn es kein Link zu Edit gibt, dann kann derjenige halt auch nicht editieren.

In der Praxis findet aber HATEOAS kaum Anwendung, weil es schon auch große Nachteile hat:

  • Man erkauft sich den Status im Response mit hoher Komplexität im Backend
  • HATEOAS ist zwar die höchste Stufe von REST; aber hat auch mit Abstand die größte Evolutionshürde. Man muss also extrem aufpassen, was das Design Concept betrifft.

Und (für mich) der größte Nachteil: Du hast fast keine Chance mehr irgendein Response zu cachen und hast damit einen immensen Impact auf die GET-Performance.
Der Benefit meiner Erfahrung nach ist relativ klein, weil der Werbe-Benefit, dass man Logik im Sinne des State in den Apps "nicht mehr machen muss" habe ich bisher so in der realen Welt kaum kennen gelernt.

Mir ist noch nicht ganz klar, wie ich die Information über die möglichen transitions

Dazu hat jede Entität eine Link-Eigenschaft (serialisiert als _links).
Bei jeder Rückgabe musst Du von allen Entitäten (also auch den Sub-Entitäten) entsprechend die Links generieren; dafür gibts keinen Automatismus.

Meistens habe ich das darüber gelöst, dass ich die Ok()-Methode (und die anderen) erweitert habe und darin den LinkGenerator verwendet habe, um die Routes zu generieren.
Aber das ist verdammt viel Code mit Type-Prüfungen.

Ansonsten weiß ich nicht, was Du mit Geschäftslogik meinst; denn in de Links wird nur der State übertragen.
Den musst natürlich ermitteln - bei jedem Request.

public class ProductDto

PS: Gib Deine API Klassen niemans ein Suffix wie DTO, sondern arbeite einfach mit einem spezifischen Namen wie "Product" und einem sauberen Namespace.
Alles Doc Discovery Modelle für APIs (Swagger, OData...) verwenden nämlich den Klassennamen als API-Identifier; und es gibt aus API Designer sicht quasi nichts schlimmeres, wenn Du anderen Deine Naming Conventions aufs Auge drückst.

emuuu Themenstarter:in
286 Beiträge seit 2011
vor 3 Jahren

In der Praxis findet aber HATEOAS kaum Anwendung, weil es schon auch große Nachteile hat:

Super erstmal danke für die Hinweise. Hab mir bewusst ein kleines Projekt gesucht, wo eine mögliche Überkomplexität, am Ende keine Beine bricht. Ich will vor allem ein Gefühl für Nutzen und Aufwand kriegen, um im Zweifel dann dafür oder dagegen argumentieren zu können.

Ansonsten weiß ich nicht, was Du mit Geschäftslogik meinst; denn in de Links wird nur der State übertragen.

Damit meine ich ganz platt in welcher Form die Informationen innerhalb der API weitergereicht / zusammengetragen werden.
Wenn ich z.B. in einer response die Links von drei verschiedenen Controllern habe und bei einem nested object kann der user aufgrund der authorization editieren, bei zweien deleten und die restlichen nur get.
Ich weiß momentan nicht an welcher Stelle in der API ich die Kenntnis über alle möglichen Links + die Authorization zusammentragen würde.

Gib Deine API Klassen niemans ein Suffix wie DTO, sondern arbeite einfach mit einem spezifischen Namen wie "Product" und einem sauberen Namespace.

Wie siehts da mit z.B. create- und update-klassen aus? ProductCreate, ProductUpdate?

Wenn ich für unterschiedliche authorization unterschiedliche Details ausgebe (bsp Kunde kann nur Name, Preis, etc sehen, Produzent die Parts, Teilenummer, usw.):
Ich verwende dafür unterschiedliche Klassen Product, ProductForManager. Wie verträgt sich das mit doc discovery?

2+2=5( (für extrem große Werte von 2)

16.807 Beiträge seit 2008
vor 3 Jahren

HATEOAS bildest Du nur in ASP.NET Core; nicht in Deiner Logik.
D.h. dass Du all die State-Dinge in ASP.NET zusammen baust - so hab ich das jedenfalls die meiste Zeit gesehen, was entsprechend Last erzeugt.

Das Zusammenbauen kennt auch die Controller nicht; sondern Du baust Dir einen "Router".
Und dieser Router generiert dann die Routes.

Gleiches Prinzip verwenden wir im neuen Forum (und ich eigentlich überall):

    public interface IPortalRouter
    {
        RouterUri ToForumPostView(int postId)
            => CreateRouteUri(Routes.ForumPostView, new RouteValueDictionary { { nameof(postId), postId } });

    public class PortalRouter : IPortalRouter
    {
        public PortalRouter(IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator)

        public RouterUri CreateRouteUri(string name, RouteValueDictionary routeValueDictionary)
            => new(_httpContextAccessor, _linkGenerator, name, routeValueDictionary);

So hast Du ein gloal verfügbares Interface, mit dessen Hilfe Du Urls "erzeugen" kannst via Routes.
Dir ist dann egal, in welchen Controllern das Zeug ist.

Ich verwende dafür unterschiedliche Klassen Product, ProductForManager. Wie verträgt sich das mit doc discovery?

Kann man so machen (mach ich auch), musst dann aber trotzdem aufs Naming aufpassen.
Ja: hast dann viele Modelle in der API Deklaration.

Weit verbreitet ist es aber auch, dass es nur "ein" Modell gibt und nur gewisse Eigenschaften verwendet werden. Man erkennt dann aber an der Doku allein oft nicht, welche Eigenschaften zusammen verwendet können / müssen.