Aufrufen einer Web-API aus einem .NET-Client (C#)

Dieser Inhalt ist für eine frühere Version von .NET. Neue Entwicklung sollte ASP.NET Core verwenden. Weitere Informationen zur Verwendung ASP.NET Core Web-API finden Sie unter:

Laden Sie das abgeschlossene Projekt herunter.

In diesem Lernprogramm wird gezeigt, wie Sie eine Web-API aus einer .NET-Anwendung mithilfe von System.Net.HttpClient aufrufen.

In diesem Lernprogramm wird eine Client-App geschrieben, die die folgende Web-API verwendet:

Aktion HTTP-Methode Relativer URI
Abrufen eines Produkts nach ID GET /api/products/id
Erstellen eines neuen Produkts POST /api/products
Aktualisieren eines Produkts PUT /api/products/id
Löschen eines Produkts DELETE /api/products/id

Informationen zum Implementieren dieser API mit ASP.NET-Web-API finden Sie unter Erstellen einer Web-API, die CRUD-Vorgänge unterstützt.

Die Clientanwendung in diesem Lernprogramm ist eine Windows-Konsolenanwendung. HttpClient wird auch für Windows Phone- und Windows Store-Apps unterstützt. Weitere Informationen finden Sie unter Schreiben von Web-API-Clientcode für mehrere Plattformen mit tragbaren Bibliotheken

HINWEIS: Wenn Sie Basis-URLs und relative URIs als hart codierte Werte übergeben, beachten Sie die Regeln für die Verwendung der HttpClient API. Die HttpClient.BaseAddress Eigenschaft sollte auf eine Adresse mit einem nachgestellten Schrägstrich (/) festgelegt werden. Wenn Sie beispielsweise hart codierte Ressourcen-URIs an die HttpClient.GetAsync Methode übergeben, enthalten Sie keine führenden Schrägstriche. So erhalten Sie eine Product NACH-ID:

  1. Festgelegt client.BaseAddress = new Uri("https://localhost:5001/");
  2. Anfordern eines Product. Beispiel: client.GetAsync<Product>("api/products/4");.

Erstellen der Konsolenanwendung

Erstellen Sie in Visual Studio eine neue Windows-Konsolen-App namens HttpClientSample , und fügen Sie den folgenden Code ein:

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace HttpClientSample
{
    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }

    class Program
    {
        static HttpClient client = new HttpClient();

        static void ShowProduct(Product product)
        {
            Console.WriteLine($"Name: {product.Name}\tPrice: " +
                $"{product.Price}\tCategory: {product.Category}");
        }

        static async Task<Uri> CreateProductAsync(Product product)
        {
            HttpResponseMessage response = await client.PostAsJsonAsync(
                "api/products", product);
            response.EnsureSuccessStatusCode();

            // return URI of the created resource.
            return response.Headers.Location;
        }

        static async Task<Product> GetProductAsync(string path)
        {
            Product product = null;
            HttpResponseMessage response = await client.GetAsync(path);
            if (response.IsSuccessStatusCode)
            {
                product = await response.Content.ReadAsAsync<Product>();
            }
            return product;
        }

        static async Task<Product> UpdateProductAsync(Product product)
        {
            HttpResponseMessage response = await client.PutAsJsonAsync(
                $"api/products/{product.Id}", product);
            response.EnsureSuccessStatusCode();

            // Deserialize the updated product from the response body.
            product = await response.Content.ReadAsAsync<Product>();
            return product;
        }

        static async Task<HttpStatusCode> DeleteProductAsync(string id)
        {
            HttpResponseMessage response = await client.DeleteAsync(
                $"api/products/{id}");
            return response.StatusCode;
        }

        static void Main()
        {
            RunAsync().GetAwaiter().GetResult();
        }

        static async Task RunAsync()
        {
            // Update port # in the following line.
            client.BaseAddress = new Uri("http://localhost:64195/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            try
            {
                // Create a new product
                Product product = new Product
                {
                    Name = "Gizmo",
                    Price = 100,
                    Category = "Widgets"
                };

                var url = await CreateProductAsync(product);
                Console.WriteLine($"Created at {url}");

                // Get the product
                product = await GetProductAsync(url.PathAndQuery);
                ShowProduct(product);

                // Update the product
                Console.WriteLine("Updating price...");
                product.Price = 80;
                await UpdateProductAsync(product);

                // Get the updated product
                product = await GetProductAsync(url.PathAndQuery);
                ShowProduct(product);

                // Delete the product
                var statusCode = await DeleteProductAsync(product.Id);
                Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadLine();
        }
    }
}

Der vorherige Code ist die vollständige Client-App.

RunAsync wird ausgeführt und blockiert, bis es abgeschlossen ist. Die meisten HttpClient-Methoden sind asynchron, da sie Netzwerk-I/O ausführen. Alle asynchronen Aufgaben werden innerhalb RunAsyncausgeführt. Normalerweise blockiert eine App den Hauptthread nicht, aber diese App erlaubt keine Interaktion.

static async Task RunAsync()
{
    // Update port # in the following line.
    client.BaseAddress = new Uri("http://localhost:64195/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

    try
    {
        // Create a new product
        Product product = new Product
        {
            Name = "Gizmo",
            Price = 100,
            Category = "Widgets"
        };

        var url = await CreateProductAsync(product);
        Console.WriteLine($"Created at {url}");

        // Get the product
        product = await GetProductAsync(url.PathAndQuery);
        ShowProduct(product);

        // Update the product
        Console.WriteLine("Updating price...");
        product.Price = 80;
        await UpdateProductAsync(product);

        // Get the updated product
        product = await GetProductAsync(url.PathAndQuery);
        ShowProduct(product);

        // Delete the product
        var statusCode = await DeleteProductAsync(product.Id);
        Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");

    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

    Console.ReadLine();
}

Installieren der Web-API-Clientbibliotheken

Verwenden Sie NuGet Package Manager, um das Web-API-Clientbibliothekspaket zu installieren.

Wählen Sie im Menü Extras die Optionen NuGet-Paket-Manager>Paket-Manager-Konsole aus. Geben Sie im Paket-Manager-Konsole (PMC) den folgenden Befehl ein:

Install-Package Microsoft.AspNet.WebApi.Client

Der vorherige Befehl fügt dem Projekt die folgenden NuGet-Pakete hinzu:

  • Microsoft.AspNet.WebApi.Client
  • Newtonsoft.Json

Newtonsoft.Json (auch bekannt als Json.NET) ist ein beliebtes JSON-Framework für .NET.

Hinzufügen einer Modellklasse

Überprüfen Sie die Product-Klasse:

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

Diese Klasse entspricht dem Datenmodell, das von der Web-API verwendet wird. Eine App kann httpClient verwenden, um eine Product Instanz aus einer HTTP-Antwort zu lesen. Die App muss keinen Deserialisierungscode schreiben.

Erstellen und Initialisieren von HttpClient

Untersuchen Sie die statische HttpClient-Eigenschaft :

static HttpClient client = new HttpClient();

HttpClient soll einmal instanziiert und während der gesamten Lebensdauer einer Anwendung wiederverwendet werden. Die folgenden Bedingungen können zu SocketException-Fehlern führen:

  • Erstellen einer neuen HttpClient-Instanz pro Anforderung.
  • Server unter schwerem Laden.

Das Erstellen einer neuen HttpClient-Instanz pro Anforderung kann die verfügbaren Sockets ausschöpfen.

Der folgende Code initialisiert die HttpClient-Instanz :

static async Task RunAsync()
{
    // Update port # in the following line.
    client.BaseAddress = new Uri("http://localhost:64195/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

Der vorangehende Code:

  • Legt den Basis-URI für HTTP-Anforderungen fest. Ändern Sie die Portnummer in den Port, der in der Server-App verwendet wird. Die App funktioniert nicht, es sei denn, portiert für die Server-App wird verwendet.
  • Legt den Accept-Header auf "application/json" fest. Durch Festlegen dieses Headers wird der Server aufgefordert, Daten im JSON-Format zu senden.

Senden einer GET-Anforderung zum Abrufen einer Ressource

Der folgende Code sendet eine GET-Anforderung für ein Produkt:

static async Task<Product> GetProductAsync(string path)
{
    Product product = null;
    HttpResponseMessage response = await client.GetAsync(path);
    if (response.IsSuccessStatusCode)
    {
        product = await response.Content.ReadAsAsync<Product>();
    }
    return product;
}

Die GetAsync-Methode sendet die HTTP-GET-Anforderung . Wenn die Methode abgeschlossen ist, gibt sie eine HttpResponseMessage zurück, die die HTTP-Antwort enthält. Wenn der Statuscode in der Antwort ein Erfolgscode ist, enthält der Antworttext die JSON-Darstellung eines Produkts. Rufen Sie ReadAsAsync auf, um die JSON-Nutzlast in eine Product Instanz zu deserialisieren. Die ReadAsAsync-Methode ist asynchron, da der Antworttext beliebig groß sein kann.

HttpClient löst keine Ausnahme aus, wenn die HTTP-Antwort einen Fehlercode enthält. Stattdessen ist die IsSuccessStatusCode-Eigenschaftfalsch , wenn der Status ein Fehlercode ist. Wenn Sie HTTP-Fehlercodes als Ausnahmen behandeln möchten, rufen Sie httpResponseMessage.EnsureSuccessStatusCode auf dem Antwortobjekt auf. EnsureSuccessStatusCode löst eine Ausnahme aus, wenn der Statuscode außerhalb des Bereichs 200-299 fällt. Beachten Sie, dass HttpClient Ausnahmen aus anderen Gründen auslösen kann , z. B. wenn die Anforderung auszeit.

Media-Type Formatters zu Deserialisieren

Wenn ReadAsAsync ohne Parameter aufgerufen wird, wird eine Standardmenge von Medienformatierern verwendet, um den Antworttext zu lesen. Die Standardformatierer unterstützen JSON-, XML- und Form-URL-codierte Daten.

Anstatt die Standardformatierer zu verwenden, können Sie eine Liste von Formatierern für die ReadAsAsync-Methode bereitstellen. Die Verwendung einer Liste von Formattern ist nützlich, wenn Sie über einen benutzerdefinierten Medientypformatierer verfügen:

var formatters = new List<MediaTypeFormatter>() {
    new MyCustomFormatter(),
    new JsonMediaTypeFormatter(),
    new XmlMediaTypeFormatter()
};
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);

Weitere Informationen finden Sie unter Medienformatierer in ASP.NET-Web-API 2

Senden einer POST-Anforderung zum Erstellen einer Ressource

Der folgende Code sendet eine POST-Anforderung, die eine Product Instanz im JSON-Format enthält:

static async Task<Uri> CreateProductAsync(Product product)
{
    HttpResponseMessage response = await client.PostAsJsonAsync(
        "api/products", product);
    response.EnsureSuccessStatusCode();

    // return URI of the created resource.
    return response.Headers.Location;
}

Die PostAsJsonAsync-Methode :

  • Serialisiert ein Objekt in JSON.
  • Sendet die JSON-Nutzlast in einer POST-Anforderung.

Wenn die Anforderung erfolgreich ist:

  • Es sollte eine Antwort auf 201 (Erstellt) zurückgegeben werden.
  • Die Antwort sollte die URL der erstellten Ressourcen in der Location-Kopfzeile enthalten.

Senden einer PUT-Anforderung zum Aktualisieren einer Ressource

Der folgende Code sendet eine PUT-Anforderung, um ein Produkt zu aktualisieren:

static async Task<Product> UpdateProductAsync(Product product)
{
    HttpResponseMessage response = await client.PutAsJsonAsync(
        $"api/products/{product.Id}", product);
    response.EnsureSuccessStatusCode();

    // Deserialize the updated product from the response body.
    product = await response.Content.ReadAsAsync<Product>();
    return product;
}

Die PutAsJsonAsync-Methode funktioniert wie PostAsJsonAsync, außer dass sie anstelle von POST eine PUT-Anforderung sendet.

Senden einer DELETE-Anforderung zum Löschen einer Ressource

Der folgende Code sendet eine DELETE-Anforderung zum Löschen eines Produkts:

static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
    HttpResponseMessage response = await client.DeleteAsync(
        $"api/products/{id}");
    return response.StatusCode;
}

Wie GET verfügt eine DELETE-Anforderung nicht über einen Anforderungstext. Sie müssen kein JSON- oder XML-Format mit DELETE angeben.

Testen des Beispiels

So testen Sie die Client-App:

  1. Laden Sie die Server-App herunter und führen Sie sie aus. Überprüfen Sie, ob die Server-App funktioniert. http://localhost:64195/api/products Beispielsweise sollte eine Liste von Produkten zurückgegeben werden.

  2. Legen Sie den Basis-URI für HTTP-Anforderungen fest. Ändern Sie die Portnummer in den Port, der in der Server-App verwendet wird.

    static async Task RunAsync()
    {
        // Update port # in the following line.
        client.BaseAddress = new Uri("http://localhost:64195/");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    
  3. Führen Sie die Client-App aus. Es wird die folgende Ausgabe generiert:

    Created at http://localhost:64195/api/products/4
    Name: Gizmo     Price: 100.0    Category: Widgets
    Updating price...
    Name: Gizmo     Price: 80.0     Category: Widgets
    Deleted (HTTP Status = 204)