Tutorial: Erstellen einer Web-API mit ASP.NET Core

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Von Rick Anderson und Kirk Larkin

In diesem Tutorial lernen Sie die Grundlagen der Erstellung einer controllerbasierten Web-API, die eine Datenbank verwendet. Ein anderer Ansatz zum Erstellen von APIs in ASP.NET Core besteht in der Erstellung von minimalen APIs. Hilfe bei der Entscheidung zwischen minimalen APIs und controllerbasierten APIs finden Sie in der Übersicht über APIs. Anleitungen zum Erstellen einer minimalen API finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.

Übersicht

In diesem Tutorial wird die folgende API erstellt:

API Beschreibung Anforderungstext Antworttext
GET /api/todoitems Alle To-do-Elemente abrufen Keine Array von To-do-Elementen
GET /api/todoitems/{id} Ein Element nach ID abrufen Keine To-do-Element
POST /api/todoitems Neues Element hinzufügen To-do-Element To-do-Element
PUT /api/todoitems/{id} Vorhandenes Element aktualisieren To-do-Element Keine
DELETE /api/todoitems/{id}     Löschen eines Elements Keine Keine

Das folgende Diagramm zeigt den Entwurf der App.

Der Client wird durch ein Rechteck auf der linken Seite dargestellt. Er sendet eine Anforderung und erhält eine Antwort von der Anwendung, die in einem Rechteck auf der rechten Seite dargestellt ist. Innerhalb des Anwendungsrechtecks stellen drei Rechtecke den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung kommt im Controller der Anwendung an, und Lese-/Schreibvorgänge finden zwischen dem Controller und der Datenzugriffsschicht statt. Das Modell wird serialisiert und in der Antwort an den Client zurückgegeben.

Voraussetzungen

Erstellen eines Webprojekts

  • Klicken Sie im Menü Datei auf Neu>Projekt.
  • Geben Sie Web-API in das Suchfeld ein.
  • Wählen Sie die ASP.NET Core-Web-API-Vorlage aus, und klicken Sie auf Weiter.
  • Geben Sie im Dialogfeld Neues Projekt konfigurieren dem Projekt den Namen TodoApi, und wählen Sie dann Weiter aus.
  • Im Dialogfeld Zusätzliche Informationen:
    • Bestätigen Sie, dass das Framework auf .NET 8.0 (Langfristiger Support) festgelegt ist.
    • Vergewissern Sie sich, dass das Kontrollkästchen Controller verwenden (zur Verwendung minimaler APIs deaktivieren) aktiviert ist.
    • Vergewissern Sie sich, dass das Kontrollkästchen OpenAPI-Unterstützung aktivieren aktiviert ist.
    • Klicken Sie auf Erstellen.

Hinzufügen eines NuGet-Pakets

Ein NuGet-Paket muss hinzugefügt werden, um die in diesem Tutorial verwendete Datenbank zu unterstützen.

  • Klicken Sie im Menü Extras auf NuGet-Paket-Manager > NuGet-Pakete für Projektmappe verwalten.
  • Wählen Sie die Registerkarte Durchsuchen aus.
  • Geben Sie Microsoft.EntityFrameworkCore.InMemory in das Suchfeld ein, und wählen Sie Microsoft.EntityFrameworkCore.InMemory aus.
  • Aktivieren Sie das Kontrollkästchen Projekt im rechten Bereich, und klicken Sie dann auf Installieren.

Hinweis

Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

Testen des Projekts

Die Projektvorlage erstellt eine WeatherForecast-API mit Unterstützung für Swagger.

Drücken Sie STRG+F5, um die Ausführung ohne den Debugger zu starten.

Visual Studio zeigt das folgende Dialogfeld an, wenn ein Projekt noch nicht für die Verwendung von SSL konfiguriert ist:

Dieses Projekt ist für die Verwendung von SSL konfiguriert. Um SSL-Warnungen im Browser zu vermeiden, können Sie dem selbstsignierten Zertifikat vertrauen, das IIS Express generiert hat. Möchten Sie dem SSL-Zertifikat von IIS Express vertrauen?

Wählen Sie Ja aus, wenn Sie dem IIS Express-SLL-Zertifikat vertrauen möchten.

Das folgende Dialogfeld wird angezeigt:

Dialogfeld „Sicherheitswarnung“

Klicken Sie auf Ja, wenn Sie zustimmen möchten, dass das Entwicklungszertifikat vertrauenswürdig ist.

Informationen dazu, wie Sie dem Firefox-Browser vertrauen, finden Sie unter Firefox-Zertifikatfehler SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio startet einen Standardbrowser und navigiert zu https://localhost:<port>/swagger/index.html, wobei es sich bei <port> um eine zufällig ausgewählte Portnummer handelt.

Die Swagger-Seite /swagger/index.html wird angezeigt. Wählen Sie GET>Tryit out>Execute (GET > Testen> Ausführen) aus. Die Seite zeigt Folgendes an:

  • Der Curl-Befehl, zum Testen der WeatherForecast-API.
  • Die URL zum Testen der WeatherForecast-API.
  • Der Antwortcode, der Text und die Header.
  • Ein Dropdown-Listenfeld mit Medientypen und dem Beispielwert und -schema.

Wenn die Swagger-Seite nicht angezeigt wird, finden Sie weitere Informationen in diesem GitHub-Issue.

Swagger wird verwendet, um hilfreiche Dokumentation und Hilfeseiten für Web-APIs zu generieren. In diesem Tutorial wird Swagger zum Testen der App verwendet. Weitere Informationen zu Swagger finden Sie in ASP.NET Core Web-API-Dokumentation mit Swagger/OpenAPI.

Kopieren Sie die Anforderungs-URL, und fügen Sie sie im Browser ein: https://localhost:<port>/weatherforecast

Der zurückgegebene JSON-Code sieht in etwa wie das folgende Beispiel aus:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Hinzufügen einer Modellklasse

Ein Modell ist eine Gruppe von Klassen, die die Daten darstellen, die die App verwaltet. Das Modell für diese App ist die Klasse TodoItem.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt. Klicken Sie auf Hinzufügen>Neuer Ordner. Geben Sie dem Ordner den Namen Modelsanmelden.
  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoItem, und wählen Sie Hinzufügen aus.
  • Ersetzen Sie den Vorlagencode durch Folgendes:
namespace TodoApi.Models;

public class TodoItem
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Die Id-Eigenschaft fungiert als eindeutiger Schlüssel in einer relationalen Datenbank.

Modellklassen können überall im Projekt platziert werden, doch gemäß der Konvention wird der Ordner Models verwendet.

Hinzufügen eines Datenbankkontexts

Der Datenbankkontext ist die Hauptklasse, die die Entity Framework-Funktionen für ein Datenmodell koordiniert. Diese Klasse wird durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellt.

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoContext, und klicken Sie auf Hinzufügen.
  • Geben Sie den folgenden Code ein:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models;
    
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }
    
        public DbSet<TodoItem> TodoItems { get; set; } = null!;
    }
    

Registrieren des Datenbankkontexts

In ASP.NET Core müssen Dienste wie der Datenbankkontext mit dem Container Abhängigkeitsinjektion registriert werden. Der Container stellt den Dienst für Controller bereit.

Aktualisieren Sie Program.cs mit dem folgenden hervorgehobenen Code:

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Der vorangehende Code:

  • Fügt using-Richtlinien hinzu.
  • Fügt dem Abhängigkeitscontainer den Datenbankkontext hinzu
  • Gibt an, dass der Datenbankkontext eine In-Memory Database verwendet

Erstellen eines Controllergerüsts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Controller.

  • Wählen Sie Hinzufügen>Neues Gerüstelement aus.

  • Klicken Sie auf API-Controller mit Aktionen unter Verwendung von Entity Framework und dann auf Hinzufügen.

  • Führen Sie im Dialogfeld API-Controller mit Aktionen unter Verwendung von Entity Framework folgende Schritte aus:

    • Wählen Sie in der Modellklasse das Element TodoItem (TodoApi.Models) aus.
    • Wählen Sie in der Datenkontextklasse das Element TodoContext (TodoApi.Models) aus.
    • Wählen Sie Hinzufügen aus.

    Wenn beim Gerüstbauvorgang ein Fehler auftritt, wählen Sie Hinzufügen aus, um ein zweites Mal den Gerüstbau zu versuchen.

Der generierte Code hat folgende Auswirkungen:

  • Markiert die Klasse mit dem [ApiController]-Attribut. Dieses Attribut gibt an, dass der Controller auf Web-API-Anforderungen reagiert. Informationen über bestimmte Verhaltensweisen, die das Attribut aktivieren, finden Sie unter Erstellen von Web-APIs mit ASP.NET Core.
  • Verwendet die Abhängigkeitsinjektion, um den Datenbankkontext (TodoContext) dem Controller hinzuzufügen. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Die ASP.NET Core-Vorlagen für:

  • Controller mit Ansichten enthalten [action] in der Routenvorlage.
  • API-Controller enthalten keine [action] in der Routenvorlage.

Wenn sich das [action]-Token nicht in der Routenvorlage befindet, ist der Aktionsname (Methodenname) nicht im Endpunkt enthalten. Dies bedeutet, dass der zugehörige Methodenname der Aktion nicht in der übereinstimmenden Route verwendet wird.

Aktualisieren der PostTodoItem-Erstellungsmethode

Aktualisieren Sie die Rückgabeanweisung in PostTodoItem mit dem nameof-Operator:

[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //    return CreatedAtAction("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

Der oben stehende Code ist eine HTTP POST-Methode, wie durch das [HttpPost]-Attribut angegeben. Die Methode ruft den Wert von TodoItem aus dem Text der HTTP-Anforderung ab.

Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

Die CreatedAtAction-Methode:

  • Gibt bei Erfolg den HTTP-Statuscode 201 zurück. HTTP 201 ist die Standardantwort für eine HTTP POST-Methode, die eine neue Ressource auf dem Server erstellt.
  • Fügt der Antwort einen Location-Header hinzu. Der Location-Header legt den URI des neu erstellten To-do-Elements fest. Weitere Informationen finden Sie unter 10.2.2 201 Created.
  • Verweist auf die PostTodoItem-Aktion zum Erstellen des URIs des Location-Headers. Das nameof-Schlüsselwort von C# wird verwendet, um eine Hartcodierung des Aktionsnamens im CreatedAtAction-Aufruf zu vermeiden.

Testen von PostTodoItem

  • Drücken Sie STRG+F5, um die App auszuführen.

  • Wählen Sie im Swagger-Browserfenster POST /api/TodoItems und dann Try it out aus.

  • Aktualisieren Sie im Eingabefenster Request body das JSOn. Beispiel:

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Wählen Sie Execute.

    Swagger POST

Testen des Adressheader-URIs

In der vorstehenden POST zeigt die Swagger-Benutzeroberfläche den Adressheader unter Response Headers an. Beispiel: location: https://localhost:7260/api/TodoItems/1. Der Adressheader zeigt den URI für die erstellte Ressource an.

Testen des Adressheaders:

  • Wählen Sie im Swagger-Browserfenster GET /api/TodoItems{id} und dann Try it out aus.

  • Geben Sie 1 in das Eingabefeld id ein, und wählen Sie dann Execute aus.

    Swagger GET

Überblick über die GET-Methoden

Zwei GET-Endpunkte werden implementiert:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Der vorherige Abschnitt veranschaulichte ein Beispiel für die /api/todoitems/{id}-Route.

Folgen Sie den POST-Anweisungen, um ein weiteres Todo-Element hinzuzufügen, und testen Sie dann die /api/todoitems-Route mit Swagger.

Diese App verwendet eine In-Memory-Datenbank. Wenn die App angehalten und dann wieder gestartet wird, werden durch die vorherige GET-Anforderung keine Daten zurückgegeben. Wenn keine Daten zurückgegeben werden, senden Sie mit POST Daten an die App.

Routing und URL-Pfade

Das [HttpGet]-Attribut gibt eine Methode an, die auf eine HTTP GET-Anforderung antwortet. Der URL-Pfad für jede Methode wird wie folgt erstellt:

  • Beginnen Sie mit der Vorlagenzeichenfolge im Route-Attribut des Controllers:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Ersetzen Sie [controller] durch den Namen des Controllers, bei dem es sich standardmäßig um den Namen der Controller-Klasse ohne das Suffix „Controller“ handelt. In diesem Beispiel ist der Klassenname des Controllers „TodoItemsController“. Der Controllername lautet also „TodoItems“. Beim ASP.NET Core-Routing wird die Groß-/Kleinschreibung nicht beachtet.

  • Wenn das [HttpGet]-Attribut eine Routenvorlage (z. B. [HttpGet("products")]) hat, fügen Sie diese an den Pfad an. In diesem Beispiel wird keine Vorlage verwendet. Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

In der folgenden GetTodoItem-Methode ist "{id}" eine Platzhaltervariable für den eindeutigen Bezeichner des To-do-Elements. Wenn GetTodoItem aufgerufen wird, wird der Wert von "{id}" in der URL der Methode in ihrem Parameter id bereitgestellt.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Rückgabewerte

Der Rückgabetyp der Methoden GetTodoItems und GetTodoItem ist ActionResult<T>-Typ . ASP.NET Core serialisiert automatisch das Objekt in JSON und schreibt den JSON-Code in den Text der Antwortnachricht. Der Antwortcode für diesen Rückgabetyp ist 200 OK, vorausgesetzt, es gibt keine Ausnahmefehler. Nicht behandelte Ausnahmen werden in 5xx-Fehler übersetzt.

ActionResult-Rückgabetypen können eine Vielzahl von HTTP-Statuscodes darstellen. Beispielsweise kann GetTodoItem zwei verschiedene Statuswerte zurückgeben:

  • Wenn kein Element mit der angeforderten ID übereinstimmt, gibt die Methode einen NotFound-Fehlercode Status 404 zurück.
  • Andernfalls gibt die Methode 200 mit einem JSON-Antworttext zurück. Die Rückgabe von item löst eine HTTP 200-Antwort aus.

PutTodoItem-Methode

Untersuchen Sie die PutTodoItem-Methode.

[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem ähnelt PostTodoItem, verwendet allerdings HTTP PUT. Die Antwort ist 204 (No Content). Gemäß der HTTP-Spezifikation erfordert eine PUT-Anforderung, dass der Client die gesamte aktualisierte Entität (nicht nur die Änderungen) sendet. Verwenden Sie HTTP PATCH, um Teilupdates zu unterstützen.

Testen der PutTodoItem-Methode

In diesem Beispiel wird eine In-Memory-Datenbank verwendet, die jedes Mal initialisiert werden muss, wenn die App gestartet wird. Es muss ein Element in der Datenbank vorhanden sein, bevor Sie einen PUT-Aufruf durchführen. Rufen Sie vor einem PUT-Aufruf GET auf, um sicherzustellen, dass ein Element in der Datenbank vorhanden ist.

Verwenden Sie in der Swagger-Benutzeroberfläche die PUT-Schaltfläche, um das TodoItem-Element mit der ID = 1 zu aktualisieren und seinen Namen auf "feed fish" festzulegen. Beachten Sie, dass die Antwort HTTP 204 No Content lautet.

DeleteTodoItem-Methode

Untersuchen Sie die DeleteTodoItem-Methode.

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Testen der DeleteTodoItem-Methode

Verwenden Sie die Benutzeroberfläche von Swagger, um das TodoItem-Element mit der ID = 1 zu löschen. Beachten Sie, dass die Antwort HTTP 204 No Content lautet.

Testen mit anderen Tools

Es gibt viele andere Tools, die zum Testen von Web-APIs verwendet werden können, z. B.:

Weitere Informationen finden Sie unter

Vermeiden von Overposting

Derzeit macht die Beispiel-App das gesamte TodoItem-Objekt verfügbar. In den Produktions-Apps sind die Daten, die eingegeben und mithilfe einer Teilmenge des Modells zurückgegeben werden, in der Regel eingeschränkt. Hierfür gibt es mehrere Gründe, wobei die Sicherheit einer der Hauptgründe ist. Die Teilmenge eines Modells wird üblicherweise als Datenübertragungsobjekt (DTO, Data Transfer Object), Eingabemodell oder Anzeigemodell bezeichnet. In diesem Tutorial wird DTO verwendet.

Ein DTO kann für Folgendes verwendet werden:

  • Vermeiden Sie Overposting.
  • Ausblenden von Eigenschaften, die Clients nicht anzeigen sollen
  • Auslassen einiger Eigenschaften, um die Nutzlast zu verringern
  • Vereinfachen von Objektgraphen, die geschachtelte Objekte enthalten Vereinfachte Objektgraphen können für Clients zweckmäßiger sein.

Um den DTO-Ansatz zu veranschaulichen, aktualisieren Sie die TodoItem-Klasse, sodass sie ein geheimes Feld einschließt:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
        public string? Secret { get; set; }
    }
}

Das geheime Feld muss in dieser App ausgeblendet werden, eine administrative App kann es jedoch verfügbar machen.

Vergewissern Sie sich, dass Sie das geheime Feld veröffentlichen und abrufen können.

Erstellen Sie ein DTO-Modell:

namespace TodoApi.Models;

public class TodoItemDTO
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Aktualisieren Sie TodoItemsController, sodass TodoItemDTO verwendet wird:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Controllers;

[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
    private readonly TodoContext _context;

    public TodoItemsController(TodoContext context)
    {
        _context = context;
    }

    // GET: api/TodoItems
    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    // GET: api/TodoItems/5
    // <snippet_GetByID>
    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }
    // </snippet_GetByID>

    // PUT: api/TodoItems/5
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Update>
    [HttpPut("{id}")]
    public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
    {
        if (id != todoDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoDTO.Name;
        todoItem.IsComplete = todoDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }
    // </snippet_Update>

    // POST: api/TodoItems
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Create>
    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoDTO.IsComplete,
            Name = todoDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }
    // </snippet_Create>

    // DELETE: api/TodoItems/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id)
    {
        return _context.TodoItems.Any(e => e.Id == id);
    }

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
       new TodoItemDTO
       {
           Id = todoItem.Id,
           Name = todoItem.Name,
           IsComplete = todoItem.IsComplete
       };
}

Vergewissern Sie sich, dass Sie das geheime Feld weder veröffentlichen noch abrufen können.

Aufrufen der Web-API mit JavaScript

Mehr dazu finden Sie im -Tutorial: Aufrufen einer ASP.NET Core-Web-API mit JavaScript.

Videoreihe zur Web-API

Sehen Sie sich das Video: Einsteigerreihe: Web-APIs an.

Zuverlässige Web-App-Muster

Eine Anleitung zum Erstellen einer modernen, zuverlässigen, leistungsfähigen, testbaren, kosteneffizienten und skalierbaren ASP.NET Core-App finden Sie in den YouTube-Videos und im Artikel zur zuverlässigen Web App für .NET – ganz gleich, ob Sie eine App von Grund auf neu erstellen oder umgestalten möchten.

Hinzufügen der Authentifizierungsunterstützung zu einer Web-API

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende Identity Server ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende Identity Server ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Wichtig

Duende Software erhebt ggf. eine Lizenzgebühr für die Nutzung von Duende Identity Server in der Produktion. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 5.0 zu 6.0.

Weitere Informationen finden Sie in der Dokumentation zu Duende Identity Server (Website von Duende Software).

Veröffentlichen in Azure

Weitere Informationen zur Bereistellung in Azure finden Sie unter Schnellstart: Bereitstellen einer ASP.NET-Web-App.

Zusätzliche Ressourcen

Sie können den Beispielcode für dieses Tutorial anzeigen oder herunterladen. Informationen zum Herunterladen finden Sie hier.

Weitere Informationen finden Sie in den folgenden Ressourcen:

In diesem Tutorial lernen Sie die Grundlagen der Erstellung einer controllerbasierten Web-API, die eine Datenbank verwendet. Ein anderer Ansatz zum Erstellen von APIs in ASP.NET Core besteht in der Erstellung von minimalen APIs. Hilfe bei der Entscheidung zwischen minimalen APIs und controllerbasierten APIs finden Sie in der Übersicht über APIs. Anleitungen zum Erstellen einer minimalen API finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.

Übersicht

In diesem Tutorial wird die folgende API erstellt:

API Beschreibung Anforderungstext Antworttext
GET /api/todoitems Alle To-do-Elemente abrufen Keine Array von To-do-Elementen
GET /api/todoitems/{id} Ein Element nach ID abrufen Keine To-do-Element
POST /api/todoitems Neues Element hinzufügen To-do-Element To-do-Element
PUT /api/todoitems/{id} Vorhandenes Element aktualisieren To-do-Element Keine
DELETE /api/todoitems/{id}     Löschen eines Elements Keine Keine

Das folgende Diagramm zeigt den Entwurf der App.

Der Client wird durch ein Rechteck auf der linken Seite dargestellt. Er sendet eine Anforderung und erhält eine Antwort von der Anwendung, die in einem Rechteck auf der rechten Seite dargestellt ist. Innerhalb des Anwendungsrechtecks stellen drei Rechtecke den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung kommt im Controller der Anwendung an, und Lese-/Schreibvorgänge finden zwischen dem Controller und der Datenzugriffsschicht statt. Das Modell wird serialisiert und in der Antwort an den Client zurückgegeben.

Voraussetzungen

Erstellen eines Webprojekts

  • Klicken Sie im Menü Datei auf Neu>Projekt.
  • Geben Sie Web-API in das Suchfeld ein.
  • Wählen Sie die ASP.NET Core-Web-API-Vorlage aus, und klicken Sie auf Weiter.
  • Geben Sie im Dialogfeld Neues Projekt konfigurieren dem Projekt den Namen TodoApi, und wählen Sie dann Weiter aus.
  • Im Dialogfeld Zusätzliche Informationen:
    • Vergewissern Sie sich, dass es sich beim Framework um .NET 7.0 (oder höher) handelt.
    • Vergewissern Sie sich, dass das Kontrollkästchen Controller verwenden (zur Verwendung minimaler APIs deaktivieren) aktiviert ist.
    • Klicken Sie auf Erstellen.

Hinweis

Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

Testen des Projekts

Die Projektvorlage erstellt eine WeatherForecast-API mit Unterstützung für Swagger.

Drücken Sie STRG+F5, um die Ausführung ohne den Debugger zu starten.

Visual Studio zeigt das folgende Dialogfeld an, wenn ein Projekt noch nicht für die Verwendung von SSL konfiguriert ist:

Dieses Projekt ist für die Verwendung von SSL konfiguriert. Um SSL-Warnungen im Browser zu vermeiden, können Sie dem selbstsignierten Zertifikat vertrauen, das IIS Express generiert hat. Möchten Sie dem SSL-Zertifikat von IIS Express vertrauen?

Wählen Sie Ja aus, wenn Sie dem IIS Express-SLL-Zertifikat vertrauen möchten.

Das folgende Dialogfeld wird angezeigt:

Dialogfeld „Sicherheitswarnung“

Klicken Sie auf Ja, wenn Sie zustimmen möchten, dass das Entwicklungszertifikat vertrauenswürdig ist.

Informationen dazu, wie Sie dem Firefox-Browser vertrauen, finden Sie unter Firefox-Zertifikatfehler SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio startet einen Standardbrowser und navigiert zu https://localhost:<port>/swagger/index.html, wobei es sich bei <port> um eine zufällig ausgewählte Portnummer handelt.

Die Swagger-Seite /swagger/index.html wird angezeigt. Wählen Sie GET>Tryit out>Execute (GET > Testen> Ausführen) aus. Die Seite zeigt Folgendes an:

  • Der Curl-Befehl, zum Testen der WeatherForecast-API.
  • Die URL zum Testen der WeatherForecast-API.
  • Der Antwortcode, der Text und die Header.
  • Ein Dropdown-Listenfeld mit Medientypen und dem Beispielwert und -schema.

Wenn die Swagger-Seite nicht angezeigt wird, finden Sie weitere Informationen in diesem GitHub-Issue.

Swagger wird verwendet, um hilfreiche Dokumentation und Hilfeseiten für Web-APIs zu generieren. Dieses Tutorial konzentriert sich auf die Erstellung einer Web-API. Weitere Informationen zu Swagger finden Sie in ASP.NET Core Web-API-Dokumentation mit Swagger/OpenAPI.

Kopieren Sie die Anforderungs-URL, und fügen Sie sie im Browser ein: https://localhost:<port>/weatherforecast

Der zurückgegebene JSON-Code sieht in etwa wie das folgende Beispiel aus:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Hinzufügen einer Modellklasse

Ein Modell ist eine Gruppe von Klassen, die die Daten darstellen, die die App verwaltet. Das Modell für diese App ist die Klasse TodoItem.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt. Klicken Sie auf Hinzufügen>Neuer Ordner. Geben Sie dem Ordner den Namen Modelsanmelden.
  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoItem, und wählen Sie Hinzufügen aus.
  • Ersetzen Sie den Vorlagencode durch Folgendes:
namespace TodoApi.Models;

public class TodoItem
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Die Id-Eigenschaft fungiert als eindeutiger Schlüssel in einer relationalen Datenbank.

Modellklassen können überall im Projekt platziert werden, doch gemäß der Konvention wird der Ordner Models verwendet.

Hinzufügen eines Datenbankkontexts

Der Datenbankkontext ist die Hauptklasse, die die Entity Framework-Funktionen für ein Datenmodell koordiniert. Diese Klasse wird durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellt.

Hinzufügen von NuGet-Paketen

  • Klicken Sie im Menü Extras auf NuGet-Paket-Manager > NuGet-Pakete für Projektmappe verwalten.
  • Klicken Sie auf die Registerkarte Durchsuchen, und geben Sie dann Microsoft.EntityFrameworkCore.InMemory in das Suchfeld ein.
  • Wählen Sie im linken Bereich die Option Microsoft.EntityFrameworkCore.InMemory aus.
  • Aktivieren Sie das Kontrollkästchen Projekt im rechten Bereich, und klicken Sie dann auf Installieren.

Hinzufügen des TodoContext-Datenbankkontexts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoContext, und klicken Sie auf Hinzufügen.
  • Geben Sie den folgenden Code ein:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models;
    
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }
    
        public DbSet<TodoItem> TodoItems { get; set; } = null!;
    }
    

Registrieren des Datenbankkontexts

In ASP.NET Core müssen Dienste wie der Datenbankkontext mit dem Container Abhängigkeitsinjektion registriert werden. Der Container stellt den Dienst für Controller bereit.

Aktualisieren Sie Program.cs mit dem folgenden hervorgehobenen Code:

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Der vorangehende Code:

  • Fügt using-Richtlinien hinzu.
  • Fügt dem Abhängigkeitscontainer den Datenbankkontext hinzu
  • Gibt an, dass der Datenbankkontext eine In-Memory Database verwendet

Erstellen eines Controllergerüsts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Controller.

  • Wählen Sie Hinzufügen>Neues Gerüstelement aus.

  • Klicken Sie auf API-Controller mit Aktionen unter Verwendung von Entity Framework und dann auf Hinzufügen.

  • Führen Sie im Dialogfeld API-Controller mit Aktionen unter Verwendung von Entity Framework folgende Schritte aus:

    • Wählen Sie in der Modellklasse das Element TodoItem (TodoApi.Models) aus.
    • Wählen Sie in der Datenkontextklasse das Element TodoContext (TodoApi.Models) aus.
    • Wählen Sie Hinzufügen aus.

    Wenn beim Gerüstbauvorgang ein Fehler auftritt, wählen Sie Hinzufügen aus, um ein zweites Mal den Gerüstbau zu versuchen.

Der generierte Code hat folgende Auswirkungen:

  • Markiert die Klasse mit dem [ApiController]-Attribut. Dieses Attribut gibt an, dass der Controller auf Web-API-Anforderungen reagiert. Informationen über bestimmte Verhaltensweisen, die das Attribut aktivieren, finden Sie unter Erstellen von Web-APIs mit ASP.NET Core.
  • Verwendet die Abhängigkeitsinjektion, um den Datenbankkontext (TodoContext) dem Controller hinzuzufügen. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Die ASP.NET Core-Vorlagen für:

  • Controller mit Ansichten enthalten [action] in der Routenvorlage.
  • API-Controller enthalten keine [action] in der Routenvorlage.

Wenn sich das [action]-Token nicht in der Routenvorlage befindet, ist der Aktionsname (Methodenname) nicht im Endpunkt enthalten. Dies bedeutet, dass der zugehörige Methodenname der Aktion nicht in der übereinstimmenden Route verwendet wird.

Aktualisieren der PostTodoItem-Erstellungsmethode

Aktualisieren Sie die Rückgabeanweisung in PostTodoItem mit dem nameof-Operator:

[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //    return CreatedAtAction("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

Der oben stehende Code ist eine HTTP POST-Methode, wie durch das [HttpPost]-Attribut angegeben. Die Methode ruft den Wert von TodoItem aus dem Text der HTTP-Anforderung ab.

Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

Die CreatedAtAction-Methode:

  • Gibt bei Erfolg den HTTP-Statuscode 201 zurück. HTTP 201 ist die Standardantwort für eine HTTP POST-Methode, die eine neue Ressource auf dem Server erstellt.
  • Fügt der Antwort einen Location-Header hinzu. Der Location-Header legt den URI des neu erstellten To-do-Elements fest. Weitere Informationen finden Sie unter 10.2.2 201 Created.
  • Verweist auf die PostTodoItem-Aktion zum Erstellen des URIs des Location-Headers. Das nameof-Schlüsselwort von C# wird verwendet, um eine Hartcodierung des Aktionsnamens im CreatedAtAction-Aufruf zu vermeiden.

Testen von PostTodoItem

  • Drücken Sie STRG+F5, um die App auszuführen.

  • Wählen Sie im Swagger-Browserfenster POST /api/TodoItems und dann Try it out aus.

  • Aktualisieren Sie im Eingabefenster Request body das JSOn. Beispiel:

    {
      "name": "walk dog",
      "isComplete": true
    }
    
  • Wählen Sie Execute.

    Swagger POST

Testen des Adressheader-URIs

In der vorstehenden POST zeigt die Swagger-Benutzeroberfläche den Adressheader unter Response Headers an. Beispiel: location: https://localhost:7260/api/TodoItems/1. Der Adressheader zeigt den URI für die erstellte Ressource an.

Testen des Adressheaders:

  • Wählen Sie im Swagger-Browserfenster GET /api/TodoItems{id} und dann Try it out aus.

  • Geben Sie 1 in das Eingabefeld id ein, und wählen Sie dann Execute aus.

    Swagger GET

Überblick über die GET-Methoden

Zwei GET-Endpunkte werden implementiert:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Der vorherige Abschnitt veranschaulichte ein Beispiel für die /api/todoitems/{id}-Route.

Folgen Sie den POST-Anweisungen, um ein weiteres Todo-Element hinzuzufügen, und testen Sie dann die /api/todoitems-Route mit Swagger.

Diese App verwendet eine In-Memory-Datenbank. Wenn die App angehalten und dann wieder gestartet wird, werden durch die vorherige GET-Anforderung keine Daten zurückgegeben. Wenn keine Daten zurückgegeben werden, senden Sie mit POST Daten an die App.

Routing und URL-Pfade

Das [HttpGet]-Attribut gibt eine Methode an, die auf eine HTTP GET-Anforderung antwortet. Der URL-Pfad für jede Methode wird wie folgt erstellt:

  • Beginnen Sie mit der Vorlagenzeichenfolge im Route-Attribut des Controllers:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Ersetzen Sie [controller] durch den Namen des Controllers, bei dem es sich standardmäßig um den Namen der Controller-Klasse ohne das Suffix „Controller“ handelt. In diesem Beispiel ist der Klassenname des Controllers „TodoItemsController“. Der Controllername lautet also „TodoItems“. Beim ASP.NET Core-Routing wird die Groß-/Kleinschreibung nicht beachtet.

  • Wenn das [HttpGet]-Attribut eine Routenvorlage (z. B. [HttpGet("products")]) hat, fügen Sie diese an den Pfad an. In diesem Beispiel wird keine Vorlage verwendet. Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

In der folgenden GetTodoItem-Methode ist "{id}" eine Platzhaltervariable für den eindeutigen Bezeichner des To-do-Elements. Wenn GetTodoItem aufgerufen wird, wird der Wert von "{id}" in der URL der Methode in ihrem Parameter id bereitgestellt.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Rückgabewerte

Der Rückgabetyp der Methoden GetTodoItems und GetTodoItem ist ActionResult<T>-Typ . ASP.NET Core serialisiert automatisch das Objekt in JSON und schreibt den JSON-Code in den Text der Antwortnachricht. Der Antwortcode für diesen Rückgabetyp ist 200 OK, vorausgesetzt, es gibt keine Ausnahmefehler. Nicht behandelte Ausnahmen werden in 5xx-Fehler übersetzt.

ActionResult-Rückgabetypen können eine Vielzahl von HTTP-Statuscodes darstellen. Beispielsweise kann GetTodoItem zwei verschiedene Statuswerte zurückgeben:

  • Wenn kein Element mit der angeforderten ID übereinstimmt, gibt die Methode einen NotFound-Fehlercode Status 404 zurück.
  • Andernfalls gibt die Methode 200 mit einem JSON-Antworttext zurück. Die Rückgabe von item löst eine HTTP 200-Antwort aus.

PutTodoItem-Methode

Untersuchen Sie die PutTodoItem-Methode.

[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem ähnelt PostTodoItem, verwendet allerdings HTTP PUT. Die Antwort ist 204 (No Content). Gemäß der HTTP-Spezifikation erfordert eine PUT-Anforderung, dass der Client die gesamte aktualisierte Entität (nicht nur die Änderungen) sendet. Verwenden Sie HTTP PATCH, um Teilupdates zu unterstützen.

Testen der PutTodoItem-Methode

In diesem Beispiel wird eine In-Memory-Datenbank verwendet, die jedes Mal initialisiert werden muss, wenn die App gestartet wird. Es muss ein Element in der Datenbank vorhanden sein, bevor Sie einen PUT-Aufruf durchführen. Rufen Sie vor einem PUT-Aufruf GET auf, um sicherzustellen, dass ein Element in der Datenbank vorhanden ist.

Verwenden Sie in der Swagger-Benutzeroberfläche die PUT-Schaltfläche, um das TodoItem-Element mit der ID = 1 zu aktualisieren und seinen Namen auf "feed fish" festzulegen. Beachten Sie, dass die Antwort HTTP 204 No Content lautet.

DeleteTodoItem-Methode

Untersuchen Sie die DeleteTodoItem-Methode.

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Testen der DeleteTodoItem-Methode

Verwenden Sie die Benutzeroberfläche von Swagger, um das TodoItem-Element mit der ID = 1 zu löschen. Beachten Sie, dass die Antwort HTTP 204 No Content lautet.

Testen mit http-repl, Postman oder curl

http-repl, Postman und curl werden häufig zum Testen einer API verwendet. Swagger verwendet curl und zeigt den gesendeten curl-Befehl an.

Anweisungen zu diesen Tools finden Sie unter den folgenden Links:

Weitere Informationen zu http-repl finden Sie unter Testen von Web-APIs mit HttpRepl.

Vermeiden von Overposting

Derzeit macht die Beispiel-App das gesamte TodoItem-Objekt verfügbar. In den Produktions-Apps sind die Daten, die eingegeben und mithilfe einer Teilmenge des Modells zurückgegeben werden, in der Regel eingeschränkt. Hierfür gibt es mehrere Gründe, wobei die Sicherheit einer der Hauptgründe ist. Die Teilmenge eines Modells wird üblicherweise als Datenübertragungsobjekt (DTO, Data Transfer Object), Eingabemodell oder Anzeigemodell bezeichnet. In diesem Tutorial wird DTO verwendet.

Ein DTO kann für Folgendes verwendet werden:

  • Vermeiden Sie Overposting.
  • Ausblenden von Eigenschaften, die Clients nicht anzeigen sollen
  • Auslassen einiger Eigenschaften, um die Nutzlast zu verringern
  • Vereinfachen von Objektgraphen, die geschachtelte Objekte enthalten Vereinfachte Objektgraphen können für Clients zweckmäßiger sein.

Um den DTO-Ansatz zu veranschaulichen, aktualisieren Sie die TodoItem-Klasse, sodass sie ein geheimes Feld einschließt:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
        public string? Secret { get; set; }
    }
}

Das geheime Feld muss in dieser App ausgeblendet werden, eine administrative App kann es jedoch verfügbar machen.

Vergewissern Sie sich, dass Sie das geheime Feld veröffentlichen und abrufen können.

Erstellen Sie ein DTO-Modell:

namespace TodoApi.Models;

public class TodoItemDTO
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Aktualisieren Sie TodoItemsController, sodass TodoItemDTO verwendet wird:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Controllers;

[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
    private readonly TodoContext _context;

    public TodoItemsController(TodoContext context)
    {
        _context = context;
    }

    // GET: api/TodoItems
    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    // GET: api/TodoItems/5
    // <snippet_GetByID>
    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }
    // </snippet_GetByID>

    // PUT: api/TodoItems/5
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Update>
    [HttpPut("{id}")]
    public async Task<IActionResult> PutTodoItem(long id, TodoItemDTO todoDTO)
    {
        if (id != todoDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoDTO.Name;
        todoItem.IsComplete = todoDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }
    // </snippet_Update>

    // POST: api/TodoItems
    // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
    // <snippet_Create>
    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> PostTodoItem(TodoItemDTO todoDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoDTO.IsComplete,
            Name = todoDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }
    // </snippet_Create>

    // DELETE: api/TodoItems/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id)
    {
        return _context.TodoItems.Any(e => e.Id == id);
    }

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
       new TodoItemDTO
       {
           Id = todoItem.Id,
           Name = todoItem.Name,
           IsComplete = todoItem.IsComplete
       };
}

Vergewissern Sie sich, dass Sie das geheime Feld weder veröffentlichen noch abrufen können.

Aufrufen der Web-API mit JavaScript

Mehr dazu finden Sie im -Tutorial: Aufrufen einer ASP.NET Core-Web-API mit JavaScript.

Videoreihe zur Web-API

Sehen Sie sich das Video: Einsteigerreihe: Web-APIs an.

Zuverlässige Web-App-Muster

Eine Anleitung zum Erstellen einer modernen, zuverlässigen, leistungsfähigen, testbaren, kosteneffizienten und skalierbaren ASP.NET Core-App finden Sie in den YouTube-Videos und im Artikel zur zuverlässigen Web App für .NET – ganz gleich, ob Sie eine App von Grund auf neu erstellen oder umgestalten möchten.

Hinzufügen der Authentifizierungsunterstützung zu einer Web-API

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende Identity Server ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende Identity Server ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Wichtig

Duende Software erhebt ggf. eine Lizenzgebühr für die Nutzung von Duende Identity Server in der Produktion. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 5.0 zu 6.0.

Weitere Informationen finden Sie in der Dokumentation zu Duende Identity Server (Website von Duende Software).

Veröffentlichen in Azure

Weitere Informationen zur Bereistellung in Azure finden Sie unter Schnellstart: Bereitstellen einer ASP.NET-Web-App.

Zusätzliche Ressourcen

Sie können den Beispielcode für dieses Tutorial anzeigen oder herunterladen. Informationen zum Herunterladen finden Sie hier.

Weitere Informationen finden Sie in den folgenden Ressourcen:

In diesem Tutorial lernen Sie die Grundlagen der Erstellung einer controllerbasierten Web-API, die eine Datenbank verwendet. Ein anderer Ansatz zum Erstellen von APIs in ASP.NET Core besteht in der Erstellung von minimalen APIs. Hilfe bei der Entscheidung zwischen minimalen APIs und controllerbasierten APIs finden Sie in der Übersicht über APIs. Anleitungen zum Erstellen einer minimalen API finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.

In diesem Tutorial lernen Sie, wie die folgenden Aufgaben ausgeführt werden:

  • Erstellen eines Web-API-Projekts
  • Hinzufügen einer Modellklasse und eines Datenbankkontexts
  • Erstellen eines Controllergerüsts mit CRUD-Methoden
  • Konfigurieren des Routings, der URL-Pfade und der Rückgabewerte
  • Aufrufen der Web-API mit http-repl.

Abschließend steht Ihnen eine Web-API zur Verfügung, die in einer relationalen Datenbank gespeicherte To-do-Elemente verwalten kann.

Übersicht

In diesem Tutorial wird die folgende API erstellt:

API Beschreibung Anforderungstext Antworttext
GET /api/todoitems Alle To-do-Elemente abrufen Keine Array von To-do-Elementen
GET /api/todoitems/{id} Ein Element nach ID abrufen Keine To-do-Element
POST /api/todoitems Neues Element hinzufügen To-do-Element To-do-Element
PUT /api/todoitems/{id} Vorhandenes Element aktualisieren To-do-Element Keine
DELETE /api/todoitems/{id}     Löschen eines Elements Keine Keine

Das folgende Diagramm zeigt den Entwurf der App.

Der Client wird durch ein Rechteck auf der linken Seite dargestellt. Er sendet eine Anforderung und erhält eine Antwort von der Anwendung, die in einem Rechteck auf der rechten Seite dargestellt ist. Innerhalb des Anwendungsrechtecks stellen drei Rechtecke den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung kommt im Controller der Anwendung an, und Lese-/Schreibvorgänge finden zwischen dem Controller und der Datenzugriffsschicht statt. Das Modell wird serialisiert und in der Antwort an den Client zurückgegeben.

Voraussetzungen

Erstellen eines Webprojekts

  • Klicken Sie im Menü Datei auf Neu>Projekt.
  • Geben Sie Web-API in das Suchfeld ein.
  • Wählen Sie die ASP.NET Core-Web-API-Vorlage aus, und klicken Sie auf Weiter.
  • Geben Sie im Dialogfeld Neues Projekt konfigurieren dem Projekt den Namen TodoApi, und wählen Sie dann Weiter aus.
  • Im Dialogfeld Zusätzliche Informationen:
    • Vergewissern Sie sich, dass das Framework auf .NET 6.0 (Langfristiger Support) festgelegt ist.
    • Vergewissern Sie sich, dass das Kontrollkästchen Controller verwenden (zur Verwendung minimaler APIs deaktivieren) aktiviert ist.
    • Klicken Sie auf Erstellen.

Hinweis

Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

Testen des Projekts

Die Projektvorlage erstellt eine WeatherForecast-API mit Unterstützung für Swagger.

Drücken Sie STRG+F5, um die Ausführung ohne den Debugger zu starten.

Visual Studio zeigt das folgende Dialogfeld an, wenn ein Projekt noch nicht für die Verwendung von SSL konfiguriert ist:

Dieses Projekt ist für die Verwendung von SSL konfiguriert. Um SSL-Warnungen im Browser zu vermeiden, können Sie dem selbstsignierten Zertifikat vertrauen, das IIS Express generiert hat. Möchten Sie dem SSL-Zertifikat von IIS Express vertrauen?

Wählen Sie Ja aus, wenn Sie dem IIS Express-SLL-Zertifikat vertrauen möchten.

Das folgende Dialogfeld wird angezeigt:

Dialogfeld „Sicherheitswarnung“

Klicken Sie auf Ja, wenn Sie zustimmen möchten, dass das Entwicklungszertifikat vertrauenswürdig ist.

Informationen dazu, wie Sie dem Firefox-Browser vertrauen, finden Sie unter Firefox-Zertifikatfehler SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio startet einen Standardbrowser und navigiert zu https://localhost:<port>/swagger/index.html, wobei es sich bei <port> um eine zufällig ausgewählte Portnummer handelt.

Die Swagger-Seite /swagger/index.html wird angezeigt. Wählen Sie GET>Tryit out>Execute (GET > Testen> Ausführen) aus. Die Seite zeigt Folgendes an:

  • Der Curl-Befehl, zum Testen der WeatherForecast-API.
  • Die URL zum Testen der WeatherForecast-API.
  • Der Antwortcode, der Text und die Header.
  • Ein Dropdown-Listenfeld mit Medientypen und dem Beispielwert und -schema.

Wenn die Swagger-Seite nicht angezeigt wird, finden Sie weitere Informationen in diesem GitHub-Issue.

Swagger wird verwendet, um hilfreiche Dokumentation und Hilfeseiten für Web-APIs zu generieren. Dieses Tutorial konzentriert sich auf die Erstellung einer Web-API. Weitere Informationen zu Swagger finden Sie in ASP.NET Core Web-API-Dokumentation mit Swagger/OpenAPI.

Kopieren Sie die Anforderungs-URL, und fügen Sie sie im Browser ein: https://localhost:<port>/weatherforecast

Der zurückgegebene JSON-Code sieht in etwa wie das folgende Beispiel aus:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Aktualisieren der launchUrl

Aktualisieren Sie launchUrl in Properties\launchSettings.json aus "swagger" in "api/todoitems":

"launchUrl": "api/todoitems",

Da Swagger entfernt wird, ändert das oben gezeigte Markup die URL, die für die GET-Methode des Controllers gestartet wird, der in den folgenden Abschnitten hinzugefügt wurde.

Hinzufügen einer Modellklasse

Ein Modell ist eine Gruppe von Klassen, die die Daten darstellen, die die App verwaltet. Das Modell für diese App ist eine einzelne TodoItem-Klasse.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt. Klicken Sie auf Hinzufügen>Neuer Ordner. Geben Sie dem Ordner den Namen Modelsanmelden.

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoItem, und wählen Sie Hinzufügen aus.

  • Ersetzen Sie den Vorlagencode durch Folgendes:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Die Id-Eigenschaft fungiert als eindeutiger Schlüssel in einer relationalen Datenbank.

Modellklassen können überall im Projekt platziert werden, doch gemäß der Konvention wird der Ordner Models verwendet.

Hinzufügen eines Datenbankkontexts

Der Datenbankkontext ist die Hauptklasse, die die Entity Framework-Funktionen für ein Datenmodell koordiniert. Diese Klasse wird durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellt.

Hinzufügen von NuGet-Paketen

  • Klicken Sie im Menü Extras auf NuGet-Paket-Manager > NuGet-Pakete für Projektmappe verwalten.
  • Klicken Sie auf die Registerkarte Durchsuchen, und geben Sie dann Microsoft.EntityFrameworkCore.InMemory in das Suchfeld ein.
  • Wählen Sie im linken Bereich die Option Microsoft.EntityFrameworkCore.InMemory aus.
  • Aktivieren Sie das Kontrollkästchen Projekt im rechten Bereich, und klicken Sie dann auf Installieren.

Hinzufügen des TodoContext-Datenbankkontexts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoContext, und klicken Sie auf Hinzufügen.
  • Geben Sie den folgenden Code ein:

    using Microsoft.EntityFrameworkCore;
    using System.Diagnostics.CodeAnalysis;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; } = null!;
        }
    }
    

Registrieren des Datenbankkontexts

In ASP.NET Core müssen Dienste wie der Datenbankkontext mit dem Container Abhängigkeitsinjektion registriert werden. Der Container stellt den Dienst für Controller bereit.

Aktualisieren Sie Program.cs mit folgendem Code:

using Microsoft.EntityFrameworkCore;
using TodoApi.Models;


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
//builder.Services.AddSwaggerGen(c =>
//{
//    c.SwaggerDoc("v1", new() { Title = "TodoApi", Version = "v1" });
//});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    //app.UseSwagger();
    //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Der vorangehende Code:

  • Entfernt die Swagger-Aufrufe.
  • Entfernt nicht verwendete using-Anweisungen
  • Fügt dem Abhängigkeitscontainer den Datenbankkontext hinzu
  • Gibt an, dass der Datenbankkontext eine In-Memory Database verwendet

Erstellen eines Controllergerüsts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Controller.

  • Wählen Sie Hinzufügen>Neues Gerüstelement aus.

  • Klicken Sie auf API-Controller mit Aktionen unter Verwendung von Entity Framework und dann auf Hinzufügen.

  • Führen Sie im Dialogfeld API-Controller mit Aktionen unter Verwendung von Entity Framework folgende Schritte aus:

    • Wählen Sie in der Modellklasse das Element TodoItem (TodoApi.Models) aus.
    • Wählen Sie in der Datenkontextklasse das Element TodoContext (TodoApi.Models) aus.
    • Wählen Sie Hinzufügen aus.

    Wenn beim Gerüstbauvorgang ein Fehler auftritt, wählen Sie Hinzufügen aus, um ein zweites Mal den Gerüstbau zu versuchen.

Der generierte Code hat folgende Auswirkungen:

  • Markiert die Klasse mit dem [ApiController]-Attribut. Dieses Attribut gibt an, dass der Controller auf Web-API-Anforderungen reagiert. Informationen über bestimmte Verhaltensweisen, die das Attribut aktivieren, finden Sie unter Erstellen von Web-APIs mit ASP.NET Core.
  • Verwendet die Abhängigkeitsinjektion, um den Datenbankkontext (TodoContext) dem Controller hinzuzufügen. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Die ASP.NET Core-Vorlagen für:

  • Controller mit Ansichten enthalten [action] in der Routenvorlage.
  • API-Controller enthalten keine [action] in der Routenvorlage.

Wenn sich das [action]-Token nicht in der Routenvorlage befindet, wird der action-Name von der Route ausgeschlossen. Dies bedeutet, dass der zugehörige Methodenname der Aktion nicht in der übereinstimmenden Route verwendet wird.

Aktualisieren der PostTodoItem-Erstellungsmethode

Aktualisieren Sie die Rückgabeanweisung in PostTodoItem mit dem nameof-Operator:

[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

Der oben stehende Code ist eine HTTP POST-Methode, wie durch das [HttpPost]-Attribut angegeben. Die Methode ruft den Wert der Aufgabe aus dem Text der HTTP-Anforderung ab.

Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

Die CreatedAtAction-Methode:

  • Gibt bei Erfolg den HTTP-Statuscode 201 zurück. HTTP 201 ist die Standardantwort für eine HTTP POST-Methode, die eine neue Ressource auf dem Server erstellt.
  • Fügt der Antwort einen Location-Header hinzu. Der Location-Header legt den URI des neu erstellten To-do-Elements fest. Weitere Informationen finden Sie unter 10.2.2 201 Created.
  • Verweist auf die GetTodoItem-Aktion zum Erstellen des URIs des Location-Headers. Das nameof-Schlüsselwort von C# wird verwendet, um eine Hartcodierung des Aktionsnamens im CreatedAtAction-Aufruf zu vermeiden.

Installieren von http-repl

Dieses Tutorial verwendet http-repl zum Testen der Web-API.

  • Führen Sie an der Eingabeaufforderung den folgenden Befehl aus:

    dotnet tool install -g Microsoft.dotnet-httprepl
    

    Hinweis

    Standardmäßig stellt die Architektur der zu installierenden .NET-Binärdateien die derzeit ausgeführte Betriebssystemarchitektur dar. Informationen zum Angeben einer anderen Betriebssystemarchitektur finden Sie unter dotnet tool install, --arch option. Weitere Informationen finden Sie unter dem GitHub Issue dotnet/docs #29262.

  • Wenn Sie das .NET 6.0 SDK oder die Runtime nicht installiert haben, installieren Sie die .NET 6.0-Runtime.

Testen von PostTodoItem

  • Drücken Sie STRG+F5, um die App auszuführen.

  • Öffnen Sie ein neues Terminalfenster, und führen Sie die folgenden Befehle aus. Wenn Ihre App eine andere Portnummer verwendet, ersetzen Sie 5001 im httprepl-Befehl durch Ihre Portnummer.

    httprepl https://localhost:5001/api/todoitems
    post -h Content-Type=application/json -c "{"name":"walk dog","isComplete":true}"
    

    Das folgende Beispiel zeigt die Ausgabe des Befehls:

    HTTP/1.1 201 Created
    Content-Type: application/json; charset=utf-8
    Date: Tue, 07 Sep 2021 20:39:47 GMT
    Location: https://localhost:5001/api/TodoItems/1
    Server: Kestrel
    Transfer-Encoding: chunked
    
    {
      "id": 1,
      "name": "walk dog",
      "isComplete": true
    }
    

Testen des Adressheader-URIs

Kopieren Sie den Speicherortheader, und fügen Sie ihn in einen httprepl-get-Befehl ein, um ihn zu testen.

Im folgenden Beispiel wird davon ausgegangen, dass Sie sich noch in einer httprepl-Sitzung befinden. Wenn Sie die vorherige httprepl-Sitzung beendet haben, ersetzen Sie connect in den folgenden Befehlen durch httprepl:

connect https://localhost:5001/api/todoitems/1
get

Das folgende Beispiel zeigt die Ausgabe des Befehls:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 07 Sep 2021 20:48:10 GMT
Server: Kestrel
Transfer-Encoding: chunked

{
  "id": 1,
  "name": "walk dog",
  "isComplete": true
}

Überblick über die GET-Methoden

Zwei GET-Endpunkte werden implementiert:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Sie haben gerade ein Beispiel für die /api/todoitems/{id}-Route gesehen. Testen Sie die /api/todoitems-Route:

connect https://localhost:5001/api/todoitems
get

Das folgende Beispiel zeigt die Ausgabe des Befehls:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 07 Sep 2021 20:59:21 GMT
Server: Kestrel
Transfer-Encoding: chunked

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": true
  }
]

Dieses Mal ist der zurückgegebene JSON-Code ein Array von einem Element.

Diese App verwendet eine In-Memory-Datenbank. Wenn die App angehalten und dann wieder gestartet wird, werden durch die vorherige GET-Anforderung keine Daten zurückgegeben. Wenn keine Daten zurückgegeben werden, senden Sie mit POST Daten an die App.

Routing und URL-Pfade

Das Attribut [HttpGet] gibt eine Methode an, die auf eine HTTP GET-Anforderung antwortet. Der URL-Pfad für jede Methode wird wie folgt erstellt:

  • Beginnen Sie mit der Vorlagenzeichenfolge im Route-Attribut des Controllers:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    
  • Ersetzen Sie [controller] durch den Namen des Controllers, bei dem es sich standardmäßig um den Namen der Controller-Klasse ohne das Suffix „Controller“ handelt. In diesem Beispiel ist der Klassenname des Controllers „TodoItemsController“. Der Controllername lautet also „TodoItems“. Beim ASP.NET Core-Routing wird die Groß-/Kleinschreibung nicht beachtet.

  • Wenn das [HttpGet]-Attribut eine Routenvorlage (z. B. [HttpGet("products")]) hat, fügen Sie diese an den Pfad an. In diesem Beispiel wird keine Vorlage verwendet. Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

In der folgenden GetTodoItem-Methode ist "{id}" eine Platzhaltervariable für den eindeutigen Bezeichner des To-do-Elements. Wenn GetTodoItem aufgerufen wird, wird der Wert von "{id}" in der URL der Methode in ihrem Parameter id bereitgestellt.

[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Rückgabewerte

Der Rückgabetyp der Methoden GetTodoItems und GetTodoItem ist ActionResult<T>-Typ . ASP.NET Core serialisiert automatisch das Objekt in JSON und schreibt den JSON-Code in den Text der Antwortnachricht. Der Antwortcode für diesen Rückgabetyp ist 200 OK, vorausgesetzt, es gibt keine Ausnahmefehler. Nicht behandelte Ausnahmen werden in 5xx-Fehler übersetzt.

ActionResult-Rückgabetypen können eine Vielzahl von HTTP-Statuscodes darstellen. Beispielsweise kann GetTodoItem zwei verschiedene Statuswerte zurückgeben:

  • Wenn kein Element mit der angeforderten ID übereinstimmt, gibt die Methode einen NotFound-Fehlercode Status 404 zurück.
  • Andernfalls gibt die Methode 200 mit einem JSON-Antworttext zurück. Die Rückgabe von item löst eine HTTP 200-Antwort aus.

PutTodoItem-Methode

Untersuchen Sie die PutTodoItem-Methode.

[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem ähnelt PostTodoItem, verwendet allerdings HTTP PUT. Die Antwort ist 204 (No Content). Gemäß der HTTP-Spezifikation erfordert eine PUT-Anforderung, dass der Client die gesamte aktualisierte Entität (nicht nur die Änderungen) sendet. Verwenden Sie HTTP PATCH, um Teilupdates zu unterstützen.

Wenn beim Aufrufen von PutTodoItem im folgenden Abschnitt ein Fehler zurückgegeben wird, rufen Sie GET auf, um sicherzustellen, dass die Datenbank ein Element enthält.

Testen der PutTodoItem-Methode

In diesem Beispiel wird eine In-Memory-Datenbank verwendet, die jedes Mal initialisiert werden muss, wenn die App gestartet wird. Es muss ein Element in der Datenbank vorhanden sein, bevor Sie einen PUT-Aufruf durchführen. Rufen Sie vor einem PUT-Aufruf GET auf, um sicherzustellen, dass ein Element in der Datenbank vorhanden ist.

Aktualisieren Sie das To-do-Element, das über den ID-Wert 1 verfügt, und legen Sie als Namen "feed fish" fest:

connect https://localhost:5001/api/todoitems/1
put -h Content-Type=application/json -c "{"id":1,"name":"feed fish","isComplete":true}"

Das folgende Beispiel zeigt die Ausgabe des Befehls:

HTTP/1.1 204 No Content
Date: Tue, 07 Sep 2021 21:20:47 GMT
Server: Kestrel

DeleteTodoItem-Methode

Untersuchen Sie die DeleteTodoItem-Methode.

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Testen der DeleteTodoItem-Methode

Löschen Sie ds To-Do-Element, das die ID „1“ besitzt:

connect https://localhost:5001/api/todoitems/1
delete

Das folgende Beispiel zeigt die Ausgabe des Befehls:

HTTP/1.1 204 No Content
Date: Tue, 07 Sep 2021 21:43:00 GMT
Server: Kestrel

Vermeiden von Overposting

Derzeit macht die Beispiel-App das gesamte TodoItem-Objekt verfügbar. In den Produktions-Apps sind die Daten, die eingegeben und mithilfe einer Teilmenge des Modells zurückgegeben werden, in der Regel eingeschränkt. Hierfür gibt es mehrere Gründe, wobei die Sicherheit einer der Hauptgründe ist. Die Teilmenge eines Modells wird üblicherweise als Datenübertragungsobjekt (DTO, Data Transfer Object), Eingabemodell oder Anzeigemodell bezeichnet. In diesem Tutorial wird DTO verwendet.

Ein DTO kann für Folgendes verwendet werden:

  • Vermeiden Sie Overposting.
  • Ausblenden von Eigenschaften, die Clients nicht anzeigen sollen
  • Auslassen einiger Eigenschaften, um die Nutzlast zu verringern
  • Vereinfachen von Objektgraphen, die geschachtelte Objekte enthalten Vereinfachte Objektgraphen können für Clients zweckmäßiger sein.

Um den DTO-Ansatz zu veranschaulichen, aktualisieren Sie die TodoItem-Klasse, sodass sie ein geheimes Feld einschließt:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
        public string? Secret { get; set; }
    }
}

Das geheime Feld muss in dieser App ausgeblendet werden, eine administrative App kann es jedoch verfügbar machen.

Vergewissern Sie sich, dass Sie das geheime Feld veröffentlichen und abrufen können.

Erstellen Sie ein DTO-Modell:

namespace TodoApi.Models
{
    public class TodoItemDTO
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Aktualisieren Sie TodoItemsController, sodass TodoItemDTO verwendet wird:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;

        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }

        // GET: api/TodoItems
        [HttpGet]
        public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
        {
            return await _context.TodoItems
                .Select(x => ItemToDTO(x))
                .ToListAsync();
        }

        // GET: api/TodoItems/5
        [HttpGet("{id}")]
        public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);

            if (todoItem == null)
            {
                return NotFound();
            }

            return ItemToDTO(todoItem);
        }
        // PUT: api/TodoItems/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
        {
            if (id != todoItemDTO.Id)
            {
                return BadRequest();
            }

            var todoItem = await _context.TodoItems.FindAsync(id);
            if (todoItem == null)
            {
                return NotFound();
            }

            todoItem.Name = todoItemDTO.Name;
            todoItem.IsComplete = todoItemDTO.IsComplete;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
            {
                return NotFound();
            }

            return NoContent();
        }
        // POST: api/TodoItems
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
        {
            var todoItem = new TodoItem
            {
                IsComplete = todoItemDTO.IsComplete,
                Name = todoItemDTO.Name
            };

            _context.TodoItems.Add(todoItem);
            await _context.SaveChangesAsync();

            return CreatedAtAction(
                nameof(GetTodoItem),
                new { id = todoItem.Id },
                ItemToDTO(todoItem));
        }

        // DELETE: api/TodoItems/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteTodoItem(long id)
        {
            var todoItem = await _context.TodoItems.FindAsync(id);

            if (todoItem == null)
            {
                return NotFound();
            }

            _context.TodoItems.Remove(todoItem);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool TodoItemExists(long id)
        {
            return _context.TodoItems.Any(e => e.Id == id);
        }

        private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
            new TodoItemDTO
            {
                Id = todoItem.Id,
                Name = todoItem.Name,
                IsComplete = todoItem.IsComplete
            };
    }
}

Vergewissern Sie sich, dass Sie das geheime Feld weder veröffentlichen noch abrufen können.

Aufrufen der Web-API mit JavaScript

Mehr dazu finden Sie im -Tutorial: Aufrufen einer ASP.NET Core-Web-API mit JavaScript.

Videoreihe zur Web-API

Sehen Sie sich das Video: Einsteigerreihe: Web-APIs an.

Hinzufügen der Authentifizierungsunterstützung zu einer Web-API

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende Identity Server ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende Identity Server ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Wichtig

Duende Software erhebt ggf. eine Lizenzgebühr für die Nutzung von Duende Identity Server in der Produktion. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 5.0 zu 6.0.

Weitere Informationen finden Sie in der Dokumentation zu Duende Identity Server (Website von Duende Software).

Veröffentlichen in Azure

Weitere Informationen zur Bereistellung in Azure finden Sie unter Schnellstart: Bereitstellen einer ASP.NET-Web-App.

Zusätzliche Ressourcen

Sie können den Beispielcode für dieses Tutorial anzeigen oder herunterladen. Informationen zum Herunterladen finden Sie hier.

Weitere Informationen finden Sie in den folgenden Ressourcen:

In diesem Tutorial lernen Sie die Grundlagen der Erstellung einer controllerbasierten Web-API, die eine Datenbank verwendet. Ein anderer Ansatz zum Erstellen von APIs in ASP.NET Core besteht in der Erstellung von minimalen APIs. Hilfe bei der Entscheidung zwischen minimalen APIs und controllerbasierten APIs finden Sie in der Übersicht über APIs. Anleitungen zum Erstellen einer minimalen API finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.

In diesem Tutorial lernen Sie, wie die folgenden Aufgaben ausgeführt werden:

  • Erstellen eines Web-API-Projekts
  • Hinzufügen einer Modellklasse und eines Datenbankkontexts
  • Erstellen eines Controllergerüsts mit CRUD-Methoden
  • Konfigurieren des Routings, der URL-Pfade und der Rückgabewerte
  • Aufrufen der Web-API mit Postman

Abschließend steht Ihnen eine Web-API zur Verfügung, die in einer relationalen Datenbank gespeicherte To-do-Elemente verwalten kann.

Übersicht

In diesem Tutorial wird die folgende API erstellt:

API Beschreibung Anforderungstext Antworttext
GET /api/todoitems Alle To-do-Elemente abrufen Keine Array von To-do-Elementen
GET /api/todoitems/{id} Ein Element nach ID abrufen Keine To-do-Element
POST /api/todoitems Neues Element hinzufügen To-do-Element To-do-Element
PUT /api/todoitems/{id} Vorhandenes Element aktualisieren To-do-Element Keine
DELETE /api/todoitems/{id}     Löschen eines Elements Keine Keine

Das folgende Diagramm zeigt den Entwurf der App.

Der Client wird durch ein Rechteck auf der linken Seite dargestellt. Er sendet eine Anforderung und erhält eine Antwort von der Anwendung, die in einem Rechteck auf der rechten Seite dargestellt ist. Innerhalb des Anwendungsrechtecks stellen drei Rechtecke den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung kommt im Controller der Anwendung an, und Lese-/Schreibvorgänge finden zwischen dem Controller und der Datenzugriffsschicht statt. Das Modell wird serialisiert und in der Antwort an den Client zurückgegeben.

Voraussetzungen

Erstellen eines Webprojekts

  • Klicken Sie im Menü Datei auf Neu>Projekt.
  • Wählen Sie die ASP.NET Core-Web-API-Vorlage aus, und klicken Sie auf Weiter.
  • Geben Sie dem Projekt den Namen TodoApi, und klicken Sie auf Erstellen.
  • Vergewissern Sie sich, dass im Dialogfeld Neue ASP.NET Core-Webanwendung erstellen die Optionen .NET Core und ASP.NET Core 5.0 ausgewählt sind. Wählen Sie die Vorlage API aus, und klicken Sie auf Erstellen.

VS-Dialogfeld „Neues Projekt“

Hinweis

Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

Testen des Projekts

Die Projektvorlage erstellt eine WeatherForecast-API mit Unterstützung für Swagger.

Drücken Sie STRG+F5, um die Ausführung ohne den Debugger zu starten.

Visual Studio zeigt das folgende Dialogfeld an, wenn ein Projekt noch nicht für die Verwendung von SSL konfiguriert ist:

Dieses Projekt ist für die Verwendung von SSL konfiguriert. Um SSL-Warnungen im Browser zu vermeiden, können Sie dem selbstsignierten Zertifikat vertrauen, das IIS Express generiert hat. Möchten Sie dem SSL-Zertifikat von IIS Express vertrauen?

Wählen Sie Ja aus, wenn Sie dem IIS Express-SLL-Zertifikat vertrauen möchten.

Das folgende Dialogfeld wird angezeigt:

Dialogfeld „Sicherheitswarnung“

Klicken Sie auf Ja, wenn Sie zustimmen möchten, dass das Entwicklungszertifikat vertrauenswürdig ist.

Informationen dazu, wie Sie dem Firefox-Browser vertrauen, finden Sie unter Firefox-Zertifikatfehler SEC_ERROR_INADEQUATE_KEY_USAGE.

Visual Studio startet Folgendes:

  • Den IIS Express-Webserver.
  • Den Standardbrowser. Visual Studio navigiert zu https://localhost:<port>/swagger/index.html, wobei es sich bei <port> um eine zufällig ausgewählte Portnummer handelt.

Die Swagger-Seite /swagger/index.html wird angezeigt. Wählen Sie GET>Tryit out>Execute (GET > Testen> Ausführen) aus. Die Seite zeigt Folgendes an:

  • Der Curl-Befehl, zum Testen der WeatherForecast-API.
  • Die URL zum Testen der WeatherForecast-API.
  • Der Antwortcode, der Text und die Header.
  • Ein Dropdown-Listenfeld mit Medientypen und dem Beispielwert und -schema.

Wenn die Swagger-Seite nicht angezeigt wird, finden Sie weitere Informationen in diesem GitHub-Issue.

Swagger wird verwendet, um hilfreiche Dokumentation und Hilfeseiten für Web-APIs zu generieren. Dieses Tutorial konzentriert sich auf die Erstellung einer Web-API. Weitere Informationen zu Swagger finden Sie in ASP.NET Core Web-API-Dokumentation mit Swagger/OpenAPI.

Kopieren Sie die Anforderungs-URL, und fügen Sie sie im Browser ein: https://localhost:<port>/weatherforecast

Der zurückgegebene JSON-Code sieht in etwa wie folgt aus:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Aktualisieren der launchUrl

Aktualisieren Sie launchUrl in Properties\launchSettings.json aus "swagger" in "api/todoitems":

"launchUrl": "api/todoitems",

Da Swagger entfernt wird, ändert das oben gezeigte Markup die URL, die für die GET-Methode des Controllers gestartet wird, der in den folgenden Abschnitten hinzugefügt wurde.

Hinzufügen einer Modellklasse

Ein Modell ist eine Gruppe von Klassen, die die Daten darstellen, die die App verwaltet. Das Modell für diese App ist eine einzelne TodoItem-Klasse.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt. Klicken Sie auf Hinzufügen>Neuer Ordner. Geben Sie dem Ordner den Namen Modelsanmelden.

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoItem, und wählen Sie Hinzufügen aus.

  • Ersetzen Sie den Vorlagencode durch Folgendes:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

Die Id-Eigenschaft fungiert als eindeutiger Schlüssel in einer relationalen Datenbank.

Modellklassen können überall im Projekt platziert werden, doch gemäß der Konvention wird der Ordner Models verwendet.

Hinzufügen eines Datenbankkontexts

Der Datenbankkontext ist die Hauptklasse, die die Entity Framework-Funktionen für ein Datenmodell koordiniert. Diese Klasse wird durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellt.

Hinzufügen von NuGet-Paketen

  • Klicken Sie im Menü Extras auf NuGet-Paket-Manager > NuGet-Pakete für Projektmappe verwalten.
  • Klicken Sie auf die Registerkarte Durchsuchen, und geben Sie dann Microsoft.EntityFrameworkCore.InMemory in das Suchfeld ein.
  • Wählen Sie im linken Bereich die Option Microsoft.EntityFrameworkCore.InMemory aus.
  • Aktivieren Sie das Kontrollkästchen Projekt im rechten Bereich, und klicken Sie dann auf Installieren.

NuGet-Paket-Manager

Hinzufügen des TodoContext-Datenbankkontexts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoContext, und klicken Sie auf Hinzufügen.
  • Geben Sie den folgenden Code ein:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

Registrieren des Datenbankkontexts

In ASP.NET Core müssen Dienste wie der Datenbankkontext mit dem Container Abhängigkeitsinjektion registriert werden. Der Container stellt den Dienst für Controller bereit.

Aktualisieren Sie Startup.cs mit folgendem Code:

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddDbContext<TodoContext>(opt =>
                                               opt.UseInMemoryDatabase("TodoList"));
            //services.AddSwaggerGen(c =>
            //{
            //    c.SwaggerDoc("v1", new OpenApiInfo { Title = "TodoApi", Version = "v1" });
            //});
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                //app.UseSwagger();
                //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
            }

            app.UseHttpsRedirection();
            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Der vorangehende Code:

  • Entfernt die Swagger-Aufrufe.
  • Entfernt nicht benötigte using-Deklarationen
  • Fügt dem Abhängigkeitscontainer den Datenbankkontext hinzu
  • Gibt an, dass der Datenbankkontext eine In-Memory Database verwendet

Erstellen eines Controllergerüsts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Controller.

  • Wählen Sie Hinzufügen>Neues Gerüstelement aus.

  • Klicken Sie auf API-Controller mit Aktionen unter Verwendung von Entity Framework und dann auf Hinzufügen.

  • Führen Sie im Dialogfeld API-Controller mit Aktionen unter Verwendung von Entity Framework folgende Schritte aus:

    • Wählen Sie in der Modellklasse das Element TodoItem (TodoApi.Models) aus.
    • Wählen Sie in der Datenkontextklasse das Element TodoContext (TodoApi.Models) aus.
    • Wählen Sie Hinzufügen aus.

Der generierte Code hat folgende Auswirkungen:

  • Markiert die Klasse mit dem [ApiController]-Attribut. Dieses Attribut gibt an, dass der Controller auf Web-API-Anforderungen reagiert. Informationen über bestimmte Verhaltensweisen, die das Attribut aktivieren, finden Sie unter Erstellen von Web-APIs mit ASP.NET Core.
  • Verwendet die Abhängigkeitsinjektion, um den Datenbankkontext (TodoContext) dem Controller hinzuzufügen. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Die ASP.NET Core-Vorlagen für:

  • Controller mit Ansichten enthalten [action] in der Routenvorlage.
  • API-Controller enthalten keine [action] in der Routenvorlage.

Wenn sich das [action]-Token nicht in der Routenvorlage befindet, wird der action-Name von der Route ausgeschlossen. Dies bedeutet, dass der zugehörige Methodenname der Aktion nicht in der übereinstimmenden Route verwendet wird.

Aktualisieren der PostTodoItem-Erstellungsmethode

Aktualisieren Sie die Rückgabeanweisung in PostTodoItem mit dem nameof-Operator:

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

Der oben stehende Code ist eine HTTP POST-Methode, wie durch das [HttpPost]-Attribut angegeben. Die Methode ruft den Wert der Aufgabe aus dem Text der HTTP-Anforderung ab.

Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

Die CreatedAtAction-Methode:

  • Gibt bei Erfolg den HTTP-Statuscode 201 zurück. HTTP 201 ist die Standardantwort für eine HTTP POST-Methode, die eine neue Ressource auf dem Server erstellt.
  • Fügt der Antwort einen Location-Header hinzu. Der Location-Header legt den URI des neu erstellten To-do-Elements fest. Weitere Informationen finden Sie unter 201 Created.
  • Verweist auf die GetTodoItem-Aktion zum Erstellen des URIs des Location-Headers. Das nameof-Schlüsselwort von C# wird verwendet, um eine Hartcodierung des Aktionsnamens im CreatedAtAction-Aufruf zu vermeiden.

Installieren von Postman

Dieses Tutorial verwendet Postman zum Testen der Web-API.

  • Installieren Sie Postman.
  • Starten Sie die Web-App.
  • Starten Sie Postman.
  • Deaktivierund von SSL certificate verification (Verifizierung des SSL-Zertifikats):
    • Postman für Windows: Datei>Einstellungen auswählen, (Registerkarte Allgemein), deaktivieren Sie die SSL-Zertifikatüberprüfung.
    • Postman für macOS: Postman>Einstellungen auswählen, (Registerkarte Allgemein), deaktivieren Sie die SSL-Zertifikatüberprüfung.

      Warnung

      Aktivieren Sie die Verifizierung des SSL-Zertifikats wieder, nachdem Sie den Controller getestet haben.

Testen von PostTodoItem mit Postman

  • Erstellen Sie eine neue Anforderung.

  • Legen Sie als HTTP-Methode POST fest.

  • Legen Sie den URI auf https://localhost:<port>/api/todoitems fest. Beispiel: https://localhost:5001/api/todoitems.

  • Wählen Sie die Registerkarte Body (Text) aus.

  • Wählen Sie das Optionsfeld raw (Unformatiert) aus.

  • Legen Sie den Typ auf JSON (application/json) fest.

  • Geben Sie für die Aufgabe den Anforderungstext im JSON-Format ein:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Wählen Sie Send (Senden) aus.

    Postman mit erstellter Anforderung

Testen des Adressheader-URIs

Der Location-Header-URI kann im Browser getestet werden. Kopieren Sie den Location-Header-URI, und fügen Sie ihn im Browser ein.

So testen Sie in Postman:

  • Wählen Sie auf der Registerkarte Header (Header) den Bereich Response (Antwort) aus.

  • Kopieren Sie den den Headerwert von Location (Speicherort):

    Registerkarte „Header“ in der Postman-Konsole

  • Legen Sie als HTTP-Methode GET fest.

  • Legen Sie den URI auf https://localhost:<port>/api/todoitems/1 fest. Beispiel: https://localhost:5001/api/todoitems/1.

  • Wählen Sie Send (Senden) aus.

Überblick über die GET-Methoden

Zwei GET-Endpunkte werden implementiert:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Testen Sie die App, indem Sie die beiden Endpunkte in einem Browser oder über Postman aufrufen. Zum Beispiel:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

Durch einen Aufruf von GetTodoItems wird eine Antwort gesendet, die der folgenden ähnelt:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Testen von GET mit Postman

  • Erstellen Sie eine neue Anforderung.
  • Legen Sie die HTTP-Methode auf GET fest.
  • Legen Sie den Anforderungs-URI auf https://localhost:<port>/api/todoitems fest. Beispielsweise https://localhost:5001/api/todoitems.
  • Wählen Sie in Postman Two pane view (Ansicht mit zwei Bereichen) aus.
  • Wählen Sie Send (Senden) aus.

Diese App verwendet eine In-Memory-Datenbank. Wenn die App angehalten und dann wieder gestartet wird, werden durch die vorherige GET-Anforderung keine Daten zurückgegeben. Wenn keine Daten zurückgegeben werden, senden Sie mit POST Daten an die App.

Routing und URL-Pfade

Das Attribut [HttpGet] gibt eine Methode an, die auf eine HTTP GET-Anforderung antwortet. Der URL-Pfad für jede Methode wird wie folgt erstellt:

  • Beginnen Sie mit der Vorlagenzeichenfolge im Route-Attribut des Controllers:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • Ersetzen Sie [controller] durch den Namen des Controllers, bei dem es sich standardmäßig um den Namen der Controller-Klasse ohne das Suffix „Controller“ handelt. In diesem Beispiel ist der Klassenname des Controllers „TodoItemsController“. Der Controllername lautet also „TodoItems“. Beim ASP.NET Core-Routing wird die Groß-/Kleinschreibung nicht beachtet.

  • Wenn das [HttpGet]-Attribut eine Routenvorlage (z. B. [HttpGet("products")]) hat, fügen Sie diese an den Pfad an. In diesem Beispiel wird keine Vorlage verwendet. Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

In der folgenden GetTodoItem-Methode ist "{id}" eine Platzhaltervariable für den eindeutigen Bezeichner des To-do-Elements. Wenn GetTodoItem aufgerufen wird, wird der Wert von "{id}" in der URL der Methode in ihrem Parameter id bereitgestellt.

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Rückgabewerte

Der Rückgabetyp der Methoden GetTodoItems und GetTodoItem ist ActionResult<T>-Typ . ASP.NET Core serialisiert automatisch das Objekt in JSON und schreibt den JSON-Code in den Text der Antwortnachricht. Der Antwortcode für diesen Rückgabetyp ist 200 OK, vorausgesetzt, es gibt keine Ausnahmefehler. Nicht behandelte Ausnahmen werden in 5xx-Fehler übersetzt.

ActionResult-Rückgabetypen können eine Vielzahl von HTTP-Statuscodes darstellen. Beispielsweise kann GetTodoItem zwei verschiedene Statuswerte zurückgeben:

  • Wenn kein Element mit der angeforderten ID übereinstimmt, gibt die Methode einen NotFound-Fehlercode Status 404 zurück.
  • Andernfalls gibt die Methode 200 mit einem JSON-Antworttext zurück. Die Rückgabe von item löst eine HTTP 200-Antwort aus.

PutTodoItem-Methode

Untersuchen Sie die PutTodoItem-Methode.

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem ähnelt PostTodoItem, verwendet allerdings HTTP PUT. Die Antwort ist 204 (No Content). Gemäß der HTTP-Spezifikation erfordert eine PUT-Anforderung, dass der Client die gesamte aktualisierte Entität (nicht nur die Änderungen) sendet. Verwenden Sie HTTP PATCH, um Teilupdates zu unterstützen.

Wenn beim Aufrufen von PutTodoItem ein Fehler zurückgegeben wird, rufen Sie GET auf, um sicherzustellen, dass die Datenbank ein Element enthält.

Testen der PutTodoItem-Methode

In diesem Beispiel wird eine In-Memory-Datenbank verwendet, die jedes Mal initialisiert werden muss, wenn die App gestartet wird. Es muss ein Element in der Datenbank vorhanden sein, bevor Sie einen PUT-Aufruf durchführen. Rufen Sie vor einem PUT-Aufruf GET auf, um sicherzustellen, dass ein Element in der Datenbank vorhanden ist.

Aktualisieren Sie das To-do-Element, das über den ID-Wert 1 verfügt, und legen Sie als Namen "feed fish" fest:

  {
    "Id":1,
    "name":"feed fish",
    "isComplete":true
  }

Die folgende Abbildung zeigt das Postman-Update:

Postman-Konsole mit der Antwort „204 (Kein Inhalt)“

DeleteTodoItem-Methode

Untersuchen Sie die DeleteTodoItem-Methode.

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

Testen der DeleteTodoItem-Methode

So löschen Sie mit Postman eine Aufgabe

  • Legen Sie die Methode auf DELETE fest.
  • Legen Sie den URI des zu löschenden Objekts fest, z. B. https://localhost:5001/api/todoitems/1.
  • Wählen Sie Send (Senden) aus.

Vermeiden von Overposting

Derzeit macht die Beispiel-App das gesamte TodoItem-Objekt verfügbar. In den Produktions-Apps sind die Daten, die eingegeben und mithilfe einer Teilmenge des Modells zurückgegeben werden, in der Regel eingeschränkt. Hierfür gibt es mehrere Gründe, wobei die Sicherheit einer der Hauptgründe ist. Die Teilmenge eines Modells wird üblicherweise als Datenübertragungsobjekt (DTO, Data Transfer Object), Eingabemodell oder Anzeigemodell bezeichnet. In diesem Artikel wird das DTO verwendet.

Ein DTO kann für Folgendes verwendet werden:

  • Vermeiden Sie Overposting.
  • Ausblenden von Eigenschaften, die Clients nicht anzeigen sollen
  • Auslassen einiger Eigenschaften, um die Nutzlast zu verringern
  • Vereinfachen von Objektgraphen, die geschachtelte Objekte enthalten Vereinfachte Objektgraphen können für Clients zweckmäßiger sein.

Um den DTO-Ansatz zu veranschaulichen, aktualisieren Sie die TodoItem-Klasse, sodass sie ein geheimes Feld einschließt:

namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public bool IsComplete { get; set; }
        public string Secret { get; set; }
    }
}

Das geheime Feld muss in dieser App ausgeblendet werden, eine administrative App kann es jedoch verfügbar machen.

Vergewissern Sie sich, dass Sie das geheime Feld veröffentlichen und abrufen können.

Erstellen Sie ein DTO-Modell:

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Aktualisieren Sie TodoItemsController, sodass TodoItemDTO verwendet wird:

// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
    return await _context.TodoItems
        .Select(x => ItemToDTO(x))
        .ToListAsync();
}

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return ItemToDTO(todoItem);
}

[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
    if (id != todoItemDTO.Id)
    {
        return BadRequest();
    }

    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    todoItem.Name = todoItemDTO.Name;
    todoItem.IsComplete = todoItemDTO.IsComplete;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
    {
        return NotFound();
    }

    return NoContent();
}

[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
    var todoItem = new TodoItem
    {
        IsComplete = todoItemDTO.IsComplete,
        Name = todoItemDTO.Name
    };

    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    return CreatedAtAction(
        nameof(GetTodoItem),
        new { id = todoItem.Id },
        ItemToDTO(todoItem));
}

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

private bool TodoItemExists(long id) =>
     _context.TodoItems.Any(e => e.Id == id);

private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
    new TodoItemDTO
    {
        Id = todoItem.Id,
        Name = todoItem.Name,
        IsComplete = todoItem.IsComplete
    };

Vergewissern Sie sich, dass Sie das geheime Feld weder veröffentlichen noch abrufen können.

Aufrufen der Web-API mit JavaScript

Mehr dazu finden Sie im -Tutorial: Aufrufen einer ASP.NET Core-Web-API mit JavaScript.

Hinzufügen der Authentifizierungsunterstützung zu einer Web-API

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende Identity Server ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende Identity Server ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Wichtig

Duende Software erhebt ggf. eine Lizenzgebühr für die Nutzung von Duende Identity Server in der Produktion. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 5.0 zu 6.0.

Weitere Informationen finden Sie in der Dokumentation zu Duende Identity Server (Website von Duende Software).

Veröffentlichen in Azure

Weitere Informationen zur Bereistellung in Azure finden Sie unter Schnellstart: Bereitstellen einer ASP.NET-Web-App.

Zusätzliche Ressourcen

Sie können den Beispielcode für dieses Tutorial anzeigen oder herunterladen. Informationen zum Herunterladen finden Sie hier.

Weitere Informationen finden Sie in den folgenden Ressourcen:

In diesem Tutorial lernen Sie die Grundlagen der Erstellung einer controllerbasierten Web-API, die eine Datenbank verwendet. Ein anderer Ansatz zum Erstellen von APIs in ASP.NET Core besteht in der Erstellung von minimalen APIs. Hilfe bei der Entscheidung zwischen minimalen APIs und controllerbasierten APIs finden Sie in der Übersicht über APIs. Anleitungen zum Erstellen einer minimalen API finden Sie im Tutorial: Erstellen einer minimalen API mit ASP.NET Core.

In diesem Tutorial lernen Sie, wie die folgenden Aufgaben ausgeführt werden:

  • Erstellen eines Web-API-Projekts
  • Hinzufügen einer Modellklasse und eines Datenbankkontexts
  • Erstellen eines Controllergerüsts mit CRUD-Methoden
  • Konfigurieren des Routings, der URL-Pfade und der Rückgabewerte
  • Aufrufen der Web-API mit Postman

Abschließend steht Ihnen eine Web-API zur Verfügung, die in einer relationalen Datenbank gespeicherte To-do-Elemente verwalten kann.

Übersicht

In diesem Tutorial wird die folgende API erstellt:

API Beschreibung Anforderungstext Antworttext
GET /api/todoitems Alle To-do-Elemente abrufen Keine Array von To-do-Elementen
GET /api/todoitems/{id} Ein Element nach ID abrufen Keine To-do-Element
POST /api/todoitems Neues Element hinzufügen To-do-Element To-do-Element
PUT /api/todoitems/{id} Vorhandenes Element aktualisieren To-do-Element Keine
DELETE /api/todoitems/{id}     Löschen eines Elements Keine Keine

Das folgende Diagramm zeigt den Entwurf der App.

Der Client wird durch ein Rechteck auf der linken Seite dargestellt. Er sendet eine Anforderung und erhält eine Antwort von der Anwendung, die in einem Rechteck auf der rechten Seite dargestellt ist. Innerhalb des Anwendungsrechtecks stellen drei Rechtecke den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung kommt im Controller der Anwendung an, und Lese-/Schreibvorgänge finden zwischen dem Controller und der Datenzugriffsschicht statt. Das Modell wird serialisiert und in der Antwort an den Client zurückgegeben.

Voraussetzungen

Erstellen eines Webprojekts

  • Klicken Sie im Menü Datei auf Neu>Projekt.
  • Wählen Sie die Vorlage ASP.NET Core-Webanwendung aus, und klicken Sie auf Weiter.
  • Geben Sie dem Projekt den Namen TodoApi, und klicken Sie auf Erstellen.
  • Vergewissern Sie sich, dass im Dialogfeld Neue ASP.NET Core-Webanwendung erstellen die Optionen .NET Core und ASP.NET Core 3.1 ausgewählt sind. Wählen Sie die Vorlage API aus, und klicken Sie auf Erstellen.

VS-Dialogfeld „Neues Projekt“

Hinweis

Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

Testen der API

Die Projektvorlage erstellt eine WeatherForecast-API. Rufen Sie in einem Browser die Get-Methode zum Testen der App auf.

Drücken Sie STRG+F5, um die App auszuführen. Visual Studio startet einen Browser und navigiert zu https://localhost:<port>/weatherforecast, wobei es sich bei <port> um eine zufällig ausgewählte Portnummer handelt.

Wenn Sie in einem Dialogfeld angeben müssen, ob Sie dem IIS Express-Zertifikat vertrauen, wählen Sie Ja aus. Wählen Sie im folgenden Dialogfeld Sicherheitswarnung die Option Ja aus.

Der zurückgegebene JSON-Code sieht in etwa wie folgt aus:

[
    {
        "date": "2019-07-16T19:04:05.7257911-06:00",
        "temperatureC": 52,
        "temperatureF": 125,
        "summary": "Mild"
    },
    {
        "date": "2019-07-17T19:04:05.7258461-06:00",
        "temperatureC": 36,
        "temperatureF": 96,
        "summary": "Warm"
    },
    {
        "date": "2019-07-18T19:04:05.7258467-06:00",
        "temperatureC": 39,
        "temperatureF": 102,
        "summary": "Cool"
    },
    {
        "date": "2019-07-19T19:04:05.7258471-06:00",
        "temperatureC": 10,
        "temperatureF": 49,
        "summary": "Bracing"
    },
    {
        "date": "2019-07-20T19:04:05.7258474-06:00",
        "temperatureC": -1,
        "temperatureF": 31,
        "summary": "Chilly"
    }
]

Hinzufügen einer Modellklasse

Ein Modell ist eine Gruppe von Klassen, die die Daten darstellen, die die App verwaltet. Das Modell für diese App ist eine einzelne TodoItem-Klasse.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt. Klicken Sie auf Hinzufügen>Neuer Ordner. Geben Sie dem Ordner den Namen Modelsanmelden.

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoItem, und wählen Sie Hinzufügen aus.

  • Ersetzen Sie den Vorlagencode durch den folgenden Code:

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Die Id-Eigenschaft fungiert als eindeutiger Schlüssel in einer relationalen Datenbank.

Modellklassen können überall im Projekt platziert werden, doch gemäß der Konvention wird der Ordner Models verwendet.

Hinzufügen eines Datenbankkontexts

Der Datenbankkontext ist die Hauptklasse, die die Entity Framework-Funktionen für ein Datenmodell koordiniert. Diese Klasse wird durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellt.

Hinzufügen von NuGet-Paketen

  • Klicken Sie im Menü Extras auf NuGet-Paket-Manager > NuGet-Pakete für Projektmappe verwalten.
  • Klicken Sie auf die Registerkarte Durchsuchen, und geben Sie dann Microsoft.EntityFrameworkCore.InMemory in das Suchfeld ein.
  • Wählen Sie im linken Bereich Microsoft.EntityFrameworkCore.InMemory aus.
  • Aktivieren Sie das Kontrollkästchen Projekt im rechten Bereich, und klicken Sie dann auf Installieren.

NuGet-Paket-Manager

Hinzufügen des TodoContext-Datenbankkontexts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Models, und wählen Sie Hinzufügen>Klasse aus. Nennen Sie die Klasse TodoContext, und klicken Sie auf Hinzufügen.
  • Geben Sie den folgenden Code ein:

    using Microsoft.EntityFrameworkCore;
    
    namespace TodoApi.Models
    {
        public class TodoContext : DbContext
        {
            public TodoContext(DbContextOptions<TodoContext> options)
                : base(options)
            {
            }
    
            public DbSet<TodoItem> TodoItems { get; set; }
        }
    }
    

Registrieren des Datenbankkontexts

In ASP.NET Core müssen Dienste wie der Datenbankkontext mit dem Container Abhängigkeitsinjektion registriert werden. Der Container stellt den Dienst für Controller bereit.

Aktualisieren Sie Startup.cs mit dem folgenden hervorgehobenen Code:

// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
               opt.UseInMemoryDatabase("TodoList"));
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Der vorangehende Code:

  • Entfernt nicht benötigte using-Deklarationen
  • Fügt dem Abhängigkeitscontainer den Datenbankkontext hinzu
  • Gibt an, dass der Datenbankkontext eine In-Memory Database verwendet

Erstellen eines Controllergerüsts

  • Klicken Sie mit der rechten Maustaste auf den Ordner Controller.

  • Wählen Sie Hinzufügen>Neues Gerüstelement aus.

  • Klicken Sie auf API-Controller mit Aktionen unter Verwendung von Entity Framework und dann auf Hinzufügen.

  • Führen Sie im Dialogfeld API-Controller mit Aktionen unter Verwendung von Entity Framework folgende Schritte aus:

    • Wählen Sie in der Modellklasse das Element TodoItem (TodoApi.Models) aus.
    • Wählen Sie in der Datenkontextklasse das Element TodoContext (TodoApi.Models) aus.
    • Wählen Sie Hinzufügen aus.

Der generierte Code hat folgende Auswirkungen:

  • Markiert die Klasse mit dem [ApiController]-Attribut. Dieses Attribut gibt an, dass der Controller auf Web-API-Anforderungen reagiert. Informationen über bestimmte Verhaltensweisen, die das Attribut aktivieren, finden Sie unter Erstellen von Web-APIs mit ASP.NET Core.
  • Verwendet die Abhängigkeitsinjektion, um den Datenbankkontext (TodoContext) dem Controller hinzuzufügen. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Die ASP.NET Core-Vorlagen für:

  • Controller mit Ansichten enthalten [action] in der Routenvorlage.
  • API-Controller enthalten keine [action] in der Routenvorlage.

Wenn sich das [action]-Token nicht in der Routenvorlage befindet, wird der action-Name von der Route ausgeschlossen. Dies bedeutet, dass der zugehörige Methodenname der Aktion nicht in der übereinstimmenden Route verwendet wird.

Überblick über die PostTodoItem-Erstellungsmethode

Ersetzen Sie die Rückgabeanweisung in PostTodoItem durch eine Anweisung mit dem nameof-Operator:

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    //return CreatedAtAction("PostTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(PostTodoItem), new { id = todoItem.Id }, todoItem);
}

Der oben stehende Code ist eine HTTP POST-Methode, wie durch das [HttpPost]-Attribut angegeben. Die Methode ruft den Wert der Aufgabe aus dem Text der HTTP-Anforderung ab.

Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

Die CreatedAtAction-Methode:

  • Gibt den HTTP-Statuscode 201 zurück, wenn der Vorgang erfolgreich ist. HTTP 201 ist die Standardantwort für eine HTTP POST-Methode, die eine neue Ressource auf dem Server erstellt.
  • Fügt der Antwort einen Location-Header hinzu. Der Location-Header legt den URI des neu erstellten To-do-Elements fest. Weitere Informationen finden Sie unter 201 Created.
  • Verweist auf die GetTodoItem-Aktion zum Erstellen des URIs des Location-Headers. Das nameof-Schlüsselwort von C# wird verwendet, um eine Hartcodierung des Aktionsnamens im CreatedAtAction-Aufruf zu vermeiden.

Installieren von Postman

Dieses Tutorial verwendet Postman zum Testen der Web-API.

  • Installieren Sie Postman.
  • Starten Sie die Web-App.
  • Starten Sie Postman.
  • Deaktivierund von SSL certificate verification (Verifizierung des SSL-Zertifikats):
    • Postman für Windows: Postman für Windows Datei>Einstellungen, (Registerkarte Allgemein), deaktivieren Sie die SSL-Zertifikatüberprüfung.
    • Postman für macOS: Postman für Windows Postman>Einstellungen, (Registerkarte Allgemein), deaktivieren Sie die SSL-Zertifikatüberprüfung.

      Warnung

      Aktivieren Sie die Verifizierung des SSL-Zertifikats wieder, nachdem Sie den Controller getestet haben.

Testen von PostTodoItem mit Postman

  • Erstellen Sie eine neue Anforderung.

  • Legen Sie als HTTP-Methode POST fest.

  • Legen Sie den URI auf https://localhost:<port>/api/todoitems fest. Beispiel: https://localhost:5001/api/todoitems.

  • Wählen Sie die Registerkarte Body (Text) aus.

  • Wählen Sie das Optionsfeld raw (Unformatiert) aus.

  • Legen Sie den Typ auf JSON (application/json) fest.

  • Geben Sie für die Aufgabe den Anforderungstext im JSON-Format ein:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Wählen Sie Send (Senden) aus.

    Postman mit erstellter Anforderung

Testen des Adressheader-URIs mit Postman

  • Wählen Sie auf der Registerkarte Header (Header) den Bereich Response (Antwort) aus.

  • Kopieren Sie den den Headerwert von Location (Speicherort):

    Registerkarte „Header“ in der Postman-Konsole

  • Legen Sie als HTTP-Methode GET fest.

  • Legen Sie den URI auf https://localhost:<port>/api/todoitems/1 fest. Beispiel: https://localhost:5001/api/todoitems/1.

  • Wählen Sie Send (Senden) aus.

Überblick über die GET-Methoden

Diese Methoden implementieren zwei GET-Endpunkte:

  • GET /api/todoitems
  • GET /api/todoitems/{id}

Testen Sie die App, indem Sie die beiden Endpunkte in einem Browser oder über Postman aufrufen. Zum Beispiel:

  • https://localhost:5001/api/todoitems
  • https://localhost:5001/api/todoitems/1

Durch einen Aufruf von GetTodoItems wird eine Antwort gesendet, die der folgenden ähnelt:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Testen von GET mit Postman

  • Erstellen Sie eine neue Anforderung.
  • Legen Sie die HTTP-Methode auf GET fest.
  • Legen Sie den Anforderungs-URI auf https://localhost:<port>/api/todoitems fest. Beispielsweise https://localhost:5001/api/todoitems.
  • Wählen Sie in Postman Two pane view (Ansicht mit zwei Bereichen) aus.
  • Wählen Sie Send (Senden) aus.

Diese App verwendet eine In-Memory-Datenbank. Wenn die App angehalten und dann wieder gestartet wird, werden durch die vorherige GET-Anforderung keine Daten zurückgegeben. Wenn keine Daten zurückgegeben werden, senden Sie mit POST Daten an die App.

Routing und URL-Pfade

Das Attribut [HttpGet] gibt eine Methode an, die auf eine HTTP GET-Anforderung antwortet. Der URL-Pfad für jede Methode wird wie folgt erstellt:

  • Beginnen Sie mit der Vorlagenzeichenfolge im Route-Attribut des Controllers:

    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;
    
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }
    
  • Ersetzen Sie [controller] durch den Namen des Controllers, bei dem es sich standardmäßig um den Namen der Controller-Klasse ohne das Suffix „Controller“ handelt. In diesem Beispiel ist der Klassenname des Controllers „TodoItemsController“. Der Controllername lautet also „TodoItems“. Beim ASP.NET Core-Routing wird die Groß-/Kleinschreibung nicht beachtet.

  • Wenn das [HttpGet]-Attribut eine Routenvorlage (z. B. [HttpGet("products")]) hat, fügen Sie diese an den Pfad an. In diesem Beispiel wird keine Vorlage verwendet. Weitere Informationen finden Sie unter Attributrouting mit Http[Verb]-Attributen.

In der folgenden GetTodoItem-Methode ist "{id}" eine Platzhaltervariable für den eindeutigen Bezeichner des To-do-Elements. Wenn GetTodoItem aufgerufen wird, wird der Wert von "{id}" in der URL der Methode in ihrem Parameter id bereitgestellt.

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

Rückgabewerte

Der Rückgabetyp der Methoden GetTodoItems und GetTodoItem ist ActionResult<T>-Typ . ASP.NET Core serialisiert automatisch das Objekt in JSON und schreibt den JSON-Code in den Text der Antwortnachricht. Der Antwortcode für diesen Rückgabetyp ist 200, vorausgesetzt, es gibt keine Ausnahmefehler. Nicht behandelte Ausnahmen werden in 5xx-Fehler übersetzt.

ActionResult-Rückgabetypen können eine Vielzahl von HTTP-Statuscodes darstellen. Beispielsweise kann GetTodoItem zwei verschiedene Statuswerte zurückgeben:

  • Wenn kein Element mit der angeforderten ID übereinstimmt, gibt die Methode einen 404-Fehlercode NotFound zurück.
  • Andernfalls gibt die Methode 200 mit einem JSON-Antworttext zurück. Die Rückgabe von item löst eine HTTP 200-Antwort aus.

PutTodoItem-Methode

Untersuchen Sie die PutTodoItem-Methode.

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

PutTodoItem ähnelt PostTodoItem, verwendet allerdings HTTP PUT. Die Antwort ist 204 (No Content). Gemäß der HTTP-Spezifikation erfordert eine PUT-Anforderung, dass der Client die gesamte aktualisierte Entität (nicht nur die Änderungen) sendet. Verwenden Sie HTTP PATCH, um Teilupdates zu unterstützen.

Wenn beim Aufrufen von PutTodoItem ein Fehler zurückgegeben wird, rufen Sie GET auf, um sicherzustellen, dass die Datenbank ein Element enthält.

Testen der PutTodoItem-Methode

In diesem Beispiel wird eine In-Memory-Datenbank verwendet, die jedes Mal initialisiert werden muss, wenn die App gestartet wird. Es muss ein Element in der Datenbank vorhanden sein, bevor Sie einen PUT-Aufruf durchführen. Rufen Sie vor einem PUT-Aufruf GET auf, um sicherzustellen, dass ein Element in der Datenbank vorhanden ist.

Aktualisieren Sie das To-do-Element, das über den ID-Wert 1 verfügt, und legen Sie als Namen „feed fish“ fest:

  {
    "id":1,
    "name":"feed fish",
    "isComplete":true
  }

Die folgende Abbildung zeigt das Postman-Update:

Postman-Konsole mit der Antwort „204 (Kein Inhalt)“

DeleteTodoItem-Methode

Untersuchen Sie die DeleteTodoItem-Methode.

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<ActionResult<TodoItem>> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return todoItem;
}

Testen der DeleteTodoItem-Methode

So löschen Sie mit Postman eine Aufgabe

  • Legen Sie die Methode auf DELETE fest.
  • Legen Sie den URI des zu löschenden Objekts fest, z. B. https://localhost:5001/api/todoitems/1.
  • Wählen Sie Send (Senden) aus.

Vermeiden von Overposting

Derzeit macht die Beispiel-App das gesamte TodoItem-Objekt verfügbar. In den Produktions-Apps sind die Daten, die eingegeben und mithilfe einer Teilmenge des Modells zurückgegeben werden, in der Regel eingeschränkt. Hierfür gibt es mehrere Gründe, wobei die Sicherheit einer der Hauptgründe ist. Die Teilmenge eines Modells wird üblicherweise als Datenübertragungsobjekt (DTO, Data Transfer Object), Eingabemodell oder Anzeigemodell bezeichnet. In diesem Artikel wird das DTO verwendet.

Ein DTO kann für Folgendes verwendet werden:

  • Vermeiden Sie Overposting.
  • Ausblenden von Eigenschaften, die Clients nicht anzeigen sollen
  • Auslassen einiger Eigenschaften, um die Nutzlast zu verringern
  • Vereinfachen von Objektgraphen, die geschachtelte Objekte enthalten Vereinfachte Objektgraphen können für Clients zweckmäßiger sein.

Um den DTO-Ansatz zu veranschaulichen, aktualisieren Sie die TodoItem-Klasse, sodass sie ein geheimes Feld einschließt:

public class TodoItem
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
    public string Secret { get; set; }
}

Das geheime Feld muss in dieser App ausgeblendet werden, eine administrative App kann es jedoch verfügbar machen.

Vergewissern Sie sich, dass Sie das geheime Feld veröffentlichen und abrufen können.

Erstellen Sie ein DTO-Modell:

public class TodoItemDTO
{
    public long Id { get; set; }
    public string Name { get; set; }
    public bool IsComplete { get; set; }
}

Aktualisieren Sie TodoItemsController, sodass TodoItemDTO verwendet wird:

    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
    {
        return await _context.TodoItems
            .Select(x => ItemToDTO(x))
            .ToListAsync();
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        return ItemToDTO(todoItem);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
    {
        if (id != todoItemDTO.Id)
        {
            return BadRequest();
        }

        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem == null)
        {
            return NotFound();
        }

        todoItem.Name = todoItemDTO.Name;
        todoItem.IsComplete = todoItemDTO.IsComplete;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
        {
            return NotFound();
        }

        return NoContent();
    }

    [HttpPost]
    public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
    {
        var todoItem = new TodoItem
        {
            IsComplete = todoItemDTO.IsComplete,
            Name = todoItemDTO.Name
        };

        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();

        return CreatedAtAction(
            nameof(GetTodoItem),
            new { id = todoItem.Id },
            ItemToDTO(todoItem));
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteTodoItem(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);

        if (todoItem == null)
        {
            return NotFound();
        }

        _context.TodoItems.Remove(todoItem);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    private bool TodoItemExists(long id) =>
         _context.TodoItems.Any(e => e.Id == id);

    private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
        new TodoItemDTO
        {
            Id = todoItem.Id,
            Name = todoItem.Name,
            IsComplete = todoItem.IsComplete
        };       
}

Vergewissern Sie sich, dass Sie das geheime Feld weder veröffentlichen noch abrufen können.

Aufrufen der Web-API mit JavaScript

Mehr dazu finden Sie im -Tutorial: Aufrufen einer ASP.NET Core-Web-API mit JavaScript.

Hinzufügen der Authentifizierungsunterstützung zu einer Web-API

ASP.NET Core Identity fügt Benutzeroberflächen-Anmeldefunktionen zu ASP.NET Core-Web-Apps hinzu. Verwenden Sie zum Sichern von Web-APIs und SPAs eine der folgenden Optionen:

Duende Identity Server ist ein OpenID Connect- und OAuth 2.0-Framework für ASP.NET Core. Duende Identity Server ermöglicht die folgenden Sicherheitsfeatures:

  • Authentifizierung als Dienst
  • Einmaliges Anmelden und einmaliges Abmelden für mehrere Anwendungstypen
  • Zugriffssteuerung für APIs
  • Federation Gateway

Wichtig

Duende Software erhebt ggf. eine Lizenzgebühr für die Nutzung von Duende Identity Server in der Produktion. Weitere Informationen finden Sie unter Migrieren von ASP.NET Core 5.0 zu 6.0.

Weitere Informationen finden Sie in der Dokumentation zu Duende Identity Server (Website von Duende Software).

Veröffentlichen in Azure

Weitere Informationen zur Bereistellung in Azure finden Sie unter Schnellstart: Bereitstellen einer ASP.NET-Web-App.

Zusätzliche Ressourcen

Sie können den Beispielcode für dieses Tutorial anzeigen oder herunterladen. Informationen zum Herunterladen finden Sie hier.

Weitere Informationen finden Sie in den folgenden Ressourcen: