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

Von Rick Anderson und Kirk Larkin

In diesem Tutorial lernen Sie die Grundlagen der Erstellung einer Web-API mit ASP.NET Core kennen.

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 ist das Feld ganz links. Er sendet eine Anforderung und erhält von der Anwendung (Feld auf der rechten Seite) eine Antwort. Im Anwendungsfeld stellen drei Felder den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung geht im Controller der Anwendung ein, und Lese-/Schreibvorgänge erfolgen zwischen Controller und Datenzugriffsschicht. 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.
  • Bestätigen Sie im Dialogfeld Zusätzliche Informationen, dass Framework auf .NET 6.0 (Vorschau) festgelegt ist, und wählen Sie Erstellen aus.

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. Sie können sich dazu entscheiden, dem selbstsignierten Zertifikat zu vertrauen, das von IIS Express erstellt wurde, um SSL-Warnungen im Browser zu umgehen. Möchten Sie dem IIS Express-SLL-Zertifikat 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 unter 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 Models .

  • 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 dem folgenden 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. Weitere Informationen zu bestimmten Verhaltensweisen, die das Attribut ermöglicht, 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("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), 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
    
  • Wenn Sie das .NET 5.0 SDK oder die Runtime nicht installiert haben, installieren Sie die .NET 5.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 „TodoItems Controller“. 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 der 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.

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 ist das Feld ganz links. Er sendet eine Anforderung und erhält von der Anwendung (Feld auf der rechten Seite) eine Antwort. Im Anwendungsfeld stellen drei Felder den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung geht im Controller der Anwendung ein, und Lese-/Schreibvorgänge erfolgen zwischen Controller und Datenzugriffsschicht. 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“

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. Sie können sich dazu entscheiden, dem selbstsignierten Zertifikat zu vertrauen, das von IIS Express erstellt wurde, um SSL-Warnungen im Browser zu umgehen. Möchten Sie dem IIS Express-SLL-Zertifikat 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 unter 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 Models .

  • 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 dem folgenden 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. Weitere Informationen zu bestimmten Verhaltensweisen, die das Attribut ermöglicht, 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("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), 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 Postman

Dieses Tutorial verwendet Postman zum Testen der Web-API.

  • Installieren Sie Postman.
  • Starten Sie die Web-App.
  • Starten Sie Postman.
  • Deaktivieren Sie SSL certificate verification (Verifizierung des SSL-Zertifikats).
    • Deaktivieren Sie auf der Registerkarte General (Allgemein) unter File > Settings (Datei > Einstellungen) SSL certificate verification (Verifizierung des SSL-Zertifikats).

      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 „TodoItems Controller“. 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 der 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.

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 ist das Feld ganz links. Er sendet eine Anforderung und erhält von der Anwendung (Feld auf der rechten Seite) eine Antwort. Im Anwendungsfeld stellen drei Felder den Controller, das Modell und die Datenzugriffsschicht dar. Die Anforderung geht im Controller der Anwendung ein, und Lese-/Schreibvorgänge erfolgen zwischen Controller und Datenzugriffsschicht. 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“

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

  • 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. Weitere Informationen zu bestimmten Verhaltensweisen, die das Attribut ermöglicht, 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("GetTodoItem", new { id = todoItem.Id }, todoItem);
    return CreatedAtAction(nameof(GetTodoItem), 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 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 Postman

Dieses Tutorial verwendet Postman zum Testen der Web-API.

  • Installieren Sie Postman.
  • Starten Sie die Web-App.
  • Starten Sie Postman.
  • Deaktivieren Sie SSL certificate verification (Verifizierung des SSL-Zertifikats).
    • Deaktivieren Sie auf der Registerkarte General (Allgemein) unter File > Settings (Datei > Einstellungen) SSL certificate verification (Verifizierung des SSL-Zertifikats).

      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 „TodoItems Controller“. 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 der 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:

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

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

Weitere Informationen finden Sie unter Willkommen bei IdentityServer4.

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: