Guten Tag,
ich weiss gar nicht, ob mein Vorgehen richtig ist, aber ich habe gelesen, dass man REST-Services so implementiert, dass nicht nur das Ergebnis der Abfrage übermittelt wird, sondern auch Links, die genutzt werden können, um weiter abzufragen. Dazu habe ich versucht eine kleine Middleware zu schreiben, die es mir erlaubt, die Links hinzuzufügen.
Ich habe also zum Beispiel als Resource Projekte, die ich über einen ProjectsController abfrage:
[HttpGet]
public IEnumerable<ProjectWebRepresenation> Get()
{
var queryProjects = _queryProjectsBusinessController.QueryProjects();
if (queryProjects == null)
return null;
return queryProjects.Select(project =>new ProjectWebRepresenation { Number = project.Number, Name = project.Name});
}
Projekte haben neben dem Namen und einer Projektnummer noch andere Daten, zum Beispiel hängen an den Projekten Kategorien. Die Kategorien sollen aber nicht mitgeschickt werden, sondern bei Bedarf abgefragt werden.
Stattdessen soll ein Link in die JSON-Antwort eingefügt werden, über die die abhängigen Objekte nachgeladen werden können. Ich habe dafür einen Artikel gefunden, allerdings ist der nicht für Core.
Anreichern von REST-Nachrichten über Links
Den Handler habe ich versucht, in eine Middleware umzuwandeln. Das Erzeugen der Middleware funktioniert ganz gut, allerdings bekomme ich ein Problem weil die Middleware ein HttpContext erwartet und kein HttpResponseMessage.
Vielleicht bin ich auch auf einem ganz falschen Weg. Es wäre schön, von euch zu hören.
Mfg fluxy
Das, was Du mit der Location einer Ressource meinst, ist nicht REST, sondern nennt sich Hypermedia.
Les Dich mal in Hypermedia ein; da gibts zB. Siren und OData. Hypermedia baut auf REST auf.
Hypermedia ist auch Teil der Ressource und sollte damit nicht einfach nur in der Middleware angereichert werden.
Dadurch werden übrigens auch andere Probleme einer API wie zB Versionierung gelöst.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo fluxy,
HATEOAS (Hypermedia ...) heisst das. Jedoch ist es mMn in eine Middleware im ASP.NET Core der falsche Ort das Ganze anzureichern.
Da die Links sich ja nach der Ressource richten, die du zurück schickst, kannst du erst im Controller wirklich sagen, welche Links da wirklich mitsollen. Daher würde ich da ansetzen.
Gruss
Coffeebean
Microsoft MVP // Me // Blog // GitHub // @Egghead // All my talks // Speakerdeck
Verstehe. Dann ist das ja nicht Rest, sondern wieder etwas neues?
Wie würde man denn mit so einer Situation umgehen, Wenn man einen REST Service ohne hypermedia schreiben will?
Ich bin mir nicht sicher, ob es das richtige ist, sich in hypermedia einzuarbeiten, Wenn man sich gerade mit den Grundlagen von REST beschäftigt.
Viele Grüße und danke für die bisherigen Anmerkungen
Fluxy
Hypermedia baut auf REST auf.
Doch, Hypermedia macht sinn. Sehe es als Anreicherung von REST.
Ohne Hypermedia hast Du halt keine Links.
REST schreibt ja nur, wie zB die URLS aufgebaut sein sollten, sprich die Endpunkte.
Hypermedia beschreibt quasi, wie der Client durch REST navigiert.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo fluxy,
schau dir mal das Richardson Maturity Model an. Ich glaube das hilft dir HATEOAS in REST einzuordnen.
Richardson Maturity Model - steps toward the glory of REST
Da gibt es vier Level (0-3). Mit diesen Leveln kann man beschreiben wie "reif" deine API ist - deine API erfüllt also Level 0 (Http als Kommunikation), 1 (Resourcen bekommen eigenen Endpunkt), 2 (Korrekte Verben und Statuscodes auf die versch. Endpunkte und Antworten) oder 3 (HATEOAS).
Erst das dritte Level bringt dann das HATEOAS, also das, wo du hinwillst.
Gruss
Coffeebean
Microsoft MVP // Me // Blog // GitHub // @Egghead // All my talks // Speakerdeck
Der Unterschied von Hypermedia und HATEOAS ist, dass die Rückgabe der Ressourcen sich anhand dem Status der Ressource orientiert.
Auch ist das dritte Level nicht HATEOAS sondern Hypermedia.
Hypermedia besagt, dass eine Ressoure zB. einen Link auf sich oder auf Relationen eingebettet hat.
HATEOAS ist als Erweiterung zu sehen.
Wenn die Ressource zum Beispiel eine Bestellung ist, dann kann diese Bestellung zum Beispiel gecancelt werden.
Mit einfachen Hypermedia Definition würde nun also ein Link zu "Cancel" zu sehen sein.
HATEOAS bindet zB im Gegensatz zu OData nun den Zustand ein - beide sind aber Hypermedia.
Ist die Bestellung noch nicht ausgeliefert, so taucht der Link zu Cancel auf. Ist die Bestellung schon im Versand, dann kann sie nicht mehr gecancelt werden und ergo taucht in der Link-Liste bei HATEOAS kein Cancel mehr auf.
Man muss sich überlegen, ob man wirklich den Zustand mit in die API Logik mit hinein nehmen will.
Hypermedia (das Konzept) und HAL (Hypertext Application Language, quasi der Standard) ist aber die Grundbasis.
Hypermedia kann ohne HATEOAS umgesetzt werden, aber nicht HATEOAS ohne Hypermedia.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo,
Vielen Dank für eure Informationen.
Aber eine Frage: so wie ich euch verstanden habe, Ist sowohl Hypermedia als auch HATEOAS als Aufsatz auf REST zu verstehen. Im Internet als auch in dem Link zu dem Artikel von Martin Fowler verstehe ich es eher so, das es dabei eher um die Herbeiführung von Zustandsänderungen geht und somit beides Teil von REST ist, bzw. Genutzt werden sollte um REST zu implementieren.
Ich habe übrigens noch einen anderen Artikel gefunden, der auch für andere hilfreich sein könnte. Es ist zwar ein Artikel aus der Javawelt, Aber da es sich bei REST um ein Protokoll handelt und nicht um eine Implementierung, sollte das egal sein
Was mir noch nicht klar ist!! Ist wie man so etwas nun mit webapi Core explizit umsetzt. Habt ihr vielleicht diesbezüglich gute Links?
Vg fluxy
Ja, Hypermedia ist quasi ein Aufsatz auf REST oder ein "Rahmen" und HATEOAS eine Erweiterung dessen. Vereinfacht gesagt.
Wie gesagt:
Hypermedia kann ohne HATEOAS umgesetzt werden, aber nicht HATEOAS ohne Hypermedia.
In ASP.NET Core setzt man die API nicht anders um als in der Vorgängerversion.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Gut, dann sind wir jetzt wieder am Anfang, aber das macht nichts, weil diese Ehrenrunde hatte einige gute Lerneffekte -;)
Als Orientierung hatte ich dieses Tutorial verwendet: Hypermedia und WebApi
Grundsätzlich werden die Links im Controller hinzugefügt. Beispiel:
public Post Get(int id)
{
var post = Session.Load<Domain.Post>(id);
var response = Mapper.Map<Post>(post);
response.AddLink(new EditLink(Url.Link(...)));
return response;
}
Die Implementierung geht davon aus, das jede Ressource von einer abstrakten Basis erbt, die die Möglichkeit erweitert, der Resource Links hinzuzufügen.
Der Author geht dann noch einen Weg um Duplizierten Code zu vermeiden, nämlich die Resourcen immer wieder hinzuzufügen. Soweit ich das verstehe ich der Mechanismus folgender:
Es gibt für verschiedene Ressourcen verschiedene "Enricher". Enricher sind dann dafür da, die Ressourcen um Links zu erweitern. Die Enricher werden quasi einfach als Service registriert, aber es gibt noch einen Handler.
Ich habe noch nicht viel mit WebApi gemacht, aber ich finde das Vorgehen gar nicht so schlecht. Ich hoffe ihr könnt mir das bestätigen. Meine Idee war es jetzt, den Handler durch eine Middleware abzulösen. Ist das wirklich ein ungünstiges Vorgehen?
Vielleicht habe ich mich auch anfangs nicht konkret genug ausgedrückt?!
Viele Grüße,
fluxy
Auch in der alten Variante hat man IHttpResponse
als Rückgabetyp verwendet und zB mit return Ok(object)
die Ressource zurück gegeben.
Tutorials sind nur Beispiele; muss man nicht so machen.
Ich sehe keine Notwendigkeit einer eigenen Middleware hier.
Eine Middleware hat immer einen Zweck der Manipulation eines Requests in der Pipeline. Hier ja gar nicht notwendig.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Eine Middleware hat immer einen Zweck der Manipulation eines Requests in der Pipeline. Hier ja gar nicht notwendig.
Erm, eigentlich manipuliert die Middleware Requests UND Responses in der Application pipeline. Insofern wäre es ohne weiteres machbar, die Antworten um zusätzliche Informationen anzureichern, und natürlich wird das auch gemacht. Der Punkt ist eher der: um HATEOAS zu implementieren, benötigt man Informationen zum Status der Applikation, die die Middleware nicht hat und auch nicht haben sollte. Wer den Status kennen muss, ist/sind der/die Controller, und aus diesem Grund passiert die Informationsanreicherung eben dort und nicht in der Middleware[1].
Grob fahrlässig verallgemeinert:
Middleware: Form der Anfragen prüfen, Form der Antworten manipulieren
EDIT: Action im [/EDIT]Controller: Inhalt der Anfragen prüfen, Inhalt der Antworten festlegen
Bei uns sieht so eine Anreicherung im Normalfall so aus:
public IActionResult Get(int id)
{
var resultObject = DoSomething(id);
return ResultResponse(resultObject).WithResultLink(uri);
}
//ResultResponse() ist analog zu OK() implementiert, liefert aber eine von IActionResult abgeleitete Schnittstelle, auf die die Extension WithResultLink manipulierend zugreifen kann, um den Inhalt anzureichern. Bezeichner sind im realen Leben natürlich anders.
LaTino
[1] habt ihr ja auch nicht anders behauptet, nur kam die Begründung nicht so recht rüber, meine ich.
EDIT: Abt hat natürlich recht, die Action ist, wo die, erm, Action ist. Ergänzt.
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
Erm, eigentlich manipuliert die Middleware Requests UND Responses in der Application pipeline.
Ist mir bewusst... 😭
Aber um bei en Spitzfindigkeiten zu bleiben:
Ein Controller ist nur ein Rahmen.
Die Action ist der Ort, wo der Standort bekannt ist und die Antwort festgelegt wird.
Eure Umsetzung mit Flunt Response finde ich gut und sinnvoll.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code