REST API con C# y AspNet fácilmente que escribirlo desde cero una y otra vez en diferentes proyectos. 
REST API Controller con CRUD completo contiene solo 20 líneas de código (~ 10 son importaciones)GET tienen soporte de paginación incorporado ;GET tienen clasificación y filtración incorporadas mediante parámetros de consulta;Create masiva, Update y Delete ) en un nivel de interfaz controlador &&IModelManager ); Buen soporte incorporado de EntityFramework (ver la clase EfModelManager ). Vea la aplicación WeatherControl que tiene 2 proyectos de API web:Wissance.WeatherControl.WebApi usa EntityFramework ;Wissance.WeatherControl.WebApi.V2 usa EdgeDb .Conceptos clave:
Controller es una clase que maneja HTTP-requests para REST Resource .REST Resource es igual a Entity class / Database TableREST Resource produce JSON con DTO como salida. Asumimos usar solo una clase DTO con todos los métodos REST . Clases DTO :
OperationResultDto representa el resultado de la operación que cambia los datos en DB;PagedDataDto representa la porción (página) de los mismos objetos (cualquier tipo); Clases de Controllers - Clases abstractas
BasicReadController ) contiene 2 métodos:GET /api/[controller]/?[page={page}&size={size}&sort={sort}&order={order}] EfModelManager obtener PagedDataDto<T> Ahora también tenemos la posibilidad de enviar cualquier número de parámetros de consultas con edición edificada. También pasamos Sort (Nombre de la columna) && Order ( asc o desc ) a las clases del Administrador, EfModelManager permite clasificar cualquier columna . Swagger para mostrar el uso de parámetros de consulta!1.6.0 es posible ver todos los parámetros en Swagger y usarlos.GET /api/[controller]/{id} para obtener un objeto por idCRUD completo ( BasicCrudController ) = Basic Read Controller ( BasicReadController ) + Create , Update y Delete operaciones:POST /api/[controller] - Para la nueva creación de objetosPUT /api/[controller]/{id} - para el objeto editar por IDDELETE /api/[controller]/{id} - para el objeto Eliminar por IDCRUD completo con operaciones a granel (operaciones sobre múltiples objetos a la vez), clase base - BasicBulkCrudController = Controlador de lectura básica ( BasicReadController ) + Operaciones BulkCreate , BulkUpdate y BulkDelete :POST /api/bulk/[controller] - para la creación de nuevos objetosPUT /api/bulk/[controller] - para editar objetos que pasen en un cuerpo de solicitudDELETE /api/bulk/[controller]/{idList} - para eliminar múltiples objetos por id.Las clases de controladores esperan que toda la operación se realice utilizando las clases de Administrador (cada controlador debe tenerlo propio Administrador)
Clases de gerentes: clases que implementan la lógica comercial de la aplicación
IModelManager - Interfaz que describe las operaciones básicasEfModelManager : es una clase abstracta que contiene la implementación de las operaciones Get y DeleteEfSoftRemovableModelManager es una clase abstracta que contiene la implementación de operaciones Get y Delete con modelos extraíbles suaves (se eliminó el modelo de medios IsDeleted = true )) Ejemplo de cuán más rápido a granel frente a no bombardeo: 
Elapsed time in Non-Bulk REST API with EF is 0.9759984016418457 secs.
Elapsed time in Bulk API with EF is 0.004002094268798828 secs.
Como resultado, obtuvimos casi ~ 250 x API más rápida.
Solo hay un requisito : todas las clases de entidad para cualquier almacenamiento de persistencia que esté utilizando con controladores y gerentes debe implementar IModelIdentifiable<T> de Wissance.WebApiToolkit.Data.Entity . Si este kit de herramientas debe usarse con EntityFramework , debe obtener su administrador de recursos de EfModelManager , tiene métodos incorporados para:
get many artículosget one artículo by iddelete el artículo by id El ejemplo completo se menciona en la Sección 6 (ver más abajo). Pero si está comenzando a construir una nueva API REST Resource debe hacer a continuación:
model ( entity ) implementando IModelIdentifiable<T> y clase DTO para la representación de TI ( para eliminar soft también agregar IModelSoftRemovable implementación), es decir:: public class BookEntity : IModelIdentifiable < int >
{
public int Id { get ; set ; }
public string Title { get ; set ; }
public string Authors { get ; set ; } // for simplicity
public DateTimeOffset Created { get ; set ; }
public DateTimeOffset Updated { get ; set ; }
}
public class BookDto
{
public int Id { get ; set ; }
public string Title { get ; set ; }
public string Authors { get ; set ; }
}Model en DTO , es decir,: public static class BookFactory
{
public static BookDto Create ( BookEntity entity )
{
return new BookDto
{
Id = entity . Id ,
Title = entity . Title ,
Authors = entity . Authors ;
} ;
}
}IModelContext que le tenga BookEntity como DbSet y su clase de implementación que también se deriva de DbContext ( clase de resumen EF ): public interface IModelContext
{
DbSet < BookEntity > Books { get ; set ; }
}
public MoidelContext : DbContext < ModelContext > , IModelContext
{
// todo: not mrntioned here constructor, entity mapping and so on
public DbSet < BookEntity > Books { get ; set ; }
}ModelContext como DbContext a través de DI ver la clase de inicioController y un par de clases de administrador, es decir, considere aquí CRUD completo [ ApiController ]
public class BookController : BasicCrudController < BookDto , BookEntity , int , EmptyAdditionalFilters >
{
public BookController ( BookManager manager )
{
Manager = manager ; // this is for basic operations
_manager = manager ; // this for extended operations
}
private BookManager _manager ;
}
public class BookManager : EfModelManager < BookEntity , BookDto , int , EmptyAdditionalFilters >
{
public BookManager ( ModelContext modelContext , ILoggerFactory loggerFactory ) : base ( modelContext , BookFactory . Create , loggerFactory )
{
_modelContext = modelContext ;
}
public override async Task < OperationResultDto < StationDto > > CreateAsync ( StationDto data )
{
// todo: implement
}
public override async Task < OperationResultDto < StationDto > > UpdateAsync ( int id , StationDto data )
{
// todo: implement
}
private readonly ModelContext _modelContext ;
} Último parámetro genérico en el ejemplo anterior: EmptyAdditionalFilters es una clase que contiene parámetros adicionales para la búsqueda para ver en Swagger, solo especifique una nueva clase que implementa IReadFilterable IE:
public class BooksFilterable : IReadFilterable
{
public IDictionary < string , string > SelectFilters ( )
{
IDictionary < string , string > additionalFilters = new Dictionary < string , string > ( ) ;
if ( ! string . IsNullOrEmpty ( Title ) )
{
additionalFilters . Add ( FilterParamsNames . TitleParameter , Title ) ;
}
if ( Authors != null && Authors . Length > 0 )
{
additionalFilters . Add ( FilterParamsNames . AuthorsParameter , string . Join ( "," , Authors ) ) ;
}
return additionalFilters ;
}
[ FromQuery ( Name = "title" ) ] public string Title { get ; set ; }
[ FromQuery ( Name = "author" ) ] public string [ ] Authors { get ; set ; }
}Podrías encontrar el paquete nuget aquí
[ ApiController ]
public class StationController : BasicCrudController < StationDto , StationEntity , int , EmptyAdditionalFilters >
{
public StationController ( StationManager manager )
{
Manager = manager ; // this is for basic operations
_manager = manager ; // this for extended operations
}
private StationManager _manager ;
} public class StationManager : EfModelManager < StationEntity , StationDto , int >
{
public StationManager ( ModelContext modelContext , ILoggerFactory loggerFactory ) : base ( modelContext , StationFactory . Create , loggerFactory )
{
_modelContext = modelContext ;
}
public override async Task < OperationResultDto < StationDto > > CreateAsync ( StationDto data )
{
try
{
StationEntity entity = StationFactory . Create ( data ) ;
await _modelContext . Stations . AddAsync ( entity ) ;
int result = await _modelContext . SaveChangesAsync ( ) ;
if ( result >= 0 )
{
return new OperationResultDto < StationDto > ( true , ( int ) HttpStatusCode . Created , null , StationFactory . Create ( entity ) ) ;
}
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , "An unknown error occurred during station creation" , null ) ;
}
catch ( Exception e )
{
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , $ "An error occurred during station creation: { e . Message } " , null ) ;
}
}
public override async Task < OperationResultDto < StationDto > > UpdateAsync ( int id , StationDto data )
{
try
{
StationEntity entity = StationFactory . Create ( data ) ;
StationEntity existingEntity = await _modelContext . Stations . FirstOrDefaultAsync ( s => s . Id == id ) ;
if ( existingEntity == null )
{
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . NotFound , $ "Station with id: { id } does not exists" , null ) ;
}
// Copy only name, description and positions, create measurements if necessary from MeasurementsManager
existingEntity . Name = entity . Name ;
existingEntity . Description = existingEntity . Description ;
existingEntity . Latitude = existingEntity . Latitude ;
existingEntity . Longitude = existingEntity . Longitude ;
int result = await _modelContext . SaveChangesAsync ( ) ;
if ( result >= 0 )
{
return new OperationResultDto < StationDto > ( true , ( int ) HttpStatusCode . OK , null , StationFactory . Create ( entity ) ) ;
}
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , "An unknown error occurred during station update" , null ) ;
}
catch ( Exception e )
{
return new OperationResultDto < StationDto > ( false , ( int ) HttpStatusCode . InternalServerError , $ "An error occurred during station update: { e . Message } " , null ) ;
}
}
private readonly ModelContext _modelContext ;
}Solo 2 clases muy simples ^^ usando webapitoolkit
Considere que nos gustaría agregar una búsqueda de métodos a nuestro controlador:
[ HttpGet ]
[ Route ( "api/[controller]/search" ) ]
public async Task < PagedDataDto < BookDto > > > SearchAsync ( [ FromQuery ] string query , [ FromQuery ] int page , [ FromQuery ] int size )
{
OperationResultDto < Tuple < IList < BookDto > , long > > result = await Manager . GetAsync ( page , size , query ) ;
if ( result == null )
{
HttpContext . Response . StatusCode = ( int ) HttpStatusCode . InternalServerError ;
}
HttpContext . Response . StatusCode = result . Status ;
return new PagedDataDto < TRes > ( pageNumber , result . Data . Item2 , GetTotalPages ( result . Data . Item2 , pageSize ) , result . Data . Item1 ) ;
} Tenemos un proyecto adicional para proteger API con Keycloak OpenId-Connect . Pase IHttpContextAccessor a la clase Manager y verifique algo como esto: ClaimsPrincipal principal = _httpContext.HttpContext.User;
Podrías ver nuestros artículos sobre el uso del kit de herramientas: