[C#] Tutorial „Client REST”

Introducere

Acest tutorial va învață câteva facilități din .NET Core și din limbajul C#. Veți învăța:
  • Bazele interfeței linie de comandă (CLI) a .NET Core.
  • O vedere de ansamblu a facilităților limbajului C#.
  • Organizarea dependențelor cu NuGet
  • Comunicații HTTP
  • Procesarea informației JSON
  • Organizarea configurației cu Atribute.
Veți construi o aplicație care trimite Cereri HTTP la un serviciu REST pe GitHub. Veți citi informații în format JSON, și converti acel pachet JSON în obiecte C#. In final, veți vedea cum să lucrați cu obiecte C#.

Sunt multe facilități în acest tutorial. Haideți să le construim una câte una.


Dacă preferați să urmăriți cu exemplul final pentru acest subiect, îl puteți descărca. Pentru instrucțiuni de descărcare, vedeți Samples and Tutorials.

Cerințe preliminare

Va trebui să vă configurați mașina să ruleze .NET Core. Dvs. puteți găsi instrucțiunile de instalare pe pagina .NET Core. Puteți rula această aplicație pe Windows, Linux, macOS sau într-un container Docker. Va trebui să vă instalați editorul dvs. favorit de cod. Descrierile de mai jos folosesc Visual Studio Code, care este un editor cu sursă deschisă, cross platform. Totuși, puteți folosi oricare unelte cu care vă simțiți confortabil.

Creați aplicația

Primul pas este să creați o nouă aplicație. Deschideți o linie de comandă și creați un nou director pentru aplicația dvs. Faceți-l directorul curent. Tastați comanda dotnet new console la linia de comandă. Aceasta creează fișierele de început pentru o aplicație de bază „Hello World”.

Inainte de a începe să faceți modificări, haideți să mergem prin pașii de a rula simpla aplicație Hello World. După crearea aplicației, tastați dotnet restore (vedeți nota) la linia de comandă. Această comandă rulează procesul de restaurare a pachetelor NuGet. NuGet este un manager de pachete .NET. Această comandă descarcă fiecare din dependențele lipsă pentru proiectul dvs. Fiindcă acesta este un proiect nou, nici una din dependențe nu sunt la locul lor, deci prima rulare va descărca framework-ul (cadrul de lucru) .NET Core. După acest pas inițial, dvs. va trebui să rulați dotnet restore (vedeți nota) doar când adăugați noi pachete-dependență, sau când actualizați versiunile oricăreia dintre dependențele dvs.

După restaurarea pachetelor, dvs. rulați dotnet build. Aceasta execută motorul de construcție și creează aplicația dvs. In final, dvs. executați dotnet run pentru a rula aplicația dvs.

Adăugarea de noi dependențe

Unul dintre scopurile cheie de design este să minimizeze dimensiunea instalației .NET. Dacă o aplicație are nevoie de biblioteci în plus pentru unele dintre facilitățile ei, dvs. adăugați aceste dependențe în fișierul dvs. de proiect C# (*.csproj). Pentru exemplul nostru, dvs. va trebui să adăugați pachetul System.Runtime.Serialization.Json astfel încât aplicația dvs. poate procesa răspunsuri JSON.

Deschideți fișierul dvs. proiect csproj. Prima linie a fișierului ar trebui să apară ca:

<Project Sdk="Microsoft.NET.Sdk">

Adăugați următoarele imediat după această linie:

<ItemGroup>
    <PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
</ItemGroup>


Majoritatea editorilor de cod vor oferi completare pentru diferite versiuni ale acestor biblioteci. De obicei dvs. veți dori să folosiți cea mai recentă versiune a oricărui pachet pe care îl adăugați. Totuși, este important să vă asigurați că versiunile tuturor pachetelor se potrivesc, și că ele se potrivesc și cu versiunea cadrului de lucru .NET Core Application.

După ce ați făcut aceste schimbări, dvs. ar trebui să rulați dotnet restore (vedeți nota) din nou astfel încât pachetul să fie instalat pe sistemul dvs.

Facerea de cereri Web

Acum sunteți pregătit să începeți să preluați date de pe web. In această aplicație, dvs. veți citi informații de la API-ul GitHub. Haideți să citim informații despre proiectele de sub umbrela Fundația .NET. Dvs. veți începe făcând cererea la API-ul GitHub pentru a prelua informații despre proiecte. Endpoint-ul pe care îl veți folosi este: https://api.github.com/orgs/dotnet/repos. Dvs. doriți să preluați toate informațiile despre aceste proiecte, deci veți folosi o cerere HTTP GET. Navigatorul dvs. folosește de asemenea cereri HTTP GET, deci dvs. puteți lipi acel URL în navigatorul dvs. pentru a vedea ce informații veți prelua și procesa.

Dvs. folosiți clasa HttpClient pentru a face cereri web. Ca toate API-urile .NET moderne, HttpClient suportă doar metode asincrone pentru API-urile sale de lungă durată. Începeți prin a face o metodă asincronă. Veți completa implementarea pe măsură ce construiți funcționalitatea aplicației. Începeți prin a deschide fișierul program.cs din directorul proiectului dvs. și adăugând următoarea metodă la clasa Program:

private static async Task ProcessRepositories()
{


}

Dvs. va trebui să adăugați o instrucțiune using în partea de sus a metodei dvs. Main astfel încât compilatorul C# să recunoască tipul Task:

using System.Threading.Tasks;

Dacă dvs. construiți proiectul dvs. în acest punct, veți primi un avertisment generat pentru această metodă, deoarece ea nu conține niciun operator await și va rula sincron. Ignorați aceasta pentru acum; dvs. veți adăuga operatori await pe măsură ce completați metoda.

In continuare, redenumiți namespace-ul definit în instrucțiunea namespace din implicitul ei ConsoleApp în WebAPIClient. Mai târziu vom defini o clasă repo în acest namespace.

In continuare, actualizați metoda Main să apeleze această metodă. Metoda ProcessRepositories întoarce un Task (sarcină), și dvs. nu ar trebui să ieșiți din program înainte ca sarcina să se finalizeze. In concluzie, trebuie să folosiți metoda Wait pentru a bloca și a aștepta pentru sarcină să se finalizeze:

static void Main(string[] args)
{
    ProcessRepositories().Wait();
}


Acum, dvs. aveți un program care nu face nimic, dar îl face asincron. Haideți să-l îmbunătățim.

Mai întâi vă trebuie un obiect care este capabil să preia date de pe web; dvs. puteți folosi un HttpClient pentru a face aceasta. Acest obiect tratează cererile și răspunsurile. Instanțiați o singură instanță a acelui tip în clasa Program în interiorul fișierului Program.cs.

namespace WebAPIClient
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();


        static void Main(string[] args)
        {
            //...
        }
    }
}


Haideți să mergem înapoi la metoda ProcessRepositories și să completăm o primă versiune a ei:

private static async Task ProcessRepositories()
{
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
    client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");


    var stringTask = client.GetStringAsync("https://api.github.com/orgs/dotnet/repos");

    var msg = await stringTask;
    Console.Write(msg);

}

Dvs. va trebui de asemenea să adăugați două noi instrucțiuni la începutul fișierului pentru ca acesta să se compileze:

using System.Net.Http;
using System.Net.Http.Headers;


Această primă versiune face o cerere web să citească lista tuturor depozitelor sub organizația fundației dotnet. (ID-ul GitHub pentru Fundația .NET este „dotnet”). Primele câteva linii configurează HttpClient-ul pentru această cerere. Mai întâi, el este configurat să accepte răspunsurile JSON GitHub. Acest format este simplu JSON. Următoarea linie adăugă un antet User Agent la toate cererile de la acest obiect. Aceste două antete sunt verificate de codul server GitHub, și sunt necesare pentru preluarea de informații de la GitHub.
După ce ați configurat HttpClient-ul, dvs. faceți o cerere web și primiți răspunsul. In această primă versiune, dvs. folosiți metoda convenabilă HttpClient.GetStringAsync(String). Această metodă confortabilă începe o sarcină care face cererea web, și atunci când cererea revine, ea citește fluxul răspuns și extrage conținutul din flux. Corpul răspunsului este întors ca un String. Șirul de caractere este disponibil când sarcina se termină.

Cele două linii finale ale acestei metode așteaptă după acea sarcină, și apoi tipăresc răspunsul la consolă. Construiți aplicația, și rulați-o. Avertismentul din construcție este dispărut acum, deoarece ProcessRepositories acum chiar conține un operator await. Veți vedea o lungă afișare de text în format JSON.

Procesarea rezultatului JSON

Până în acest punct, dvs. ați scris cod să preia răspuns de la un server web, si să afișeze textul care este conținut în acel răspuns. In continuare, haideți să convertim acest răspuns JSON în obiecte C#.

Serializatorul JSON convertește date JSON în obiecte C# (tipul object). Prima dvs. sarcină este să definiți un tip clasă C# care să conțină informațiile pe care dvs. le folosiți din acest răspuns. Haideți să construim aceasta încet, deci începeți cu un tip simplu C# care conține numele depozitului:

using System;

namespace WebAPIClient
{
    public class repo
    {
        public string name;
    }

}


Puneți codul de mai sus într-un nou fișier numit „repo.cs”. Această versiune a clasei reprezintă cea mai simplă cale de a procesa date JSON. Numele clasei și numele membrului se potrivesc cu numele folosite în pachetul JSON, în loc de a urma convențiile C#. Veți repara aceasta oferind câteva atribute de configurare mai târziu. Această clasă demonstrează o altă importantă facilitate a serializării și deserializării JSON: Nu toate câmpurile din pachetul JSON sunt parte a acestei clase. Serializatorul JSON va ignora informația care nu este inclusă în tipul clasă folosit. Această facilitate face mai ușor să se creeze tipuri care lucrează cu doar o submulțime a câmpurilor din pachetul JSON.

Acum că dvs. ați creat tipul, haideți să-l deserializăm. Dvs. va trebui să creați un obiect DataContractJsonSerializer. Acest obiect trebuie să știe tipul CLR așteptat pentru pachetul JSON pe care îl preia. Acest pachet de la GitHub conține o secvență de depozite, deci un List<repo> este tipul corect. Adăugați următoarea linie la metoda dvs. ProcessRepositories:

var serializer = new DataContractJsonSerializer(typeof(List<repo>));

Dvs. folosiți două noi namespace-uri, deci va trebui să adăugați următoarele de asemenea:

using System.Collections.Generic;
using System.Runtime.Serialization.Json;


In continuare, dvs. veți folosi serializatorul pentru a converti JSON în obiecte C#. Inlocuiți apelul la GetStringAsync(String) în metoda dvs. ProcessRepositories cu următoarele două linii:

var streamTask = client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
var repositories = serializer.ReadObject(await streamTask) as List<repo>;


Observați că acum dvs. folosiți GetStreamAsync(String) în locul lui GetStringAsync(String). Serializatorul folosește un flux (en. stream) în loc de un șir de caractere ca sursă. Haideți să explicăm două facilități ale limbajului C# care sunt folosite în a doua linie de mai sus. Argumentul către ReadObject(Stream) este o expresie await. Expresiile await pot apărea aproape oriunde în codul dvs, chiar dacă până acum, dvs. le-ați văzut doar ca parte a unei instrucțiuni de atribuire.

In al doilea rând, operatorul as convertește de la tipul din timpul compilării object la List<repo>. Declarația lui ReadObject(Stream) declară că ea întoarce un obiect de tipul System.Object. ReadObject(Stream) va întoarce tipul specificat de dvs. când dvs. l-ați construit (List<repo> în acest tutorial). Dacă conversia nu reușește, operatorul as evaluează la null, în loc de a arunca o excepție.

Dvs. aproape ați analizat această secțiune. Acum că dvs. ați convertit JSON-ul în obiecte C#, haideți să afișăm numele fiecărui depozit. Inlocuiți liniile care spun:

var msg = await stringTask;
Console.Write(msg);

cu următoarele:

foreach (var repo in repositories)
    Console.WriteLine(repo.name);


Compilați și rulați aplicația. Ea va tipări numele depozitelor care sunt parte din Fundația .NET.

Controlarea serializării

Inainte de a adăuga mai multe facilități, haideți să ne adresăm tipului repo și să-l facem să urmeze convenții C# mai standarde. Veți face aceasta adnotând tipul repo cu atribute care controlează cum serializatorul JSON lucrează. In cazul dvs., veți folosi aceste atribute pentru a defini o mapare între numele cheilor JSON și numele claselor și membrilor C#. Cele două atribute folosite sunt atributul DataContract și atributul DataMember. Prin convenție, toate clasele Attribute se termină cu sufixul Attribute. Totuși, nu trebuie să folosiți acel sufix când aplicați un atribut.

Atributele DataContract și DataMember sunt într-o bibliotecă diferită, deci dvs. va trebui să adăugați acea bibliotecă la fișierul dvs. de proiect C# ca o dependență. Adăugați următoarea linie la secțiunea <ItemGroup> a fișierului dvs. proiect:

<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />

După ce salvați fișierul, rulați dotnet restore (vedeți nota) pentru a obține acest pachet.

In continuare, deschideți fișierul repo.cs. Haideți să schimbăm numele să folosească Pascal Case, și să spună complet numele Repository. Noi încă mai dorim să mapăm nodurile JSON „repo” la acest tip, deci dvs. va trebui să adăugați atributul DataContract la declarația clasei. Dvs. veți seta proprietatea Name a atributului cu numele nodurilor JSON care mapează la acest tip:

[DataContract(Name="repo")]
public class Repository



DataContractAttribute este un membru al namespace-ului System.Runtime.Serialization, deci dvs. va trebui să adăugați instrucțiunea using potrivită la începutul fișierului.

using System.Runtime.Serialization;

Dvs. ați schimbat numele clasei repo în Repository, deci dvs. va trebui să faceți aceeași schimbare de nume în Program.cs (unii editori ar putea suporta refactorizarea de redenumire care va face această schimbare automat:)

var serializer = new DataContractJsonSerializer(typeof(List<Repository>));

// ...

var repositories = serializer.ReadObject(await streamTask) as List<Repository>;
Acum, haideți să facem aceeași schimbare cu câmpul name folosind clasa DataMemberAttribute. Faceți următoarele schimbări declarației câmpului name în repo.cs:

[DataMember(Name="name")]
public string Name;


Această schimbare înseamnă că dvs. va trebui să schimbați codul care scrie numele fiecărui depozit în program.cs:

Console.WriteLine(repo.Name);

Dați un dotnet build urmat de un dotnet run să vă asigurați că ați făcut mapările corect. Dvs. ar trebui să vedeți aceeași ieșire ca înainte. Inainte de a procesa mai multe proprietăți de pe serverul web, haideți să facem o schimbare în plus la clasa Repository. Membrul Name este un câmp public accesibil. Aceasta nu este o bună practică orientată pe obiecte, deci haideți să îl schimbăm într-o proprietate. Pentru scopurile noastre, nu ne trebuie niciun cod specific să ruleze când citim sau scriem proprietatea, dar schimbând-o într-o proprietate face mai ușor să adăugăm aceste schimbări mai târziu fără să stricăm vreun cod care folosește clasa Repository.

Ștergeți definiția de câmp, și înlocuiți-o cu o proprietate auto-implementată:


public string Name { get; set; }

Compilatorul generează corpurile accesorilor get și set, și de asemenea un câmp privat să rețină numele. Ar fi similar cu următorul cod pe care dvs. l-ați putea tasta manual:

public string Name
{
    get { return this._name; }
    set { this._name = value; }
}

private string _name;


Haideți să mai facem o schimbare înainte de a adăuga noi facilități. Metoda ProcessRepositories poate face muncă asincron și să întoarcă o colecție de depozite. Haideți să întoarcem List<Repository>-ul din acea metodă, și să mutăm codul care scrie informația în metoda Main.

Schimbați semnătura lui ProcessRepositories să întoarcă o sarcină al cărei rezultat este o listă de obiecte Repository:

private static async Task<List<Repository>> ProcessRepositories()

Apoi, doar întoarceți depozitele după procesarea răspunsului JSON:

var repositories = serializer.ReadObject(await streamTask) as List<Repository>;
return repositories;


Compilatorul generează obiectul Task<T> pentru întoarcere deoarece dvs. ați marcat această metodă ca async. Apoi, haideți să modificăm metoda Main astfel încât ea capturează aceste rezultate și scrie numele fiecărui depozit la consolă. Metoda dvs. Main arată acum astfel:

public static void Main(string[] args)
{
    var repositories = ProcessRepositories().Result;


    foreach (var repo in repositories)
        Console.WriteLine(repo.Name);
}


Accesarea proprietății Result a unui Task (sarcină) blochează până când sarcina s-a finalizat. In mod normal, dvs. ați prefera să „await” (să așteptați) completarea sarcinii, la fel ca în metoda ProcessRepositories, dar acest lucru nu este permis în metoda Main.

Citirea mai multor informații

Haideți să finalizăm aceasta procesând încă câteva din proprietățile din pachetul JSON care este trimis din API-ul GitHub. Dvs. nu veți dori să apucați tot, dar adăugând câteva proprietăți va demonstra încă câteva facilități ale limbajului C#.

Haideți să începem prin a adăuga încă câteva tipuri simple la definiția clasei Repository. Adăugați aceste proprietăți la acea clasă:

[DataMember(Name="description")]
public string Description { get; set; }


[DataMember(Name="html_url")]
public Uri GitHubHomeUrl { get; set; }


[DataMember(Name="homepage")]
public Uri Homepage { get; set; }


[DataMember(Name="watchers")]
public int Watchers { get; set; }


Aceste proprietăți au conversii incorporate din tipul șir (ceea ce este ceea ce pachetele JSON conțin) către tipul țintă. Tipul Uri s-ar putea să fie nou pentru dvs. El reprezintă un URI, sau în acest caz, un URL. In cazul tipurilor Uri și int, dacă pachetul JSON conține date care nu se convertesc la tipul țintă, acțiunea de serializare va arunca o excepție.

Odată ce ați adăugat acestea, actualizați metoda Main să afișeze aceste elemente:

foreach (var repo in repositories)
{
    Console.WriteLine(repo.Name);
    Console.WriteLine(repo.Description);
    Console.WriteLine(repo.GitHubHomeUrl);
    Console.WriteLine(repo.Homepage);
    Console.WriteLine(repo.Watchers);
    Console.WriteLine();
}


Ca un pas final, haideți să adăugăm informația cu ultima operație push. Această informație este formatată în acest mod în răspunsul JSON:

2016-02-08T21:27:00Z

Acest format nu urmează nici unul din formatele standard .NET DateTime. De aceea, dvs. va trebui să scrieți o metodă personalizată de convertire. De asemenea dvs. probabil nu doriți șirul brut expus utilizatorilor clasei Repository. Atributele pot ajuta să controlăm aceasta de asemenea. Mai întâi, definiți o proprietate private care va reține reprezentarea șir a datei și timpului în clasa dvs. Repository:

[DataMember(Name="pushed_at")]
private string JsonDate { get; set; }


Atributul DataMember informează serializatorul că aceasta ar trebui să fie procesată, chiar dacă nu este un membru public. In continuare, dvs. trebuie să scrieți o proprietate publică doar-pentru-citire care convertește șirul într-un obiect DateTime valid, și întoarce acel DateTime:

[IgnoreDataMember]
public DateTime LastPush
{
    get
    {
        return DateTime.ParseExact(JsonDate, "yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture);
    }
}


Haideți să trecem în revistă noile constructe de mai sus. Atributul IgnoreDataMember instruiește serializatorul că acest tip ar trebui să nu fie citit sau scris din/în niciun obiect JSON. Această proprietate conține doar un accesor get. Nu există un accesor set. Acesta este modul cum definiți o proprietate doar-pentru-citire în C#. (Da, dvs. puteți crea proprietăți doar-pentru-scriere în C#, dar valoarea lor este limitată.) Metda ParseExact(String, String, IFormatProvider) analizează un șir și creează un obiect DateTime folosind un format de dată furnizat, și adaugă metadate în plus la DateTime folosind un obiect CultureInfo. Dacă operația de analizare eșuează, accesorul proprietății aruncă o excepție.

Pentru a folosi InvariantCulture, dvs. va trebui să adăugați namespace-ul System.Globalization la instrucțiunile using în repo.cs:

using System.Globalization;

La final, adăugați încă o instrucțiune de ieșire în consolă, și sunteți pregătit să construiți și să rulați această aplicație din nou:

Console.WriteLine(repo.LastPush);

Versiunea dvs. a trebui să se potrivească acum cu exemplul finalizat.

Concluzie

Acest tutorial v-a arătat cum să faceți cereri web, să analizați rezultatul, și să afișați proprietăți ale acestor rezultate. Dvs. ați adăugat de asemenea noi pachete ca dependențe în proiectul dvs. Ați văzut câteva din facilitățile limbajului C# care suportă tehnici orientate pe obiecte.

Notă. Începând cu .NET Core 2.0, dvs. nu trebuie să rulați dotnet restore deoarece ea este rulată implicit de toate comenzile care cer o restaurare să aibă loc, cum sunt dotnet new, dotnet build și dotnet run. Este încă o comandă validă în unele scenarii în care a face o restaurare explicită are sens, cum ar fi construcții în integrare continuă în Azure DevOps Services sau în sisteme de construcție care cer să controleze explicit tipul la care restaurarea are loc.


Tradus din această pagină oficială de documentație Microsoft.

Niciun comentariu:

Trimiteți un comentariu