Capítulo 5: Creación e publicación dunha API web en Azure
Unha vez establecido que os datos da aplicación dos técnicos deberían proceder dos sistemas existentes a través dunha API web, Maria e Kiana traballan xuntas para determinar exactamente que información se necesita e en que formato. Kiana creará entón unha aplicación web que expón a API web adecuada e organizará o seu aloxamento en Azure. A aplicación pode conectarse a Azure desde calquera lugar onde haxa conexión sen fíos.
Definición das operacións da API web: xestión de inventario de campo
A pantalla Explorar da sección de xestión de inventario de campo da aplicación mostra unha lista de pezas para caldeiras e sistemas de aire acondicionado (denominada simplemente pezas de caldeira). A pantalla Detalles permite ao técnico ver máis información sobre unha peza seleccionada.
Na base de datos de inventario existente (denominada InventoryDB), a información sobre as pezas móstrase nunha única táboa chamada BoilerParts. Kiana determina que a API web debería admitir as seguintes solicitudes:
- Obter todas as pezas de caldeira.
- Obter os detalles dunha peza, dada a identificación da peza.
Definición das operacións da API web: base de coñecemento de campo
No sistema existente, a base de datos da base de coñecemento (denominada KnowledgeDB) contén tres táboas que rexistran e xestionan as relacións entre consellos, enxeñeiros e pezas:
- Consellos, que contén os detalles dun consello. Cada consello inclúe un resumo dunha soa liña que identifica un problema concreto (o asunto) e unha explicación máis detallada que describe como resolver o problema (o corpo). Cada consello tamén fai referencia a unha peza e ao enxeñeiro que o rexistrou.
- BoilerParts, que contén unha lista das pezas ás que fan referencia os consellos. Os detalles das propias pezas almacénanse na táboa BoilerParts na base de datos InventoryDB.
- Enxeñeiros, que enumera os técnicos que foron autores de cada consello.
A parte da base de coñecemento da aplicación actualmente só contén unha pantalla Explorar de marcador de posición. María quere implementar a seguinte funcionalidade:
O técnico especifica un termo de busca na pantalla Explorar para atopar todos os consellos correspondentes. A coincidencia podería estar no nome da peza á que se refire o consello, o texto no asunto ou corpo do consello ou no nome dun técnico experto cun equipo específico.
Cando se atopen todos os consellos correspondentes, o técnico pode seleccionar un consello para ver os seus detalles.
Un técnico tamén pode engadir novos consellos á base de coñecemento, así como engadir notas e comentarios aos consellos existentes.
A base de coñecemento é grande e está crecendo, e a consulta en varias táboas e columnas pode implicar unha lóxica complexa que require un poder de cálculo significativo. Para reducir a carga na API web, Kiana decide usala Azure Cognitive Search para proporcionar a funcionalidade de busca, como se describiu anteriormente. Para compatibilizar a aplicación, Kiana decide que se requiren as seguintes operacións da API web:
Buscar os detalles dun consello da base de coñecemento especificado na táboa Consellos.
Actualizar un consello da base de coñecemento existente na táboa Consellos.
Engadir un novo consello da base de coñecemento á tboa Consellos, o que tamén pode implicar engadir filas ás táboas BoilerParts e Enxeñeiros se a peza ou o enxeñeiro especificado non teñen consellos rexistrados na actualidade. A rutina que realmente realiza a lóxica detrás de engadir un novo consello implementarase como unha aplicación lóxica chamada desde Power Apps.
Definición das operacións da API web: programación de campo
A programación de citas de técnicos non só require consultar, engadir e eliminar citas, senón tamén rexistrar información sobre clientes. O sistema de citas existente rexistra estes datos en tres táboas na base de datos SchedulesDB:
- Citas, que contén os detalles de cada cita, incluínda a data, a hora, o problema, as notas e o técnico asignado á tarefa.
- Clientes, que contén os datos de cada cliente, incluído o seu nome, enderezo e datos de contacto.
- Enxeñeiros, que lista a cada técnico que asiste a citas.
Nota
A base de datos contén realmente unha cuarta táboa chamada AppointmentsStatus. Esta táboa contén unha lista de valores válidos para o estado dunha cita e é simplemente unha busca usada por outras partes do sistema de citas existente.
Kiana decide que as seguintes operacións serían útiles para a parte de programación de campo da aplicación:
- Buscar todas as citas dun técnico especificado.
- Buscar todas as citas do día actual dun técnico especificado.
- Atopar a próxima cita programada dun técnico especificado.
- Actualizar os detalles dunha cita, como engadir notas ou unha fotografía.
- Buscar detalles sobre un cliente.
Compilación da API web: xestión de inventario de campo
Os sistemas existentes almacenan datos mediante Azure SQL Database. Kiana decide construír a API web empregando o Entity Framework Core, porque este enfoque pode xerar moito código que consulta, insire e actualiza datos automaticamente. O modelo de API web fornecido por Microsoft tamén pode crear as descricións de Swagger que describen cada operación na API. Estas descricións son útiles para probar as operacións da API. Moitas ferramentas poden usar esta información para integrar a API con outros servizos, como Azure API Management.
Kiana comezou coa funcionalidade Inventario de campo porque esta é a parte máis sinxela. As operacións de inventario de campo na API web consultan unha soa táboa, BoilerParts, na base de datos InventoryDB. Esta táboa contén as columnas que se amosan na seguinte imaxe.

Kiana adoptou o enfoque de "código primeiro" para construír a API web. Con esta estratexia, fixo o seguinte:
Definiu a súa propia clase de modelo C# que reflectía a estrutura da táboa BoilerParts na base de datos InventoryDB.
Creou unha clase de contexto de Entity Framework que usa a API web para conectarse á base de datos, para realizar consultas.
Configurou a clase de contexto para conectarse á base de datos InventoryDB en Azure.
Usou as ferramentas da liña de comandos de Entity Framework para xerar unha clase de controlador da API web que implementa solicitudes HTTP de REST para cada unha das operacións que se poden realizar na táboa BoilerParts.
Usou a API de Swagger para probar a API web.
A seguinte imaxe mostra a estrutura de alto nivel da API web.

Kiana utilizou o seguinte procedemento para crear a API web empregando ferramentas de liña de comandos de .NET 5.0 e Visual Studio Code:
Abra unha ventá do terminado en Visual Studio Code.

Execute o seguinte comando para crear un novo proxecto de API web chamado FieldEngineerApi.
dotnet new webapi -o FieldEngineerApiAbra o cartafol FieldEngineerApi.

Elimine o controlador de exemplo WeatherForecastController.cs e o ficheiro de clase WeatherForecast.cs creado polo modelo de API web.

Na ventá Terminal engada os seguintes paquetes e ferramentas de Entity Framework, xunto co soporte para usar SQL Server, ao proxecto.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson dotnet tool install --global dotnet-ef dotnet tool install --global dotnet-aspnet-codegeneratorNo cartafol FieldEngineerApi, cree un cartafol novo chamado Modelos.

No cartafol Modelos, cree un ficheiro de código C# co nome BoilerPart.cs.

Neste ficheiro, engada as seguintes propiedades e campos. Estas propiedades e campos reflicten a estrutura da táboa BoilerParts na base de datos InventoryDB.
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace FieldEngineerApi.Models { public class BoilerPart { [Key] public long Id { get; set; } public string Name { get; set; } public string CategoryId { get; set; } [Column(TypeName = "money")] public decimal Price { get; set; } public string Overview { get; set; } public int NumberInStock { get; set; } public string ImageUrl { get; set; } } }No cartafol Modelos, cree outro ficheiro de código C# co nome InventoryContext.cs. Engada o seguinte código a esta clase. A clase proporciona a conexión entre o controlador (que se creará a continuación) e a base de datos.
using Microsoft.EntityFrameworkCore; namespace FieldEngineerApi.Models { public class InventoryContext : DbContext { public InventoryContext(DbContextOptions<InventoryContext> options) : base(options) { } public DbSet\<BoilerPart\> BoilerParts { get; set; } } }Edite o ficheiro appsettings.Development.json do proxecto e engada unha sección ConnectionStrings coa seguinte cadea de conexión de InventoryDB. Substitúa <server name> co nome do servidor da base de datos SQL que creou para manter a base de datos InventoryDB.
{ "ConnectionStrings": { "InventoryDB": "Server=tcp*:<server name>*.database.windows.net,1433;Initial Catalog=InventoryDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } } }Importante
Só para os efectos desta guía, a cadea de conexión contén o ID de usuario e o contrasinal da base de datos. Nun sistema de produción, nunca debería almacenar estes elementos en texto claro nun ficheiro de configuración.
Edite o ficheiro Startup.cs e engada o seguinte usando directivas á lista ao comezo do ficheiro.
using FieldEngineerApi.Models; using Microsoft.EntityFrameworkCore;Na clase Inicio, atope o método ConfigureServices. Engada a seguinte instrución a este método.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>(options => options.UseSqlServer(Configuration.GetConnectionString("InventoryDB"))); services.AddControllers(); ... }Modifique o método Configurar e habilite a IU de Swagger incluso cando a aplicación estea funcionando no modo de produción, como se mostra (este cambio implica recolocar as dúas chamadas do método app.UseSwagger fóra da instrución if).
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "FieldEngineerApi v1")); ... }Importante
Este cambio permite expoñer o extremo de Swagger para a integración de xestión da API. Despois de configurar a xestión da API, debería mover este código de novo dentro da instrución if e volver despregar a API web. Nunca deixe aberto o extremo de Swagger nun sistema de produción.
Na ventá Terminal, execute o seguinte comando para xerar o controlador de BoilerParts desde a clase de modelo BoilerPart e a clase de contexto InventoryContext.
dotnet aspnet-codegenerator controller ^ -name BoilerPartsController -async -api ^ -m BoilerPart -dc InventoryContext -outDir ControllersO controlador BoilerParts debería ser creado no cartafol Controladores.
[!NOTE] O carácter terminador de liña, ^, só é recoñecido por Windows. Se está executando Visual Studio Code nun sistema Linux, use o carácter \ no seu lugar.
Abra o ficheiro BoilerParts.cs no cartafol Controladores e revise o seu contido. A clase BoilerPartsController expón os seguintes métodos de REST:
- GetBoilerParts(), que devolve unha lista de todos os obxectos BoilerPart da base de datos.
- GetBoilerPart(id longa), que recupera os detalles da peza da caldeira especificada.
- PutBoilerPart(long id, BoilerPart boilerPart), que actualiza unha peza da caldeira na base de datos cos detalles no obxecto BoilerPart especificado como parámetro.
- PostBoilerPart(BoilerPart boilerPart), que crea unha nova peza de caldeira.
- DeleteBoilerPart(id longa), que elimina os detalles da peza da caldeira especificada da base de datos.
Nota
A aplicación do técnico só require os dous métodos Get, pero os outros son útiles para a aplicación de xestión de inventario de escritorio (non tratada nesta guía).
Compile e constrúa a API web.
dotnet build
A API web debe ser compilada sen informar de erros ou avisos.
Despregamento da API web en Azure: xestión de inventario de campo
Kiana despregou e probou a API web realizando as seguintes tarefas:
Usando a extensión da conta Azure en Visual Studio Code, inicie sesión na súa subscrición a Azure.
Desde a xanela do terminal en Visual Studio Code, cree un novo grupo de recursos chamado webapi_rg na súa subscrición a Azure. No seguinte comando, substitúa <location> pola rexión de Azure máis próxima.
az group create ^ --name webapi_rg ^ --location <location>Cree un plan de servizo de aplicacións de Azure para fornecer os recursos para aloxar a API web.
az appservice plan create ^ --name webapi_plan ^ --resource-group webapi_rg ^ --sku F1Nota
F1 é o SKU gratuíto para plans de servizo de aplicacións. Ofrece rendemento e capacidade limitados e só é adecuado para fins de desenvolvemento.
Cree unha aplicación web de Azure usando o plan de servizo de aplicacións. Substitúa <webapp name> por un nome único para a aplicación web.
az webapp create ^ --name <webapp name> ^ --resource-group webapi_rg ^ --plan webapi_planEn Visual Studio Code, edite o ficheiro appSettings.json e engada a mesma cadea de conexión que escribiu previamente no ficheiro appSettings.Development.json. Lembre substituír <server name> polo nome do servidor da base de datos SQL que creou para manter a base de datos InventoryDB.
{ "ConnectionStrings": { "InventoryDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=InventoryDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"** }, "Logging": { "LogLevel": { "Default\: "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" }Na xanela Terminal, empaquete a API web preparada para a súa implementación en Azure.
dotnet publish -c Release -o ./publishEste comando garda os ficheiros empaquetados nun cartafol chamado publicar.
En Visual Studio Code, faga clic co botón dereito do rato no cartafol publicar e logo seleccione Implementar na aplicación web.

Seleccione o nome da aplicación web que creou anteriormente no paso 4 (<webapp name>). No seguinte exemplo, chámase a aplicación web my-fieldengineer-webapp.

Na solicitude no diálogo de Visual Studio Code, seleccione Despregar para aceptar o aviso e despregar a aplicación web.

Comprobe que a aplicación web se despregou correctamente e logo navegue ata o sitio web.

O sitio web abrirase nunha nova ventá do navegador, pero amosará un erro HTTP 404 (non atopado). Isto débese a que as operacións da API web están dispoñibles a través do extremo da api en lugar da raíz do sitio web. Cambie o URL a https://<webapp name>.azurewebsites.net/api/BoilerParts. Este URI invoca o método GetBoilerParts no controlador BoilerParts. A API web debería responder cun documento JSON que lista todas as pezas da caldeira na base de datos InventoryDB.

Cambie o URL do navegador a https://<webapp name>.azurewebsites.net/swagger. Debería aparecer a API de Swagger. Trátase dunha interface gráfica de usuario que permite a un programador verificar e probar cada unha das operacións nunha API web. Tamén actúa como unha útil ferramenta de documentación.

Seleccione GET adxacente ao extremo /api/BoilerParts/{id} e logo seleccione Probalo.

No campo id, introduza o ID dunha peza e logo seleccione Executar. Esta acción chama o método GetBoilerPart(id longa) no controlador BoilerParts. Devolverá un documento JSON cos detalles da peza ou un erro HTTP 404 se non se atopa ningunha peza na base de datos.

Peche o navegador web e volva a Visual Studio Code.
Construción e despregamendo da API web: Base de coñecemento de campo
As operacións da base de datos de campo da API web funcionan en tres táboas na bae de datos KnowledgeDB: Consellos, BoilerParts e Enxeñeiros. A seguinte imaxe mostra as relacións entre estas táboas e as columnas que conteñen.

Kiana adoptou un enfoque similar para a base de datos da base de coñecemento de campo que utilizou para a base de datos de xestión do inventario de campo. Realizou as seguintes tarefas:
Cree as clases do modelo C# que reflictan a estrutura da táboa Consellos, BoilerParts e Enxeñeiros na base de datos KnowledgeDB. O código para cada unha destas clases móstrase a continuación.
Nota
A táboa BoilerParts na base de datos KnowledgeDB é distinta da táboa BoilerParts na base de datos InventoryDB. Para evitar un choque de nomes, as clases de modelos para táboas na base de datos KnowledgeDB teñen o prefixo KnowledgeBase.
// KnowledgeBaseTips.cs using System.ComponentModel.DataAnnotations; namespace FieldEngineerApi.Models { public class KnowledgeBaseTip { [Key] public long Id { get; set; } public long KnowledgeBaseBoilerPartId { get; set; } public virtual KnowledgeBaseBoilerPart KnowledgeBaseBoilerPart { get; set; } public string KnowledgeBaseEngineerId { get; set; } public virtual KnowledgeBaseEngineer KnowledgeBaseEngineer { get; set; } public string Subject { get; set; } public string Body { get; set; } } }Nota
O Id do enxeñeiro é unha cadea, non un número. Isto débese a que os sistemas existentes utilizan GUID para identificar técnicos e outros usuarios.
// KnowledgeBaseBoilerPart.cs using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace FieldEngineerApi.Models { public class KnowledgeBaseBoilerPart { [Key] public long Id { get; set; } public string Name { get; set; } public string Overview { get; set; } public virtual ICollection<KnowledgeBaseTip> KnowledgeBaseTips { get; set; } } }// KnowledgeBaseEngineer.cs using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace FieldEngineerApi.Models { public class KnowledgeBaseEngineer { [Key] public string Id { get; set; } [Required] public string Name { get; set; } public string ContactNumber { get; set; } public virtual ICollection<KnowledgeBaseTip> KnowledgeBaseTips { get; set; } } }Cree outra clase de contexto de Entity Framework que use a API web para conectarse á base de datos KnowledgeDB.
// KnowledgeBaseContext.cs using Microsoft.EntityFrameworkCore; namespace FieldEngineerApi.Models { public class KnowledgeBaseContext : DbContext { public KnowledgeBaseContext(DbContextOptions<KnowledgeBaseContext> options) : base(options) { } public DbSet<KnowledgeBaseBoilerPart> BoilerParts { get; set; } public DbSet<KnowledgeBaseEngineer> Engineers { get; set; } public DbSet<KnowledgeBaseTip> Tips { get; set; } } }Edite o ficheiro appsettings.Development.json do proxecto e engada a seguinte cadea de conexión de KnowledgDB á sección ConnectionStrings. Substitúa <server name> polo nome do servidor da base de datos SQL que creou para manter a base de datos KnowledgeDB.
{ "ConnectionStrings": { "InventoryDB": "Server=tcp:...", "KnowledgeDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=KnowledgeDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "Logging": { ... } } }Importante
Só para os efectos desta guía, a cadea de conexión contén o ID de usuario e o contrasinal da base de datos. Nun sistema de produción, nunca debería almacenar estes elementos en texto claro nun ficheiro de configuración.
Edite o ficheiro Startup.cs e, no método ConfigureServices, engada as seguintes instrucións.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>...; services.AddDbContext<KnowledgeBaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("KnowledgeD"))); services.AddControllers().AddNewtonsoftJson( options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore** ); services.AddControllers(); ... }A segunda instrución controla a forma en que se serializan os datos cando se recuperan. Algunhas das clases de modelos teñen referencias a outras clases de modelos, que á súa vez poden facer referencia a outras clases de modelos. Algunhas destas referencias poden producir bucles recursivos (a Entidade A fai referencia á Entidade B, que remite á Entidade A, que fai referencia de novo á Entidade B etc.). A opción ReferenceLoopHandling fai que o serializador ignore estes bucles nos datos e só devolva unha entidade e os obxectos aos que fai referencia inmediatamente, pero non máis.
Na ventá Terminal, execute o seguinte comando para xerar controladores desde as clases de modelo KnowledgeBaseBoilerTip, KnowledgeBaseBoilerPart e KnowledgeBaseEngineer e a clase de contexto KnowledgeBaseContext.
dotnet aspnet-codegenerator controller ^ -name KnowledgeBaseTipController -async -api ^ -m KnowledgeBaseTip ^ -dc KnowledgeBaseContext -outDir Controllers dotnet aspnet-codegenerator controller ^ -name KnowledgeBaseBoilerPartController -async -api ^ -m KnowledgeBaseBoilerPart ^ -dc KnowledgeBaseContext -outDir Controllers dotnet aspnet-codegenerator controller ^ -name KnowledgeBaseEngineerController -async -api ^ -m KnowledgeBaseEngineer ^ -dc KnowledgeBaseContext -outDir ControllersOs tres controladores deberían ser creados no cartafol Controladores.
Edite o ficheiro KnowledgeBaseBoilerPartController.cs. Este ficheiro contén o código do controlador KnowledgeBaseBoilerPart. Debe seguir o mesmo padrón que a clase BoilerPartsController creada anteriormente, expoñendo métodos de REST que permiten a un cliente listar, consultar, inserir, actualizar e eliminar entidades. Engada o seguinte método GetTipsForPart ao controlador.
[Route("api/[controller]")] [ApiController] public class KnowledgeBaseBoilerPartController : ControllerBase { private readonly KnowledgeBaseContext _context; public KnowledgeBaseBoilerPartController(KnowledgeBaseContext context) { _context = context; } // GET: api/KnowledgeBaseBoilerPart/5/Tips [HttpGet("{id}/Tips")] public async Task<ActionResult<IEnumerable<KnowledgeBaseTip>>>GetTipsForPart(long id) { return await _context.Tips.Where( t => t.KnowledgeBaseBoilerPartId == id).ToListAsync(); } ... }Este método devolve todos os consellos da base de coñecemento que fan referencia a unha peza especificada. Consulta a táboa Consellos na base de datos a través do obxecto KnowledgeBaseContext para atopar esta información.
Edite o ficheiro KnowledgeBaseEngineerController.cs e engada o seguinte método na clase KnowledgeBaseEngineerController.
[Route("api/[controller]")] [ApiController] public class KnowledgeBaseEngineerController : ControllerBase { private readonly KnowledgeBaseContext _context; public KnowledgeBaseEngineerController(KnowledgeBaseContext context) { _context = context; } // GET: api/KnowledgeBaseEngineer/5/Tips [HttpGet("{id}/Tips")] public async Task\<ActionResult<IEnumerable<KnowledgeBaseTip>>> GetTipsForEngineer(string id) { return await _context.Tips.Where(t => t.KnowledgeBaseEngineerId == id).ToListAsync(); } ... }O método GetTipsForEngineer atopa todos os consellos da base de coñecemento publicados polo enxeñeiro especificado.
Na ventá do Terminal, compile e constrúa a API web.
dotnet buildA API web debe ser compilada sen informar de erros ou avisos.
Edite o ficheiro appSettings.json e engada a cadea de conexión para a base de datos KnowledgeDB. Esta cadea debería ser a mesma que escribiu anteriormente no ficheiro appSettings.Development.json.
{ "ConnectionStrings": { "InventoryDB": ..., "KnowledgeDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=KnowledgeDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "Logging": { ... }, "AllowedHosts": "*" }Na xanela do Terminal, empaquete a API web preparada para a súa implementación en Azure.
dotnet publish -c Release -o ./publishEn Visual Studio Code, faga clic co botón dereito do rato no cartafol publicar e logo seleccione Implementar na aplicación web. Implemente na mesma aplicación web de Azure que creou anteriormente. Permita ao asistente sobrescribir a aplicación web existente co novo código.
Cando finalice a implantación, busque o sitio web pero cambie a URL do explorador a https://<webapp name>.azurewebsites.net/swagger. As operacións dos controladores KnowledgeBaseBoilerPart, KnowledgeBaseEngineer e KnowldgeBaseTip deberían estar listadas ademais das operacións existentes de BoilerParts. Verifique que as operacións de KnowledgeBaseBoilerPart inclúen a operación GET operación para o URI /api/KnowledgeBaseBoilerPart/{id}/Tips e as operacións de KnowledgeBaseEngineer inclúen a operación GET para o URI /api/KnowledgeBaseEngineer/{id}/Tips.

Construción e despregamendo da API web: programación de campo
As operacións de programación de campo utilizan as táboas Citas, AppointmentStatuses (esta é unha táboa de busca sinxela que lista os valores de estado de cita válidos), Clientes e Enxeñeiros, amosadas na seguinte imaxe. Estas táboas almacénanse na base de datos SchedulesDB.

Para crear as operacións da API web para a parte de planificación de campo do sistema, Kiana realizou as seguintes tarefas:
Cree as clases do modelo C# que reflictan a estrutura da táboa AppointmentStatus, Citas, Clientes e Enxeñeiros na base de datos SchedulesDB. O código seguinte mostra cada unha destas clases.
Nota
A clase de modelo da táboa Enxeñeiros chámase ScheduleEngineer para distinguilo do modelo da táboa Enxeñeiros da base de datos InventoryDB.
// AppointmentStatus.cs using Newtonsoft.Json; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace FieldEngineerApi.Models { public class AppointmentStatus { [Key] public long Id { get; set; } public string StatusName { get; set; } [JsonIgnore] public virtual ICollection<Appointment> Appointments { get; set; } } }// Appointment.cs using System; using System.ComponentModel.DataAnnotations; namespace FieldEngineerApi.Models { public class Appointment { [Key] public long Id { get; set; } [Required] public long CustomerId { get; set; } public virtual Customer Customer { get; set; } public string ProblemDetails { get; set; } [Required] public long AppointmentStatusId { get; set; } public virtual AppointmentStatus AppointmentStatus { get; set; } public string EngineerId { get; set; } public virtual ScheduleEngineer Engineer { get ; set; } [Display(Name = "StartTime")] [DataType(DataType.DateTime)] [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy H:mm:ss}")] public DateTime StartDateTime { get; set; } public string Notes { get; set; } public string ImageUrl { get; set; } } }// Customer.cs using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace FieldEngineerApi.Models { public class Customer { [Key] public long Id { get; set; } [Required] public string Name { get; set; } public string Address { get; set; } public string ContactNumber { get; set; } public virtual ICollection<Appointment> Appointments { get; set; } } }// ScheduleEngineer.cs using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; using System.Collections.Generic; namespace FieldEngineerApi.Models { public class ScheduleEngineer { [Key] public string Id { get; set; } [Required] public string Name { get; set; } public string ContactNumber { get; set; } [JsonIgnore] public virtual ICollection<Appointment> Appointments { get; set; } } }Cree unha clase de contexto de Entity Framework que use a API web para conectarse á base de datos SchedulesDB.
// ScheduleContext.cs using System; using Microsoft.EntityFrameworkCore; namespace FieldEngineerApi.Models { public class ScheduleContext : DbContext { public ScheduleContext(DbContextOptions<ScheduleContext> options) : base(options) { } public DbSet<Appointment> Appointments { get; set; } public DbSet<AppointmentStatus> AppointmentStatuses { get; set; } public DbSet<Customer> Customers { get; set; } public DbSet<ScheduleEngineer> Engineers { get; set; } } }Edite o ficheiro appsettings.Development.json do proxecto e engada a seguinte cadea de conexión de SchedulesDB á sección ConnectionStrings. Substitúa <server name> polo nome do servidor da base de datos SQL que creou para manter a base de datos KnowledgeDB.
{ "ConnectionStrings": { "InventoryDB": "Server=tcp*: ...", "KnowledgeDB": "Server=tcp; ... ", "SchedulesDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=SchedulesDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "Logging": { ... } } }Edite o ficheiro Startup.cs e, no método ConfigureServices, engada a seguinte instrución.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>...; services.AddDbContex\<KnowledgeBaseContext>...; services.AddDbContext<ScheduleContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SchedulesDB"))); services.AddControllers().AddNewtonsoftJson(...); ... }Na ventá do Terminal, execute o seguinte comando para xerar controladores desde as clases de modelo Cita, Cliente e ScheduleEngineer e a clase de contexto ScheduleContext.
Nota
Non cree un controlador separado para o modelo AppointmentStatus.
dotnet aspnet-codegenerator controller ^ -name AppointmentsController -async -api ^ -m Appointment ^ -dc ScheduleContext -outDir Controllers dotnet aspnet-codegenerator controller ^ -name CustomerController -async -api ^ -m Customer ^ -dc ScheduleContext -outDir Controllers dotnet aspnet-codegenerator controller ^ -name ScheduleEngineerController -async -api ^ -m ScheduleEngineer ^ -dc ScheduleContext -outDir ControllersEdite o ficheiro AppointmentsController.cs. Na clase AppointmentsController, atope o método GetAppointments. Modifique a instrución return, como se mostra. Este cambio garante que a información de Cliente, Enxeñeiro e AppointmentStatus recupérase como parte da operación GET; estes campos fan referencia a outras entidades que doutro xeito quedarían nulas debido ao preguiceiro mecanismo de carga do Entity Framework.
public class AppointmentsController : ControllerBase { private readonly ScheduleContext _context; public AppointmentsController(ScheduleContext context) { _context = context; } // GET: api/Appointments [HttpGet] public async Task<ActionResult<IEnumerable<Appointment>>> GetAppointments() { return await _context.Appointments .Include(c => c.Customer) .Include(e => e.Engineer) .Include(s => s.AppointmentStatus) .ToListAsync(); } ... }No mesmo ficheiro, modifique o método GetAppointment (id longa), como se mostra.
// GET: api/Appointments/5 [HttpGet("{id}")] public async Task<ActionResult<Appointment>> GetAppointment(long id) { var appointment = _context.Appointments .Where(a => a.Id == id) .Include(c => c.Customer) .Include(e => e.Engineer) .Include(s => s.AppointmentStatus); var appData = await appointment.FirstOrDefaultAsync(); if (appData == null) { return NotFound(); } return appData; }Esta versión do método completa os campos Cliente, Enxeñeiro e AppointmentStatus dunha cita cando se recupera (a carga perezosa deixaría estes campos baleiros doutro xeito).
Atope o método PutAppointment e substitúao polo seguinte código. Esta versión do método PutAppointment colle os campos dunha cita que un usuario pode modificar na aplicación en lugar dun obxecto completo de Cita.
[HttpPut("{id}")] public async Task<IActionResult> PutAppointment(long id, string problemDetails, string statusName, string notes, string imageUrl) { var statusId = _context.AppointmentStatuses.First(s => s.StatusName == statusName).Id; var appointment = _context.Appointments.First(e => e.Id == id); if (appointment == null) { return BadRequest(); } appointment.ProblemDetails = problemDetails; appointment.AppointmentStatusId = statusId; appointment.Notes = notes; appointment.ImageUrl = imageUrl; _context.Entry(appointment).State = EntityState.Modified; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!AppointmentExists(id)) { return NotFound(); } else { throw; } } return NoContent(); }Nota
Como regra xeral, as operacións PUT só deben modificar os datos que un usuario debería permitir actualizar, non necesariamente todos os campos dunha entidade.
Abra o ficheiro ScheduleEngineerController.cs e engada o seguinte método na clase GetScheduleEngineerAppointments á clase ScheduleEngineerController.
[Route("api/[controller]")] [ApiController] public class ScheduleEngineerController : ControllerBase { private readonly ScheduleContext _context; public ScheduleEngineerController(ScheduleContext context) { _context = context; } // GET: api/ScheduleEngineer/5/Appointments [HttpGet("{id}/Appointments")] public async Task<ActionResult<IEnumerable<Appointment>>> GetScheduleEngineerAppointments(string id) { return await _context.Appointments .Where(a => a.EngineerId == id) .OrderByDescending(a => a.StartDateTime) .Include(c => c.Customer) .Include(e => e.Engineer) .Include(s => s.AppointmentStatus) .ToListAsync(); } ... } These methods retrieve the appointments for the specified technician.Edit the CustomerController.cs file and add the GetAppointments and GetNotes methods, as shown, to the CustomerController class.
[Route("api/[controller]")] [ApiController] public class CustomerController : ControllerBase { private readonly ScheduleContext _context; public CustomerController(ScheduleContext context) { _context = context; } //GET: api/Customers/5/Appointments [HttpGet("{id}/Appointments")] public async Task<ActionResult<IEnumerable<Appointment>>> GetAppointments(long id) { return await _context.Appointments .Where(a => a.CustomerId == id) .OrderByDescending(a => a.StartDateTime) .ToListAsync(); } //GET: api/Customers/5/Notes [HttpGet("{id}/Notes")] public async Task<ActionResult<IEnumerable<object>>> GetNotes(long id) { return await _context.Appointments .Where(a => a.CustomerId == id) .OrderByDescending(a => a.StartDateTime) .Select(a => new {a.StartDateTime, a.ProblemDetails, a.Notes}) .ToListAsync(); } ... }O método GetAppointments atopa todas as citas do cliente especificado. O método GetNotes recupera todas as notas que o técnico fixo nas visitas anteriores ao cliente.
Edite o ficheiro appSettings.json e engada a cadea de conexión para a base de datos KnowledgeDB. Esta cadea debería ser a mesma que escribiu anteriormente no ficheiro appSettings.Development.json.
{ "ConnectionStrings": { "InventoryDB": ..., "KnowledgeDB": ..., "SchedulesDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=SchedulesDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "Logging": { ... }, "AllowedHosts": "*" }Na ventá do Terminal, compile e constrúa a API web.
dotnet buildA API web debe ser compilada sen informar de erros ou avisos.
Na xanela do Terminal, empaquete a API web preparada para a súa implementación en Azure.
dotnet publish -c Release -o ./publishEn Visual Studio Code, faga clic co botón dereito do rato no cartafol publicar e logo seleccione Implementar na aplicación web. Implemente na mesma aplicación web de Azure que creou anteriormente. Permita ao asistente sobrescribir a aplicación web existente co novo código.
Cando finalice a implantación, busque o sitio web pero cambie a URL do explorador a https://<webapp name>.azurewebsites.net/swagger. Verifique que as operacións dos controladores Citas, Cliente e ScheduleEngineer agora están dispoñibles.
A API web xa está lista para ser incorporada á aplicación.
Comentarios
Enviar e ver os comentarios