Da Nancy Swagger aktuell nicht wirklich unterstützt oder die Einbindung ziemlich Aufwändig ist, war ich heute auf der Suche nach einer einfachen Alternative um wenigstens ein paar Informationen über den REST-Service bereitzustellen.

TL;DR

Demo-Projekt: https://github.com/InvaderZim85/NancyMetadataDemo

Nach einiger Recherche bin ich über die „Metadaten“ von NancyFx gestolpert. Die Metadaten kommen zwar nicht an Swagger ran, aber bieten für den Anfang doch schon einmal ein paar Informationen an und es ist halbwegs „automatisch“. Da die Doku von NancyFx aktuell leider nicht die beste ist, hab ich mich wieder auf die Suche nach ein paar nützlichen Informationen gemacht und hab dabei die folgenden Seiten gefunden:

  1. Generate API Document in Nancy
  2. Nancy Metadata Modules

Nachdem ich mich durch den ersten Artikel gearbeitet hab und langsam am Verzweifeln war und mal wieder an meinen Fähigkeiten als Programmierer gezweifelt hab (hab alles 1:1 nachgebaut aber irgendwie hat das immer noch nicht geklappt) bin ich am Ende des zweiten Artikels auf die Namenskonvention gestoßen. Nachdem ich diese Umgesetzt hab, wurde mir auch alles schön angezeigt.

Ergebnis – NancyFx Route Metadaten

Aber irgendwie war mir das ganze immer noch zu Umständlich. Für jedes Modul nochmal extra eine Klasse mit Metadaten erstellen und da die ganzen Informationen eintragen? Echt jetzt?! Ich bin absolut kein Fan davon, in einer Klasse die Logik zu haben und die Doku an einer anderen Stelle. Weil mir passiert es dann immer wieder, das ich die Logik anpasse, die Doku dazu aber vergesse. Für mein NuGet-Packet hab ich die Doku deswegen schon automatisiert. Man muss das ganze doch irgendwie einfacher lösen können…

Nach längerem Grübeln hab ich mir gedacht, das man das ganze doch auch irgendwie über Attribute lösen können müsste. Für jede Route ein Attribut mit den Infos (Name, Beschreibung, etc.) mit an den Konstruktor des Modules und das ganze dann in der Metadaten-Klasse auslesen und übergeben. Es wäre zwar immer noch kein ganz so schöner Weg aber ich hätte die Informationen schon mal da wo ich sie haben will und nicht in einer anderen Klasse.

Nach etwas rumprobieren (Attribut erstellen, Attribut an Konstruktor hinterlegen, Attribut auslesen, Werte in der Metadaten-Klasse übergeben) viel mir auf, dass das ganze doch einfacher ging als ich dachte. Die Lösung ist zwar immer noch nicht die schönste, aber für den Anfang reicht sie 😀

Und wie hab ich das ganze gemacht?!

1. Passendes Attribut erstellen

Das Attribut ist für den ersten Schritt ziemlich einfach gehalten, aber reicht:

Damit ich mein Attribut auch mehrfach benutzen kann, musste ich meinem Attribut noch das AttributeUsage-Attribut hinzufügen (menge Attribute hier 🙂 ). Dabei hab ich dann noch fix gesagt, dass das Attribut nur an Konstruktoren verwendet werden darf. Ich kenn mich ja, sonst wird das schnell wieder für was anderes Zweckentfremdet.

2. Attribut hinzufügen

Jetzt hab ich zwei Attribute, mit ein paar Infos, an meinem Konstruktor. Soweit so gut…

3. Attribute auslesen

Damit meine Infos aus den Attributen auch noch schön an die Metadaten übergeben werden, muss ich das ganze noch auslesen:

Ich musste zwar immer noch die Metadaten-Klasse erstellen, aber wie man sehen kann, stehen die Infos direkt am InfoModule. Somit hat man die Logik und die Dokumentation an einem Ort.

Update (22:45)

Ich hab mir gerade nochmal etwas länger die Metadaten-Klasse angeschaut und war noch nicht so ganz zufrieden damit. Und zwar müsste man, wie oben beschrieben, jedesmal im Konstruktor noch ein paar Zeilen Code hinzufügen. Nach etwas Grübeln hab ich mir gedacht, das man doch eigentlich die Metadaten-Klassen (weil man kann ja unter Umständen mehrere davon haben), von einer Basis-Klasse erben lassen kann. Das sollte dann soweit gehen, das die Basis-Klasse alles für mich übernimmt und die erbenden Klassen nichts mehr machen müssen. Also hab ich mal ein wenig rumprobiert und dabei ist folgendes raus gekommen:

Der Basis-Klasse muss man jetzt nur noch mitteilen, welches das „Hauptmodul“ (TModule) ist. Um den Rest kümmert sich die Basisklasse dann. Damit ist meine Metadaten-Klasse auf folgendes zusammengeschrumpft:

Man muss zwar immer noch eine zusätzliche Klasse für die Metadaten erstellen, aber die Klasse ist mittlerweile auf eine Zeile geschrumpft. Langsam aber sicher komm ich zum Ziel 😉

Fazit

Wie gesagt, das ganze ist nicht die schönste Lösung, weil man immer noch zwei Dateien für einen Endpunkt hat, aber mit der Lösung hat man die Logik und die Dokumentation immerhin an einem Ort. Die zweite Klasse (Metadaten-Klasse) dient jetzt mehr oder weniger nur noch als Bindeglied. Aktuell bin ich am Grübeln wie ich dieses Bindeglied auch noch los werden! Sobald ich was dazu gefunden hab, gibts nen neuen Eintrag dazu.