Estructura de API de ServiceStack recomendada


Estoy tratando de encontrar la mejor manera de estructurar nuestra API; tenemos revisiones que hemos configurado en una estructura REST estándar (listar uno, listar todo, crear, actualizar, etc.). Donde no encaja del todo en los ejemplos es: cada revisión se puede vincular a uno o más tipos, por ejemplo, Evento, Ubicación o Cosa.

Mi pensamiento es que las urls estarían en la línea de: / event / reviews / (o al revés de esto, por ejemplo / reviews / event/) / ubicación / opiniones/ /thing / reviews /

El problema que puedo ver sin embargo es el " GET " para cada uno de estos debe devolver el objeto padre es decir, un Evento.

Entonces, usando ServiceStack, ¿cuál es la mejor manera de manejar este escenario? ¿Es para crear un servicio personalizado para cada solicitud de datos en lugar de abusar de la configuración de REST lista para usar o me he perdido algo más fundamental?

Author: Tim, 2013-03-05

2 answers

En primer lugar, la "mejor" solución es un término bastante subjetivo. Por lo general, voy a apuntar a soluciones SECAS, reutilizables y de rendimiento que promuevan el menor esfuerzo, fricción y chattiness, mientras que otros pueden definir "Mejor" en lo cerca que sigue los principios del DESCANSO. Por lo tanto, obtendrá respuestas variadas dependiendo de cuáles son los objetivos. Solo puedo ofrecer cómo lo abordaría.

Las implementaciones de servicios ServiceStack están desacopladas de sus rutas personalizadas

Una cosa a tener en cuenta es la forma en que define y diseña sus servicios en ServiceStack están bastante desconectados en la forma en que los expone, ya que puede exponer sus servicios bajo cualquier ruta personalizada. ServiceStack fomenta un diseño basado en mensajes, por lo que debe dar a cada operación un mensaje distinto.

Utilice una estructura de url lógica / jerárquica

Usaría una estructura de url lógica que mi objetivo es representar el identificador de un sustantivo, que está jerárquicamente estructurado, es decir, la ruta padre categoriza su recurso y le da un contexto significativo. Así que en este caso si quieres exponer Eventos y reseñas mi inclinación es ir con la siguiente estructura de url:

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

Cada uno de estos identificadores de recursos puede tener cualquier verbo HTTP aplicado a ellos

Aplicación

Para la implementación, generalmente sigo un diseño basado en mensajes y agrupo todas las operaciones relacionadas según el tipo de respuesta y el contexto de la llamada. Para esto haría algo como:

[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; }
}

Y seguir una patrón para revisiones de eventos

[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; }
}

La implementación debería ser bastante sencilla basada en estos mensajes, que (dependiendo del tamaño de la base de código) organizaría en 2 EventsService y EventReviewsService clases. Debo tener en cuenta que uso la pluralización para los nombres de DTO de solicitud de Servicio para evitar chocar con modelos de datos del mismo nombre.

Aunque he separado UpdateEvent y CreateEvent aquí, a veces los fusionaré en un solo idempotente StoreEvent operación si el caso de uso lo permite.

Estructura Física del Proyecto

Idealmente el proyecto de nivel raíz AppHost debe mantenerse ligero y libre de implementación. Aunque para proyectos pequeños con solo unos pocos servicios está bien que todo esté en un solo proyecto y simplemente haga crecer su arquitectura cuando y según sea necesario.

Para proyectos medianos a grandes recomendamos la estructura física debajo de la cual para los propósitos de este ejemplo supongamos que nuestra aplicación se llama EventMan .

El orden de los proyectos también muestra sus dependencias, por ejemplo, el nivel superior EventMan referencias del proyecto todos subproyectos mientras que el último EventMan.ServiceModel referencias del proyecto ninguno :

- 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

Con los DTO EventMan.ServiceModel mantenidos en su propia implementación separada y dll libre de dependencias, puede compartir libremente este dll en cualquier proyecto cliente. NET tal cual, que puede usar con cualquiera de los Servicios genéricos C # Clientes para proporcionar una API de extremo a extremo sin ningún código-gen.


Actualización

 135
Author: mythz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:36

No estoy seguro de si va a ayudar en su escenario / comprensión,pero me parece útil esta presentación:

Diseñando una hermosa API REST + JSON

 13
Author: MikeT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-05 20:44:52