Introdução
Olá a todos, este artigo mostrará como escrever uma API Rest simples usando a tecnologia C # ASP.NET Core. Faça testes de unidade para camadas de aplicativo. Envie respostas Json. Também mostrarei como implantar esse aplicativo no Docker.
Este artigo não descreve como tornar o cliente (doravante, Front) parte do aplicativo. Aqui vou mostrar apenas a sala do servidor (doravante Voltar).
O que usamos?
Escreverei o código no Visual Studio 2019.
Para implementar o aplicativo, usarei as seguintes bibliotecas NuGet:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Para testes, essas bibliotecas são:
Microsoft.NET.Test.Sdk
Microsoft.NETCore.App
Moq
xunit
xunit.runner.visualstudio
Para instalar pacotes, você precisa ir para o navegador de pacotes NuGet, você pode fazer isso clicando com o botão direito do mouse no projeto e selecionando lá o item "gerenciar pacotes NuGet"
O que programar?
Como exemplo, vou pegar um modelo altamente simplificado de um serviço de reparo de automóveis. Meu modelo terá trabalhadores que farão os reparos, carros chegando para reparos e a documentação do reparo que será enviada de volta.
Configuração de banco de dados
ApplicationContext ( ) , «appsettings.json». . , .
, «appsettings.json» :
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=testdb;Trusted_Connection=True;"
},
, Entity Framework .
, . , . , , ( ).
, - . ?
- , .
, - () .
,
,
, . - , . Entity Framework . "Add-Migration". Entity Framework , DbContext. , "Update-Database", ( ).
- -, Front . Front, , - Front.
Json. return
new JsonResult( )
, GET, POST, PUT DELETE . GET- Front, POST- , PUT DELETE .
DAO ()
, . , , .
, , get, get all, update, create, delete.
- , - . .
, , Work. . «» , .
, .
, - ASP.NET Core, (RestApi) , . API.

.
( Unit- ) :

BaseModel. , Id , ( ):
public abstract class BaseModel
{
public Guid Id { get; set; }
}
:
public class Car : BaseModel
{
public string Name { get; set; }
public string Number { get; set; }
}
public class Document : BaseModel
{
public Guid CarId { get; set; }
public Guid WorkerId { get; set; }
public virtual Car Car { get; set; }
public virtual Worker Worker { get; set; }
}
public class Worker : BaseModel
{
public string Name { get; set; }
public string Position { get; set; }
public string Telephone { get; set; }
}
, . , .
:
public interface IBaseRepository<TDbModel> where TDbModel : BaseModel
{
public List<TDbModel> GetAll();
public TDbModel Get(Guid id);
public TDbModel Create(TDbModel model);
public TDbModel Update(TDbModel model);
public void Delete(Guid id);
}
:
public class BaseRepository<TDbModel> : IBaseRepository<TDbModel> where TDbModel : BaseModel
{
private ApplicationContext Context { get; set; }
public BaseRepository(ApplicationContext context)
{
Context = context;
}
public TDbModel Create(TDbModel model)
{
Context.Set<TDbModel>().Add(model);
Context.SaveChanges();
return model;
}
public void Delete(Guid id)
{
var toDelete = Context.Set<TDbModel>().FirstOrDefault(m => m.Id == id);
Context.Set<TDbModel>().Remove(toDelete);
Context.SaveChanges();
}
public List<TDbModel> GetAll()
{
return Context.Set<TDbModel>().ToList();
}
public TDbModel Update(TDbModel model)
{
var toUpdate = Context.Set<TDbModel>().FirstOrDefault(m => m.Id == model.Id);
if (toUpdate != null)
{
toUpdate = model;
}
Context.Update(toUpdate);
Context.SaveChanges();
return toUpdate;
}
public TDbModel Get(Guid id)
{
return Context.Set<TDbModel>().FirstOrDefault(m => m.Id == id);
}
}
.
:
public interface IRepairService
{
public void Work();
}
:
public class RepairService : IRepairService
{
private IBaseRepository<Document> Documents { get; set; }
private IBaseRepository<Car> Cars { get; set; }
private IBaseRepository<Worker> Workers { get; set; }
public void Work()
{
var rand = new Random();
var carId = Guid.NewGuid();
var workerId = Guid.NewGuid();
Cars.Create(new Car
{
Id = carId,
Name = String.Format($"Car{rand.Next()}"),
Number = String.Format($"{rand.Next()}")
});
Workers.Create(new Worker
{
Id = workerId,
Name = String.Format($"Worker{rand.Next()}"),
Position = String.Format($"Position{rand.Next()}"),
Telephone = String.Format($"8916{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}{rand.Next()}")
});
var car = Cars.Get(carId);
var worker = Workers.Get(workerId);
Documents.Create(new Document {
CarId = car.Id,
WorkerId = worker.Id,
Car = car,
Worker = worker
});
}
}
, . , Front , , :
//?( )
( ).
MainController:
[ApiController]
[Route("[controller]")]
public class MainController : ControllerBase
{
private IRepairService RepairService { get; set; }
private IBaseRepository<Document> Documents { get; set; }
public MainController(IRepairService repairService, IBaseRepository<Document> document )
{
RepairService = repairService;
Documents = document;
}
[HttpGet]
public JsonResult Get()
{
return new JsonResult(Documents.GetAll());
}
[HttpPost]
public JsonResult Post()
{
RepairService.Work();
return new JsonResult("Work was successfully done");
}
[HttpPut]
public JsonResult Put(Document doc)
{
bool success = true;
var document = Documents.Get(doc.Id);
try
{
if (document != null)
{
document = Documents.Update(doc);
}
else
{
success = false;
}
}
catch (Exception)
{
success = false;
}
return success ? new JsonResult($"Update successful {document.Id}") : new JsonResult("Update was not successful");
}
[HttpDelete]
public JsonResult Delete(Guid id)
{
bool success = true;
var document = Documents.Get(id);
try
{
if (document != null)
{
Documents.Delete(document.Id);
}
else
{
success = false;
}
}
catch (Exception)
{
success = false;
}
return success ? new JsonResult("Delete successful") : new JsonResult("Delete was not successful");
}
}
Application Context
ApplicationContext – , DbContext. DbSet. , , .
public class ApplicationContext: DbContext
{
public DbSet<Car> Cars { get; set; }
public DbSet<Document> Documents { get; set; }
public DbSet<Worker> Workers { get; set; }
public ApplicationContext(DbContextOptions<ApplicationContext> options): base(options)
{
Database.EnsureCreated();
}
}
. Asp.net core . «Startup.cs».
? ( , ), .
. ? .
«Startup.cs»:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("DefaultConnection");
services.AddMvc();
services.AddDbContext<ApplicationContext>(options =>
options.UseSqlServer(connection));
services.AddTransient<IRepairService, RepairService>();
services.AddTransient<IBaseRepository<Document>, BaseRepository<Document>>();
services.AddTransient<IBaseRepository<Car>, BaseRepository<Car>>();
services.AddTransient<IBaseRepository<Worker>, BaseRepository<Worker>>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
. :
Add-Migration init ( )
Update-Database
, , . , .
UNIT- . ( .Net Core).
public class MainControllerTests
{
[Fact]
public void GetDataMessage()
{
var mockDocs = new Mock<IBaseRepository<Document>>();
var mockService = new Mock<IRepairService>();
var document = GetDoc();
mockDocs.Setup(x => x.GetAll()).Returns(new List<Document> { document });
// Arrange
MainController controller = new MainController(mockService.Object, mockDocs.Object);
// Act
JsonResult result = controller.Get() as JsonResult;
// Assert
Assert.Equal(new List<Document> { document }, result?.Value);
}
[Fact]
public void GetNotNull()
{
var mockDocs = new Mock<IBaseRepository<Document>>();
var mockService = new Mock<IRepairService>();
mockDocs.Setup(x => x.Create(GetDoc())).Returns(GetDoc());
// Arrange
MainController controller = new MainController(mockService.Object, mockDocs.Object);
// Act
JsonResult result = controller.Get() as JsonResult;
// Assert
Assert.NotNull(result);
}
[Fact]
public void PostDataMessage()
{
var mockDocs = new Mock<IBaseRepository<Document>>();
var mockService = new Mock<IRepairService>();
mockDocs.Setup(x => x.Create(GetDoc())).Returns(GetDoc());
// Arrange
MainController controller = new MainController(mockService.Object, mockDocs.Object);
// Act
JsonResult result = controller.Post() as JsonResult;
// Assert
Assert.Equal("Work was successfully done", result?.Value);
}
[Fact]
public void UpdateDataMessage()
{
var mockDocs = new Mock<IBaseRepository<Document>>();
var mockService = new Mock<IRepairService>();
var document = GetDoc();
mockDocs.Setup(x => x.Get(document.Id)).Returns(document);
mockDocs.Setup(x => x.Update(document)).Returns(document);
// Arrange
MainController controller = new MainController(mockService.Object, mockDocs.Object);
// Act
JsonResult result = controller.Put(document) as JsonResult;
// Assert
Assert.Equal($"Update successful {document.Id}", result?.Value);
}
[Fact]
public void DeleteDataMessage()
{
var mockDocs = new Mock<IBaseRepository<Document>>();
var mockService = new Mock<IRepairService>();
var doc = GetDoc();
mockDocs.Setup(x => x.Get(doc.Id)).Returns(doc);
mockDocs.Setup(x => x.Delete(doc.Id));
// Arrange
MainController controller = new MainController(mockService.Object, mockDocs.Object);
// Act
JsonResult result = controller.Delete(doc.Id) as JsonResult;
// Assert
Assert.Equal("Delete successful", result?.Value);
}
public Document GetDoc()
{
var mockCars = new Mock<IBaseRepository<Car>>();
var mockWorkers = new Mock<IBaseRepository<Worker>>();
var carId = Guid.NewGuid();
var workerId = Guid.NewGuid();
mockCars.Setup(x => x.Create(new Car()
{
Id = carId,
Name = "car",
Number = "123"
}));
mockWorkers.Setup(x => x.Create(new Worker()
{
Id = workerId,
Name = "worker",
Position = "manager",
Telephone = "89165555555"
}));
return new Document
{
Id = Guid.NewGuid(),
CarId = carId,
WorkerId = workerId
};
}
}
.
public class RepairServiceTests
{
[Fact]
public void WorkSuccessTest()
{
var serviceMock = new Mock<IRepairService>();
var mockCars = new Mock<IBaseRepository<Car>>();
var mockWorkers = new Mock<IBaseRepository<Worker>>();
var mockDocs = new Mock<IBaseRepository<Document>>();
var car = CreateCar(Guid.NewGuid());
var worker = CreateWorker(Guid.NewGuid());
var doc = CreateDoc(Guid.NewGuid(), worker.Id, car.Id);
mockCars.Setup(x => x.Create(car)).Returns(car);
mockDocs.Setup(x => x.Create(doc)).Returns(doc);
mockWorkers.Setup(x => x.Create(worker)).Returns(worker);
serviceMock.Object.Work();
serviceMock.Verify(x => x.Work());
}
private Car CreateCar(Guid carId)
{
return new Car()
{
Id = carId,
Name = "car",
Number = "123"
};
}
private Worker CreateWorker(Guid workerId)
{
return new Worker()
{
Id = workerId,
Name = "worker",
Position = "manager",
Telephone = "89165555555"
};
}
private Document CreateDoc(Guid docId, Guid workerId, Guid carId)
{
return new Document
{
Id = docId,
CarId = carId,
WorkerId = workerId
};
}
}
Work. .
«» .
Docker
, Docker Hub. Visual Studio 2019 . , Docker Docker Hub.
.
Docker Container Registry

, Docker Hub

Docker.
, , «».
, Docker Hub!
Neste artigo, mostrei como usar os recursos do C # ASP.NET Core para criar uma API Rest simples. Mostrou como criar modelos, gravá-los no banco de dados, como criar seu próprio repositório, como usar serviços e como criar controladores que enviarão respostas JSON para seu Front. Ele também mostrou como fazer testes de unidade para camadas de controlador e serviço. E no final, ele mostrou como implantar um aplicativo no Docker.
Espero que você ache este artigo útil!