66 votes

Structure recommandée de l'API ServiceStack

J'essaie de trouver la meilleure façon de structurer notre API ; nous avons des revues que nous avons configurées dans une structure REST standard (list one, list all, create, update etc). Là où ça ne correspond pas tout à fait aux exemples, c'est que chaque avis peut être lié à un ou plusieurs autres types, par exemple un événement, un lieu ou une chose.

Je pense que les urls seraient du type : /événement/reviews/ (ou l'inverse, par exemple /reviews/événement/) /location/revue/ /chose/revue/

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

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

139voto

mythz Points 54874

Tout d'abord, la "meilleure" solution est un terme assez subjectif. Je vise généralement des solutions DRY, réutilisables et performantes, qui demandent le moins d'efforts, de frictions et de bavardages possible, tandis que d'autres définiront la "meilleure" solution en fonction de sa conformité aux principes de REST. Vous obtiendrez donc des réponses variées en fonction de vos objectifs. Je ne peux que vous proposer la façon dont j'aborderais la question.

Les implémentations de services ServiceStack sont découplées de leurs routes personnalisées.

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

Utiliser une structure d'URL logique / hiérarchique

J'utiliserais une structure logique d'Url 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. Ainsi, dans ce cas, si vous souhaitez exposer des événements et des critiques, je suis enclin à utiliser la structure d'URL suivante :

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

Chacun de ces identificateurs de ressources peut se voir appliquer n'importe quel verbe HTTP.

Mise en œuvre

Pour la mise en œuvre, je suis généralement une conception basée sur les messages et je regroupe toutes les opérations connexes 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 Events : IReturn<EventsResponse>
{
   //Optional resultset filters, e.g. ?Category=Tech&Search=servicestack
   public string Category { get; set; } 
   public string Search { 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 schéma similaire pour les examens d'événements

[Route("/events/{EventId}/reviews", "GET")]
public class EventReviews : IReturn<EventReviewsResponse>
{
   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 en se basant sur ces messages, que (selon la taille de la base de code) j'organiserais en 2 EventsService y EventReviewsService classes. Je dois noter que j'utilise moi-même le pluriel pour les noms de DTO de demande de service afin d'éviter tout conflit avec les modèles de données du même nom.

Bien que j'aie séparé UpdateEvent y CreateEvent ici, je vais parfois les fusionner en un seul idempotent StoreEvent si le cas d'utilisation le permet.

Structure physique du projet

Idéalement, le niveau racine AppHost doit rester léger et sans implémentation. Pour les petits projets ne comportant que quelques services, il est possible de tout regrouper dans un seul projet et de faire évoluer l'architecture en fonction des besoins.

Pour les projets de taille moyenne à grande, nous recommandons la structure physique ci-dessous. Pour les besoins de cet exemple, nous supposerons que notre application s'appelle EventMan .

L'ordre des projets indique également ses dépendances, par exemple, le projet de premier niveau EventMan références du projet todo sous-projets tandis que le dernier EventMan.ServiceModel références du projet aucun :

- 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               //Events, CreateEvent, GetEvent, UpdateEvent DTOs 
    EventReviews.cs         //EventReviews, GetEventReview, CreateEventReview
    Types/
      Event.cs              //Event type
      EventReview.cs        //EventReview type

Avec le EventMan.ServiceModel conservés dans leur propre implémentation séparée 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 quel programme générique de la gamme Clients de service C# pour fournir une API typée de bout en bout sans aucun code-gen.


Mise à jour

Cette structure de projet recommandée est maintenant contenue dans toutes les Modèles VS.NET de ServiceStackVS .

13voto

MikeT Points 522

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X