5. poglavlje: Izrada i objavljivanje API-ja za web u servisu Azure

Utvrdivši da podaci za aplikaciju tehničara trebaju biti dobiveni iz postojećih sustava putem API-ja za web, Maria i Kiana zajedno utvrđuju koje su informacije potrebne i u kojem formatu. Kiana će tada izraditi web-aplikaciju koja izlaže odgovarajući API za web i organizirati hostiranje na usluzi Azure. Aplikacija se može povezati s uslugom Azure s bilo kojeg mjesta na kojem postoji bežična veza.

Definiranje operacija API-ja za web: terensko upravljanje zalihama

Zaslon Pretraživanje odjeljka Terensko upravljanje zalihama aplikacije prikazuje popis dijelova za bojlere i klimatizacijske sustave (nazivaju se jednostavno dijelovi bojlera). Zaslon Detalji omogućuje tehničaru da prikaže više informacija o odabranom dijelu.

U postojećoj bazi podataka inventara (pod nazivom InventarDB), podaci o dijelovima nalaze se u jednoj tablici pod nazivom Dijelovi bojlera. Kiana utvrđuje da bi API za web trebao podržavati sljedeće zahtjeve:

  • Uzmite sve dijelove bojlera.
  • Prikažite detalje o dijelu pomoću ID-ja dijela.

Definiranje operacija API-ja za web: terenska baza podataka

U postojećem sustavu baza podataka baza znanja (nazvana KnowledgeDB) sadrži tri tablice koje bilježe odnose i upravljaju njima među savjetima, inženjerima i dijelovima:

  • Savjeti, koji sadrži pojedinosti o savjetu. Svaki savjet sadrži sažetak u jednom retku koji identificira određeni problem (predmet) i detaljnije objašnjenje koje opisuje kako riješiti problem (tijelo). Svaki savjet također upućuje na dio i inženjera koji je napisao savjet.
  • Dijelovi boljlera, koji sadrži popis dijelova na koje upućuju savjeti. Pojedinosti samih dijelova pohranjene su u tablici Dijelovi bojlera u bazu podataka InventarDB
  • Inženjeri, u kojem su navedeni tehničari koji su napisali svaki savjet.

Dio baze znanja aplikacije koji trenutačno sadrži samo rezervirano mjesto zaslona Preglednik. Maria želi implementirati sljedeću funkcionalnost:

  • Tehničar određuje termin za pretraživanje na zaslonu Pretraživanje radi pronalaženja svih odgovarajućih savjeta. Podudaranje bi moglo biti u nazivu dijela na koji se savjet odnosi, tekstu u predmetu ili tijelu savjeta ili imenu tehničara koji je stručnjak za određeni dio opreme.

  • Kad se pronađu svi odgovarajući savjeti, tehničar može odabrati savjet kako bi pregledao njegove detalje.

  • Tehničar također može dodati nove savjete u bazu znanja, kao i dodati bilješke i komentare postojećim savjetima.

    Baza znanja velika je i raste, a upiti u više tablica i stupaca mogu uključivati složenu logiku koja zahtijeva značajnu računsku snagu. Da bi smanjila opterećenje API-ja za web, Kiana odlučuje upotrijebiti Azure kognitivno pretraživane za osiguravanje funkcionalnosti pretraživanja, kao što je ranije opisano. Da bi podržala aplikaciju, Kiana odlučuje da API za web zahtijeva sljedeće radnje:

  • Detalji o određenom savjetu iz baze znanja potražite u tablici Savjeti.

  • Ažurirajte postojeći savjet iz baze znanja u tablici Savjeti.

  • Dodajte novi savjet iz baze znanja u tablicu Savjeti, što također može uključivati dodavanje redaka u tablice Dijelovi bojlera i Inženjeri ako navedeni dio ili inženjer trenutačno nema zabilježenih savjeta. Rutina koja zapravo izvodi logiku koja stoji iza dodavanja novog savjeta bit će implementirana kao logička aplikacija koja se poziva iz usluge Power Apps.

Definiranje operacija API-ja za web: terensko zakazivanje

Obveza zakazivanja tehničara zahtijeva ne samo postavljanje upita, dodavanje i uklanjanje obveza, već i bilježenje podataka o klijentima. Postojeći sustav obveza bilježi ove podatke u tri tablice u bazi podataka SchedulesDB:

  • Obveze, koje sadrže pojedinosti svake obveze, uključujući datum, vrijeme, problem, bilješke i tehničara dodijeljenog za zadatak.
  • Klijenti, koji sadrže detalje o svakom klijentu, uključujući njegovo ime, adresu i podatke za kontakt.
  • Inženjeri, koji navode svakog tehničara koji prisustvuje obvezi.

Napomena

Baza podataka zapravo sadrži četvrtu tablicu pod nazivom Status obveza. Ova tablica sadrži popis valjanih vrijednosti za status obveze i jednostavno je traženje koje upotrebljavaju drugi dijelovi postojećeg sustava obveza.

Kiana odlučuje da bi sljedeće aktivnosti bile korisne za dio aplikacije za terensko zakazivanje:

  • Pronađite sve obveze za određenog tehničara.
  • Pronađite sve obveze za trenutačni dan za određenog tehničara.
  • Pronađite sljedeću zakazanu obvezu za određenog tehničara.
  • Ažurirajte detalje obveze, poput dodavanja bilješki ili fotografije.
  • Pronađite detalje o klijentu.

Izrada API-ja za web: terensko upravljanje zalihama

Postojeći sustavi pohranjuju podatke pomoću Azure SQL baze podataka. Kiana odlučuje izgraditi API za web pomoću jezgre entitetskog okvira jer ovaj pristup može generirati puno koda koji automatski postavlja upite, umeće i ažurira podatke. Predložak API-ja za web koji pruža Microsoft također može izraditi Swaggerove opise koji opisuju svaku operaciju u API-ju. Ovi su opisi korisni za testiranje operacija za API. Mnogi alati mogu upotrebljavati ove informacije za integraciju API-ja s drugim uslugama, kao što je Upravljanje API-jevima usluge Azure.

Kiana je započela s funkcionalnošću inventara na terenu jer je ovo najjednostavniji dio. Operacije zaliha na terenu u API-ju za web postavljaju jednu tablicu, Dijelovi bojlera, u bazu podataka InventoryDB. Ova tablica sadrži stupce prikazane na sljedećoj slici.

Tablica Dijelovi bojlera koja prikazuje stupce ID, Naziv, ID kategorije, Cijena, Pregled, Broj na zalihi i URL slike.

Kiana je pri izradi API-ja za web upotrijebila pristup "prvo kodiranje". Ovom strategijom učinila je sljedeće:

  1. Definirala je vlastitu klasu C# modela koja je odražavala strukturu tablice Dijelovi bojlera u bazi podataka InventoryDB.

  2. Izradite kontekstnu klasu entitetskog okvira koje API za web upotrebljava za povezivanje s bazom podataka radi postavljanja upita.

  3. Konfigurirana je kontekstnu klasu za povezivanje s bazom podataka InventoryDB na usluzi Azure.

  4. Alati naredbenog retka entitetskog okvira upotrijebljeni su za generiranje klase kontrolera API-ja za web koja implementira zahtjeve HTTP REST za svaku od operacija koje se mogu izvršiti pomoću tablice Dijelovi bojlera.

  5. Upotrijebljen je Swagger API za testiranje API-ja za web.

Sljedeća slika prikazuje strukturu API-ja za web na visokoj razini.

Struktura visoke razine API-ja za web za inventar.

Kiana je upotrijebila sljedeći postupak za izradu API-ja za web koristeći alate za naredbene retke .NET 5.0 i Visual Studio Code:

  1. Otvorite prozor terminala u značajci Visual Studio Code.

    Novi prozor terminala u značajci VS Code.

  2. Pokrenite sljedeću naredbu za izradu novog projekta API-ja za web pod nazivom FieldEngineerApi.

    dotnet new webapi -o FieldEngineerApi
    
  3. Otvorite mapu FieldEngineerApi.

    Otvorite mapu FieldEngineerApi.

  4. Uklonite primjer kontrolera WeatherForecastController.cs i datoteku klase WeatherForecast.cs koju je izradio predložak API za web.

    Izbrišite datoteku WeatherForecast.

  5. Uprozoru Terminal dodajte sljedeće pakete i alate entitetskog okvira, zajedno s podrškom za upotrebu SQL poslužitelja.

    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-codegenerator
    
  6. Umapi FieldEngineerApi izradite novu mapu pod nazivom Modeli.

    Izradite mapu Modeli.

  7. U mapi Modeli izradite datoteku s kodom C# pod nazivom BoilerPart.cs.

    Izradite klasu Dio bojlera.

  8. U ovu datoteku dodajte sljedeća svojstva i polja. Ova svojstva i polja odražavaju strukturu tablice Dijelovi bojlera u bazi podataka 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; }
        }
    }
    
  9. U mapi Modeli izradite još jednu datoteku s kodom C# pod nazivom InventoryContext.cs. Dodajte sljedeći kod ovoj klasi. Klasa pruža vezu između kontrolera (koji će se izraditi sljedeći) i baze podataka.

    using Microsoft.EntityFrameworkCore;
    
    namespace FieldEngineerApi.Models
    {
        public class InventoryContext : DbContext
        {
            public InventoryContext(DbContextOptions<InventoryContext> options)
                : base(options)
            {
    
            }
    
            public DbSet\<BoilerPart\> BoilerParts { get; set; }
        }
    }
    
  10. Uredite datoteku appsettings.Development.json za projekt i dodajte sljedeći odjeljak Nizovi veze sa sljedećim nizom veze InventoryDB. Zamijenite <server name> nazivom poslužitelja baze podataka SQL koji ste izradili da čuva bazu podataka 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"
            }
        }
    }
    

    Važno

    Samo za potrebe ovog vodiča, niz veze sadrži korisnički ID i lozinku za bazu podataka. U proizvodnom sustavu nikada ne biste trebali pohraniti te stavke u čistom tekstu u konfiguracijsku datoteku.

  11. Uredite datoteku Startup.cs i dodajte sljedeće koristeći smjernice na popis na početku datoteke.

    using FieldEngineerApi.Models;
    using Microsoft.EntityFrameworkCore;
    
  12. U klasi Pokretanje pronađite metodu ConfigureServices. Dodajte sljedeću izjavu ovoj metodi.

    public void ConfigureServices(IServiceCollection services)
    {
    
        services.AddDbContext<InventoryContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("InventoryDB")));
    
        services.AddControllers();
        ...
    }
    
  13. Izmijenite metodu Konfiguriranje i omogućite korisničko sučelje Swagger čak i kad je aplikacija pokrenuta u produkcijskom načinu, kao što je prikazano (ova promjena uključuje premještanje dva poziva metode app.UseSwagger izvan izjave ako).

    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"));
    
        ...
    }
    

    Važno

    Ova promjena omogućuje otkrivanje krajnje točke Swagger za integraciju upravljanja API-jevima. Nakon što je upravljanje API-jevima konfigurirano, trebali biste premjestiti ovaj kod natrag u izjavu ako i ponovno uvedete API za web. Nikada ne ostavljajte krajnju točku Swagger otvorenom u proizvodnom sustavu.

  14. U prozoru Terminal pokrenite sljedeću naredbu za generiranje kontrolera Dijelovi bojlera iz klase modela Dio bojlera i kontekstne klase Kontekst zaliha.

    dotnet aspnet-codegenerator controller ^
        -name BoilerPartsController -async -api ^
         -m BoilerPart -dc InventoryContext -outDir Controllers
    

    Kontroler Dijelovi bojlera treba izraditi u mapi Kontroleri.

    [!NOTE] Znak završetka crte ^ prepoznaje samo Windows. Ako pokrenete Visual Studio Code u sustavu Linux, upotrijebite znak \.

  15. Otvorite datoteku Dijelove bojlera u mapi Kontroleri i pregledajte njezin sadržaj. Klasa Kontroler dijelova bojlera izlaže sljedeće metode REST:

    • GetBoilerParts (), koji vraća popis svih objekata Dio bojlera iz baze podataka.
    • GetBoilerPart (dugi ID), koji pronalazi detalje navedenog dijela bojlera.
    • PutBoilerPart (long id, BoilerPart boilerPart), koji ažurira dio bojlera u bazi podataka detaljima u objektu Dio bojlera koji je naveden kao parametar.
    • PostBoilerPart (BoilerPart kotlovPart), koji izrađuje novi dio bojlera.
    • DeleteBoilerPart(long id), koji uklanja određeni dio bojlera iz baze podataka.

    Napomena

    Aplikacija tehničara zahtijeva samo dvije metode Dohvaćanje, ali ostale su korisne za aplikaciju za stolna računala za upravljanje zalihama (nisu obuhvaćene ovim vodičem).

  16. Sastavite i izradite API za web.

    dotnet build
    

API za web trebao bi se izraditi bez prijavljivanja pogrešaka ili upozorenja.

Implementacija API-ja za web u Azure: terensko upravljanje zalihama

Kiana je implementirala i testirala API za web izvršavajući sljedeće zadatke:

  1. Koristeći proširenje Azure računa u značajci Visual Studio Code, prijavite se na svoju Azure pretplatu.

  2. Iz prozora terminala u značajci Visual Studio Code izradite novu grupu resursa u svojoj Azure pretplati pod nazivom webapi_rg. U sljedećoj naredbi zamijenite <location> najbližom regijom Azure.

    az group create ^
        --name webapi_rg ^
        --location <location>
    
  3. Izradite plan aplikacije Azure za pružanje resursa za hostiranje API-ja za web.

    az appservice plan create ^
        --name webapi_plan ^
        --resource-group webapi_rg ^
        --sku F1
    

    Napomena

    F1 je besplatni SKU za planove za uslugu aplikacije. Pruža ograničenu propusnost i kapacitet, a pogodan je samo za razvojne svrhe.

  4. Izradite web-aplikaciju Azure pomoću plana usluge aplikacije. Zamijenite <webapp name> jedinstvenim nazivom za web-aplikaciju.

    az webapp create ^
        --name <webapp name> ^
        --resource-group webapi_rg ^
        --plan webapi_plan
    
  5. U značajci Visual Studio Code uredite datoteku appSettings.json i dodajte isti niz veze koji ste prethodno napisali u datoteku appSettings.Development.json. Sjetite se zamijeniti <server name> nazivom poslužitelja baze podataka SQL koji ste izradili da čuva bazu podataka 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": "*"
    }
    
  6. U prozor Terminal spakirajte API za web spreman za uvođenje na Azure.

    dotnet publish -c Release -o ./publish
    

    Ova naredba sprema pakirane datoteke u mapu pod nazivom objave.

  7. U značajci Visual Studio Code desnom tipkom miša kliknite mapu objave, a zatim odaberite Uvođenje u web-aplikaciju.

    Uvedite web-aplikaciju iz značajke VS Code.

  8. Odaberite naziv web-aplikacije koju ste izradili ranije u 4. koraku (<webapp name>). U sljedećem je primjeru web-aplikacija imenovana my-fieldingineer-webapp.

    Odaberite web-aplikaciju.

  9. Pri upitu u djalogu značajke Visual Studio Code odaberite Uvedi kako biste prihvatili upozorenje i uveli web-aplikaciju.

    Upozorenje o uvođenju značajke VS Code.

  10. Provjerite je li web-aplikacija uspješno uvedena, a zatim posjetite web-mjesto.

    Pregledajte dijalog web-mjesta u VS Code.

  11. Web-mjesto otvorit će se u novom prozoru preglednika, ali prikazat će se pogreška HTTP 404 (nije pronađena). To je zato što su operacije API-ja za web dostupne putem krajnje točke api, a ne korijena web-mjesta. Promijenite URL u https://<webapp name>.azurewebsites.net/api/BoilerParts. Ovaj URI poziva metodu GetBoilerParts u kontroleru Dijelovi bojlera. API za web trebao bi biti u skladu s JSON dokumentom koji navodi sve dijelove bojlera u bazi podataka InventoryDB.

    Popis dijelova prikazan u web-pregledniku.

  12. Promijenite URL u pregledniku u https://<webapp name>. azurewebsites.net/swagger. Trebao bi se pojaviti API za Swagger. Ovo je grafičko korisničko sučelje koje razvojnom inženjeru omogućuje provjeru i testiranje svake od operacija u API-ju za web. Također djeluje kao koristan alat za dokumentaciju.

    Korisničko sučelje za Swagger koje prikazuje popis operacija.

  13. Odaberite DOHVAĆANJE uz krajnju točku / api/BoilerParts/{id}, a zatim odaberite Isprobaj.

    Zaslon "Isprobaj" u korisničkom sučelju Swagger

  14. U polje ID unesite ID dijela, a zatim odaberite Izvrši. Ova radnja poziva metodu GetBoilerParts u kontroleru Dijelovi bojlera. Vratit će JSON dokument s detaljima o dijelu ili pogrešku HTTP 404 ako u bazi podataka nije pronađen odgovarajući dio.

    Odgovor u korisničkom sučelju Swagger.

  15. Zatvorite web-preglednik i vratite se u Visual Studio Code.

Izrada i uvođenje API-ja za web: terenska baza znanja

Operacije baze znanja na terenu u API-ju za web rade u tri tablice u bazi znanja KnowledgeDB: Savjeti, Dijelovi bojlera i Inženjeri. Sljedeća slika prikazuje odnose među ovim tablicama i stupcima koje sadrže.

Odnosi tablica baze znanja.

Kiana je usvojila sličan pristup za bazu podataka terenske baze znanja koji je koristila za bazu podataka terenskog upravljanja zalihama. Izvršila je sljedeće zadatke:

  1. Izradite klase modela C# koje odražavaju strukturu tablica Savjeti, Dijelovi bojlera i Inženjeri u bazi podataka KnowledgeDB. Kôd svake od ovih klasa prikazan je u nastavku.

    Napomena

    Tablica Dijelovi bojlera u bazi podataka KnowledgeDB razlikuje se od tablice Dijelovi bojlera tablica u bazi podataka InventoryDB. Da bi se izbjegao sukob naziva, klase modela za tablice u bazi podataka KnowledgeDB imaju prefiks Baza znanja.

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

    Napomena

    ID inženjera je niz, a ne broj. To je zato što postojeći sustavi koriste GUID-ove za identifikaciju tehičara i korisnika.

    // 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; }
        }
    }
    
  2. Izradite još jednu kontekstnu klasu entitetskog okvira koje API za web upotrebljava za povezivanje s bazom podataka 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; }
        }
    }
    
  3. Uredite datoteku appsettings.Development.json za projekt i dodajte sljedeći niz veze KnowledgeDB u odjeljak Nizovi veze. Zamijenite <server name> nazivom poslužitelja baze podataka SQL koji ste izradili da čuva bazu podataka 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": {
            ...
            }
        }
    }
    

    Važno

    Samo za potrebe ovog vodiča, niz veze sadrži korisnički ID i lozinku za bazu podataka. U proizvodnom sustavu nikada ne biste trebali pohraniti te stavke u čistom tekstu u konfiguracijsku datoteku.

  4. Uredite datoteku Startup.cs i u metodu ConfigureServices dodajte sljedeće izjave.

    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();
        ...
    }
    

    Druga izjava kontrolira način na koji se podaci serijaliziraju kada se preuzmu. Neke klase modela imaju reference na druge klase modela, koje se pak mogu pozivati na druge klase modela. Neke od ovih referenci mogu rezultirati rekurzivnim petljama (entitet A upućuje na entitet B, koji upućuje natrag na entitet A, koji ponovno upućuje na entitet B i tako dalje). Opcija ReferenceLoopHandling uzrokuje da serijalizator ignorira takve petlje u podacima i vraća samo entitet i objekte na koje odmah upućuje, ali ne više.

  5. U prozoru Terminal pokrenite sljedeću naredbu za generiranje kontrolera iz klasa modela KnowledgeBaseBoilerTip, KnowledgeBaseBoilerPart i KnowledgeBaseEngineer i kontekstualne klase 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 Controllers
    

    Sva tri kontrolera treba izraditi u mapi Kontroleri.

  6. Uredite datoteku KnowledgeBaseBoilerPartController.cs. Ova datoteka sadrži kod za kontroler KnowledgeBaseBoilerPart. Treba slijediti isti obrazac kao i klasa BoilerPartsController klasa izrađena ranije, izlažući metode REST koje omogućavaju klijentu popis, upite, umetanje, ažuriranje i brisanje entiteta. Dodajte sljedeću metodu GetTipsForPart u kontroler.

    [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();
        }
        ...
    }
    

    Ova metoda vraća sve savjete iz baze znanja koji upućuju na navedeni dio. Šalje upit tablici Savjeti u bazi podataka putem objekta KnowledgeBaseContext kako bi se pronašli ti podaci.

  7. Otvorite datoteku KnowledgeBaseEngineerController.cs i dodajte sljedeću metodu u klasu 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();
        }
    
        ...
    }
    

    Metoda GetTipsForEngineer pronalazi sve savjete iz baze znanja koje je objavio navedeni inženjer.

  8. U prozoru Terminal kompajlirajte i izradite API za web.

    dotnet build
    

    API za web trebao bi se izraditi bez prijavljivanja pogrešaka ili upozorenja.

  9. Uredite datoteku appSettings.json i dodajte niz veze za bazu podataka KnowledgeDB. Ovaj bi niz trebao biti isti kao što ste prethodno napisali u datoteci 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": "*"
    }
    
  10. U prozor Terminal spakirajte API za web spreman za uvođenje na Azure.

    dotnet publish -c Release -o ./publish
    
  11. U značajci Visual Studio Code desnom tipkom miša kliknite mapu objave, a zatim odaberite Uvođenje u web-aplikaciju. Uvedite istu Azure web-aplikaciju koju ste prethodno izradili. Dopustite čarobnjaku da novi kôd prepiše postojećom web-aplikacijom.

  12. Kada je uvođenje završeno, pregledajte web-mjesto, ali promijenite URL u pregledniku u https://<webapp name>.azurewebsites.net/swagger. Operacije za kontolere KnowledgeBaseBoilerPart, KnowledgeBaseEngineer i KnowldgeBaseTip trebaju se navesti uz postojeće operacije Dijelovi bojlera. Provjerite uključuju li KnowledgeBaseBoilerPart uključuju operaciju DOHVAĆANJE za URI /api/KnowledgeBaseBoilerPart/{id}/Tips i operacije KnowledgeBaseEngineer uključuju operaciju DOHVAĆANJE za URI /api/KnowledgeBaseEngineer/{id}/Tips.

    Korisničko sučelje Swagger s novim operacijama.

Izrada i uvođenje API-ja za web: terensko zakazivanje

Operacije terenskog zakazivanja upotrebljavaju tablice Obveze, Statusi obveza (ovo je jednostavna tablica pretraživanja koja navodi valjane vrijednosti statusa obveze), Klijenti i Inženjeri što je prikazano na sljedećoj slici. Te su tablice pohranjene u bazi podataka SchedulesDB.

Odnosi tablica za obveze i zakazivanje.

Da bi izradila operacije API-ja za web za dio sustava za zakazivanje na terenu, Kiana je izvršila sljedeće zadatke:

  1. Izradite klase modela C# koji odražavaju strukturu tablica Status obveza, Obveze, Klijenti i Inženjeri u bazi podataka SchedulesDB. Kôd svake od ovih klasa prikazan je u nastavku.

    Napomena

    Klasa modela za tablicu Inženjeri nazvana je ScheduleEngineer kako bi se razlikovao od modela za tablicu Inženjeri u bazi podataka 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; }
        }
    }
    
  2. Izradite kontekstnu klasu entietskog okvira koje API za web upotrebljava za povezivanje s bazom podataka 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; }
        }
    }
    
  3. Uredite datoteku appsettings.Development.json za projekt i dodajte sljedeći niz veze SchedulesDB u odjeljak Nizovi veze. Zamijenite <server name> nazivom poslužitelja baze podataka SQL koji ste izradili da čuva bazu podataka 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": {
            ...
            }
        }
    }
    
  4. Uredite datoteku Startup.cs i u metodu ConfigureServices dodajte sljedeću izjavu.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<InventoryContext>...;
    
        services.AddDbContex\<KnowledgeBaseContext>...;
    
        services.AddDbContext<ScheduleContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("SchedulesDB")));
    
        services.AddControllers().AddNewtonsoftJson(...);
    
        ...
    }
    
  5. U prozoru Terminal pokrenite sljedeću naredbu za generiranje kontrolera iz klasa modela Obveza, Klijent i Zakaži inženjera i kontekstne klase Kontekst zakazivanja.

    Napomena

    Ne izrađujte zasebni kontroler za model Status obveze.

    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 Controllers
    
  6. Uredite datoteku AppointmentsController.cs. U klasi Kontroler obveza pronađite metodu Dohvaćanje obveza. Izmijenite izjavu povratak kako je prikazano. Ova promjena osigurava da se preuzimaju informacije Klijent, Inženjer i Status obveze preuzimaju kao dio operacije DOHVAĆANJE; ta se polja odnose na druge entitete koji bi inače ostali prazni zbog lijenog mehanizma učitavanja entitetskog okvira.

    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();
        }
    
        ...
    }
    
  7. U istoj datoteci izmijenite metodu Dohvati obvezu (dugi ID), kao što je prikazano.

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

    Ova verzija metode popunjava polja Klijenac, Inženjer i Status obveze obveze kada se preuzme (lijeno učitavanje inače bi ta polja ostavilo praznima).

  8. Pronađite metodu Stavljanje obveze i zamijenite je sljedećim kodom. Ova verzija metode Stavljanje obveze uzima polja u obvezi koja korisnik može izmijeniti u aplikaciji, a ne kompletan objekt Obveza.

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

    Napomena

    Općenito, operacije STAVLJANJE trebaju modificirati samo podatke koje bi korisnik trebao imati pravo ažurirati, a ne nužno svako polje u entitetu.

  9. Otvorite datoteku ScheduleEngineerController.cs i dodajte sljedeću metodu GetScheduleEngineerAppointments u klasu 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.
    
    
  10. 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();
        }
    
        ...
    }
    

    Metoda GetAppointments pronalazi sve obveze za određenog klijenta. Metoda GetNotes dobiva sve bilješke koje je tehničar dao tijekom prethodnih posjeta klijentu.

  11. Uredite datoteku appSettings.json i dodajte niz veze za bazu podataka KnowledgeDB. Ovaj bi niz trebao biti isti kao što ste prethodno napisali u datoteci 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": "*"
    }
    
  12. U prozoru Terminal kompajlirajte i izradite API za web.

    dotnet build
    

    API za web trebao bi se izraditi bez prijavljivanja pogrešaka ili upozorenja.

  13. U prozor Terminal spakirajte API za web spreman za uvođenje na Azure.

    dotnet publish -c Release -o ./publish
    
  14. U značajci Visual Studio Code desnom tipkom miša kliknite mapu objave, a zatim odaberite Uvođenje u web-aplikaciju. Uvedite istu Azure web-aplikaciju koju ste prethodno izradili. Dopustite čarobnjaku da novi kôd prepiše postojećom web-aplikacijom.

  15. Kada je uvođenje završeno, pregledajte web-mjesto, ali promijenite URL u pregledniku u https://<webapp name>.azurewebsites.net/swagger. Provjerite jesu li operacije za kontrolere Obveze, Klijent i Zakazivanje inženjerar sada dostupni.

API za web sada je spreman za ugradnju u aplikaciju.