Aufrufen einer Web-API über einen .NET-Client (C#)

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

Laden Sie abgeschlossenes Projekt herunter.

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

In diesem Tutorial wird eine Client-App geschrieben, die die folgende Web-API nutzt:

Action 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.

Der Einfachheit halber ist die Clientanwendung in diesem Tutorial 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 portablen Bibliotheken.

HINWEIS: Wenn Sie Basis-URLs und relative URIs als hartcodierte 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 hartcodierte Ressourcen-URIs an die HttpClient.GetAsync -Methode übergeben, schließen Sie keinen führenden Schrägstrich ein. So rufen Sie eine nach ID ab Product :

  1. client.BaseAddress = new Uri("https://localhost:5001/"); festlegen
  2. Fordern Sie eine an Product. Beispiel: client.GetAsync<Product>("api/products/4");.

Erstellen der Konsolenanwendung

Erstellen Sie in Visual Studio eine neue Windows-Konsolen-App mit dem Namen 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 vorangehende Code ist die vollständige Client-App.

RunAsync wird ausgeführt und blockiert, bis er abgeschlossen ist. Die meisten HttpClient-Methoden sind asynchron, da sie Netzwerk-E/A ausführen. Alle asynchronen Aufgaben werden in RunAsyncausgeführt. Normalerweise blockiert eine App den Standard Threads nicht, aber diese App lässt keine Interaktion zu.

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 den NuGet-Paket-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 in der Paket-Manager-Konsole (PMC) den folgenden Befehl ein:

Install-Package Microsoft.AspNet.WebApi.Client

Mit dem vorherigen Befehl werden dem Projekt die folgenden NuGet-Pakete hinzugefügt:

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

Newtonsoft.Json (auch bekannt als Json.NET) ist ein beliebtes hochleistungsfähiges 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 einen Product instance 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 eines neuen HttpClient-instance pro Anforderung.
  • Server unter hoher Last.

Das Erstellen eines neuen HttpClient-instance pro Anforderung kann die verfügbaren Sockets aufgebraucht haben.

Der folgende Code initialisiert den HttpClient-instance:

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 obige 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 nur, wenn der Port für die Server-App verwendet wird.
  • 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. Nach Abschluss der Methode wird eine HttpResponseMessage zurückgegeben, die die HTTP-Antwort enthält. Wenn der status Code 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 instance 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-Eigenschaftfalse, wenn die status ein Fehlercode ist. Wenn Sie HTTP-Fehlercodes lieber als Ausnahmen behandeln möchten, rufen Sie HttpResponseMessage.EnsureSuccessStatusCode für das Antwortobjekt auf. EnsureSuccessStatusCodelöst eine Ausnahme aus, wenn der status Code außerhalb des Bereichs 200–299 liegt. Beachten Sie, dass HttpClient aus anderen Gründen Ausnahmen auslösen kann, z. B. wenn für die Anforderung ein Zeitüberschreitung auftritt.

Media-Type Formatierer zum Deserialisieren

Wenn ReadAsAsync ohne Parameter aufgerufen wird, wird ein Standardsatz von Medienformatierern verwendet, um den Antworttext zu lesen. Die Standardformatierer unterstützen JSON-, XML- und Formular-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 Formatierern 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 instance 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:

  • Die Antwort 201 (Erstellt) sollte zurückgegeben werden.
  • Die Antwort sollte die URL der erstellten Ressourcen im Location-Header 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, mit der Ausnahme, 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. Sollte beispielsweise http://localhost:64195/api/products eine Liste von Produkten zurückgeben.

  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)