web-dev-qa-db-fra.com

Structure d'API ServiceStack recommandée

J'essaie de trouver la meilleure façon de structurer notre API; nous avons des revues que nous avons configurées dans une structure standard REST (listez-en une, listez tout, créez, mettez à jour, etc.).) liés à un ou plusieurs autres types, par exemple événement, lieu ou chose.

Ma pensée est que les URL seraient dans le sens de:/event/reviews/(ou l'inverse de ceci par exemple/reviews/event /)/location/reviews// thing/reviews /

Le problème que je peux voir est cependant le "GET" pour chacun d'eux devrait retourner l'objet parent c'est-à-dire un événement.

Donc, en utilisant ServiceStack, quelle est la meilleure façon de gérer ce scénario? Est-ce pour créer un service personnalisé pour chaque demande de données plutôt que d'abuser de la configuration prête à l'emploi REST setup ou ai-je oublié quelque chose de plus fondamental?

66
Tim

Premièrement, la "meilleure" solution est un terme assez subjectif. Je viserai généralement des solutions SÈCHES, réutilisables et performantes qui favorisent le moins d'effort, de friction et de bavardage, tandis que d'autres peuvent définir "Best" dans la mesure où il suit les principes de REST. Ainsi, vous obtiendrez des réponses variées en fonction des objectifs. Je ne peux offrir que la façon dont je l'aborderais.

Les implémentations de service ServiceStack sont découplées de leurs itinéraires personnalisés

Une chose à garder à l'esprit est la façon dont vous définissez et concevez vos services dans ServiceStack sont assez découplés dans la façon dont vous les exposez, car vous pouvez exposer vos services sous n'importe quel itinéraire personnalisé. ServiceStack encourage une conception basée sur les messages, vous devez donc donner à chaque opération un message distinct.

Utiliser une structure URL logique/hiérarchique

J'utiliserais une structure d'URL logique qui vise à représenter l'identifiant d'un nom, qui est structuré hiérarchiquement, c'est-à-dire que le chemin parent catégorise votre ressource et lui donne un contexte significatif. Donc, dans ce cas, si vous souhaitez exposer des événements et des critiques, mon inclination est d'aller avec la structure d'URL suivante:

/events             //all events
/events/1           //event #1
/events/1/reviews   //event #1 reviews

Chacun de ces identifiants de ressource peut se voir appliquer un verbe HTTP

La mise en oeuvre

Pour la mise en œuvre, je suis généralement une conception basée sur les messages et je regroupe toutes les opérations associées en fonction du type de réponse et du contexte d'appel. Pour cela, je ferais quelque chose comme:

[Route("/events", "GET")]
[Route("/events/category/{Category}", "GET")] //*Optional top-level views
public class SearchEvents : IReturn<SearchEventsResponse>
{
   //Optional resultset filters, e.g. ?Category=Tech&Query=servicestack
   public string Category { get; set; } 
   public string Query { get; set; }
}

[Route("/events", "POST")]
public class CreateEvent : IReturn<Event>
{
   public string Name { get; set; }
   public DateTime StartDate { get; set; }
}

[Route("/events/{Id}", "GET")]
[Route("/events/code/{EventCode}", "GET")] //*Optional
public class GetEvent : IReturn<Event>
{
   public int Id { get; set; }
   public string EventCode { get; set; } //Alternative way to fetch an Event
}

[Route("/events/{Id}", "PUT")]
public class UpdateEvent : IReturn<Event>
{
   public int Id { get; set; }
   public string Name { get; set; }
   public DateTime StartDate { get; set; }
}

Et suivez un modèle similaire pour les évaluations d'événements

[Route("/events/{EventId}/reviews", "GET")]
public class GetEventReviews : IReturn<GetEventReviewsResponse>
{
   public int EventId { get; set; }
}

[Route("/events/{EventId}/reviews/{Id}", "GET")]
public class GetEventReview : IReturn<EventReview>
{
   public int EventId { get; set; }
   public int Id { get; set; }
}

[Route("/events/{EventId}/reviews", "POST")]
public class CreateEventReview : IReturn<EventReview>
{
   public int EventId { get; set; }
   public string Comments { get; set; }
}

L'implémentation devrait être assez simple sur la base de ces messages, que (selon la taille de la base de code) j'organiserais en 2 EventsService et Classes EventReviewsService . Je dois noter que j'utilise moi-même la pluralisation pour les noms DTO de demande de service pour éviter les conflits avec les modèles de données du même nom.

Bien que j'aie séparé UpdateEvent et CreateEvent ici, je les fusionnerai parfois en une seule opération idempotente StoreEvent si le cas d'utilisation le permet.

Structure physique du projet

Idéalement, le projet de niveau racine AppHost doit être léger et sans implémentation. Bien que pour les petits projets avec seulement quelques services, il est normal que tout soit dans un seul projet et que vous développiez simplement votre architecture quand et selon vos besoins.

Pour les projets de moyenne à grande envergure, nous recommandons la structure physique en dessous de laquelle, aux fins de cet exemple, nous supposerons que notre application est appelée EventMan .

L'ordre des projets montre également ses dépendances, par ex. le projet de niveau supérieur EventMan fait référence à tous les sous-projets tandis que le dernier EventMan.ServiceModel références du projet aucune :

- EventMan
    AppHost.cs              // ServiceStack ASP.NET Web or Console Host Project

- EventMan.ServiceInterface // Service implementations (akin to MVC Controllers)
    EventsService.cs
    EventsReviewsService.cs

- EventMan.Logic            //For larger projs: pure C# logic, data models, etc
    IGoogleCalendarGateway  //E.g of a external dependency this project could use

- EventMan.ServiceModel     //Service Request/Response DTOs and DTO types
    Events.cs               //SearchEvents, CreateEvent, GetEvent DTOs 
    EventReviews.cs         //GetEventReviews, CreateEventReview
    Types/
      Event.cs              //Event type
      EventReview.cs        //EventReview type

Avec le EventMan.ServiceModel Les DTO sont conservés dans leur propre implémentation distincte et dans une DLL sans dépendance, vous pouvez librement partager cette DLL dans n'importe quel projet client .NET tel quel - que vous pouvez utiliser avec n'importe lequel des génériques Clients de service C # pour fournir une API typée de bout en bout sans code-gen.


Mise à jour

137
mythz

Je ne sais pas si cela vous aidera dans votre scénario/compréhension, mais je trouve cette présentation utile:

Conception d'une belle API REST + JSON

13
MikeT