JSON:API Spezifikation mit .NET implementieren

Json:API Spezifikation mit .NET implementieren

Alexander Wobbe | Softwareentwickler | 05.03.2020

Die JSON:API Spezifikation definiert wie ein Client eine Ressource anfragen oder ändern kann, und wie der Server auf diese Anfrage zu antworten hat. JSON:API wurde entwickelt um die Anzahl der Anfragen und die Menge der übertragenen Daten zu minimieren. JSON:API benötigt den Media Type „application/vnd.api+json“ um Daten auszutauschen. Alle Details zu den Spezifikationen sind unter https://jsonapi.org/ zu finden. 

Durchführung

Wer die Spezifikation nicht „from the scratch“ in sein Projekt implementieren möchte, der hat die Möglichkeit auf bereits bestehende Implementierungen für die verschiedensten Umgebungen zurückzugreifen. Eine Auswahl ist unter https://jsonapi.org/implementations/ zu finden. Im Detail gehe ich hier nur auf das „JsonApiDotNetCore“ Package ein, welches ich per nuget in meine .NET Core API eingebunden habe. 

Nach dem installieren des nuget Pakets, muss die Ressource definiert werden. Um die Ressource für das „JsonApiDotNetCore“ verfügbar zu machen, muss sie von „Identifiable“ erben. Um den Namen der Eigenschaft innerhalb des JsonObjektes zu definieren muss diese per DataAnnotation über der Eigenschaft angelegt werden. Die Ressourcenklasse könnte dann so aussehen: 

public class MitarbeiterDTO : Identifiable 

    { 
        [Attr("Id")] 
        public override int Id { get; set; } 
 
        [Attr("Vorname")] 
        public string Vorname { get; set; } 
    }

In der Startup.cs Configure Methode muss das „JsonApiDotNetCore“ als Middleware angemeldet werden 

public void Configure(IApplicationBuilder app) 
        { 
            app.UseJsonApi(); 
        }

app.UseMvc()“ braucht dann nicht explizit aufgerufen zu werden, da es in „app.UseJsonApi“ bereits aufgerufen wird. 

 

Mit DbContext 

Anschließend wird eine Dbcontext angelegt, welche die Ressource implementiert: 

public class BeispielContext : DbContext 

    { 
        public BeispielContext (DbContextOptions<BeispielContext> options) 
                          : base(options) { } 
 
        public DbSet<MitarbeiterDTO> Mitarbeiter { get; set; } 
    }

 Diese wird dann in der Startup.cs inklusive der globalen Optionen angemeldet 

services.AddJsonApi<BeispielContext>(options => 
            { 
                options.Namespace = "jsonapi"; 
                options.IncludeTotalRecordCount = true; 
            });

Soll die Ressource durch eine Custom Funktion bereitgestellt oder geändert werden, dann ist es nötig eine Klasse anzulegen welche von EntityResourceService erbt 

public class MitarbeiterERS : EntityResourceService<MitarbeiterDTO>

um die API üblichen Funktionen wie GET/GET(id)/PATCH/POST/DELETE zu überschreiben. Diese muss auch in der Startup.cs angelegt werden: 

services.AddScoped<IResourceService<MitarbeiterDTO>, MitarbeiterERS>();

 

Ohne DbContext 

Alternativ kann auch ohne DbContext gearbeitet werden, dann müssen die einzelnen Ressourcen allerdings über den BuildResourceGraph angemeldet und zusätzlich eine Klasse angelegt werden, welche IResourceService implementiert. In dieser können dann die API spezifischen Funktionen implementiert werden, welche für die Ressource benötigt werden. 

var mvcBuilder = services.AddMvcCore() 
 
services.AddJsonApi(options => 
            { 
                options.Namespace = "jsonapi"; 
                options.IncludeTotalRecordCount = true; 
                options.BuildResourceGraph((builder) => 
                { 
                    builder.AddResource<MitarbeiterDTO>("mitarbeiter"); 
                }); 
            });

 

public class MitarbeiterResourceService : IResourceService <MitarbeiterDTO>

Um Funktionen wie Filter oder Sorting ohne DbContext nutzen zu können wird eine weitere Klasse benötigt, welche von ResourceDefinition erbt: 

public class MitarbeiterResourceDefinition : ResourceDefinition<MitarbeiterDTO>

Diese muss in der Startup.cs angemeldet werden: 

services.AddScoped<ResourceDefinition<MitarbeiterDTO>,MitarbeiterResourceDefinition>();

 

Der JsonApiController 

Nun kann ein Api Controller erstellt werden, welcher von „JsonApiController“ erbt. Dieser Controller stellt bereits die API üblichen Funktionen wie GET/GET(id)/PATCH/POST/DELETE bereit. Die Aufrufe werden dann, abhängig davon ob DbContext genutzt wird und ob ein ResourceService implementiert wurde an die jeweilige Stelle weitergeleitet. 

public class MitarbeiterController : JsonApiController<MitarbeiterDTO> 
    { 
  public MitarbeiterController(IJsonApiContext jsonApiContext,  
            IResourceService<MitarbeiterDTO> resourceService,  
            ILoggerFactory loggerFactory)  
            : base(jsonApiContext, resourceService, loggerFactory) 
        {} 
}

Die API Funktionen können im Controller überschrieben und somit für den inviduellen Einsatz angepasst werden. 

 

Der BaseJsonApiController 

Alternativ kann auch von „BaseJsonApiController“ geerbet werden.  

public class MitarbeiterController : BaseJsonApiController<MitarbeiterDTO>

Dann müssen die API Funktionen allerdings überschrieben werden, da ansonsten ein 404 Fehler zurückgegeben wird. Optional hat man hier die Möglichkeit mit DataAnnotations die Fähigkeiten des Controllers einzuschränken. 

HttpReadOnly“ z.B. ermöglicht es ohne ausdrückliches überschreiben der Funktionen, alle „lesenden“ Anfragen zu nutzen. Für alle „nicht-lesenden“ Anfragen wird dann ein „405 Method not allowed“ zurückgegeben. 

[HttpReadOnly] 
    public class MitarbeiterController : BaseJsonApiController<MitarbeiterDTO>

 

Dies ist nur ein Teil der umfangreichen Konfigurationsmöglichkeiten, welches dieses Paket zur Verfügung stellt. 

Resümee

Die Einarbeitung in die JSON:API Spezifikation und in das „JsonApiDotNetCore“ Package hat einige Zeit in Anspruch genommen. Und nach heutigem Stand, kann ich nicht behaupten alles gesehen oder verstanden zu haben. Wer jedoch JSON:API Spezifikationen in einem MVC Projekt umsetzen und nicht Monate mit der Implementierung verbringen möchte, hat z.Zt. nur wenige Alternativen zu diesem Paket. 

Der Einsatz von JSON:API ist grundsätzlich vom Umfang des Projektes und der benötigten Ressourcen abhängig und sollte vor der Entscheidung individuell geprüft werden. 

Sie suchen einen Software Dienstleister?
Informationen zu unseren Leistungen finden Sie in unserem Portfolio