Udostępnij za pośrednictwem


Tworzenie usług zaplecza dla natywnych aplikacji mobilnych przy użyciu platformy ASP.NET Core

Autor: James Montemagno

Aplikacje mobilne mogą komunikować się z usługami zaplecza ASP.NET Core. Aby uzyskać instrukcje dotyczące łączenia lokalnych usług sieci Web z symulatorów systemu iOS i emulatorów systemu Android, zobacz Connect to local web services from Android emulators and iOS simulators.

Wyświetlanie lub pobieranie przykładowego kodu usług zaplecza

Przykładowa natywna aplikacja mobilna

W tym samouczku pokazano, jak tworzyć usługi zaplecza przy użyciu platformy ASP.NET Core do obsługi natywnych aplikacji mobilnych. Używa aplikacji .NET MAUI jako swojego natywnego klienta. Przykład zawiera projekt usług ASP.NET Core Web API, który w tym artykule pokazuje, jak utworzyć.

aplikacja To Do Rest działająca na smartfonie z systemem Android

Funkcje

Aplikacja TodoREST obsługuje wyświetlanie listy, dodawanie, usuwanie i aktualizowanie elementów zadań do wykonania. Każdy element ma identyfikator, nazwę, notatki i właściwość wskazującą, czy został już wykonany.

W poprzednim przykładzie główny widok elementów zawiera listę nazw każdego elementu i wskazuje, czy jest on ukończony za pomocą znacznika wyboru.

Naciśnięcie ikony + powoduje przejście do strony dodawania elementu:

Okno dialogowe Dodawanie elementu

Naciśnięcie elementu na stronie głównej powoduje przejście do strony edycji, na której można zmodyfikować nazwę, notatki i gotowe ustawienia elementu lub element można usunąć:

Okno dialogowe Edytowanie elementu

Aby przetestować ją samodzielnie względem aplikacji ASP.NET Core utworzonej w następnej sekcji, jeśli hostujesz ją w trybie online, zaktualizuj stałą RestUrl aplikacji. W przeciwnym razie aplikacja będzie komunikować się z aplikacją ASP.NET Core hostowaną lokalnie na maszynie.

Emulatory Android nie działają na maszynie lokalnej i używają adresu IP pętli zwrotnej (10.0.2.2), aby komunikować się z maszyną lokalną. Użyj klasy .NET MAUIDeviceInfo, aby wykryć system operacyjny, na którym działa aplikacja, aby użyć poprawnego adresu URL.

Przejdź do TodoREST projektu i otwórz Constants.cs plik. Plik Constants.cs zawiera następującą konfigurację.

namespace TodoREST
{
    public static class Constants
    {
        // URL of REST service
        //public static string RestUrl = "https://dotnetmauitodorest.azurewebsites.net/api/todoitems/{0}";

        // URL of REST service (Android does not use localhost)
        // Use http cleartext for local deployment. Change to https for production
        public static string LocalhostUrl = DeviceInfo.Platform == DevicePlatform.Android ? "10.0.2.2" : "localhost";
        public static string Scheme = "https"; // or http
        public static string Port = "5001";
        public static string RestUrl = $"{Scheme}://{LocalhostUrl}:{Port}/api/todoitems/{{0}}";
    }
}

Opcjonalnie możesz wdrożyć usługę internetową w usłudze w chmurze, takiej jak platforma Azure, i zaktualizować usługę RestUrl.

Tworzenie projektu podstawowego ASP.NET

Utwórz nową aplikację internetową ASP.NET Core w programie Visual Studio. Wybierz szablon internetowego interfejsu API. Nadaj projektowi nazwę TodoAPI.

Nowe okno dialogowe aplikacji internetowej ASP.NET z wybranym szablonem projektu internetowego interfejsu API

Aplikacja powinna odpowiadać na wszystkie żądania wysyłane za pośrednictwem protokołu HTTPS do portu 5001.

Uwaga

Uruchom aplikację bezpośrednio, zamiast za pośrednictwem IIS Express. Usługa IIS Express domyślnie ignoruje żądania inne niż lokalne. Uruchom dotnet run z poziomu wiersza polecenia lub wybierz profil aplikacji z listy rozwijanej celu debugowania na pasku narzędzi programu Visual Studio.

Dodaj klasę modelu do reprezentowania elementów zadań do wykonania. Oznacz wymagane pola atrybutem [Required] :

using System.ComponentModel.DataAnnotations;

namespace TodoAPI.Models
{
    public class TodoItem
    {
        [Required]
        public string ID { get; set; }

        [Required]
        public string Name { get; set; }

        [Required]
        public string Notes { get; set; }

        public bool Done { get; set; }
    }
}

Metody interfejsu API wymagają zdefiniowania, aby pracować z danymi. Użyj tego samego interfejsu ITodoRepository, którego używa przykład.

using TodoAPI.Models;

namespace TodoAPI.Interfaces
{
    public interface ITodoRepository
    {
        bool DoesItemExist(string id);
        IEnumerable<TodoItem> All { get; }
        TodoItem Find(string id);
        void Insert(TodoItem item);
        void Update(TodoItem item);
        void Delete(string id);
    }
}

W tym przykładzie implementacja repozytorium używa tylko prywatnej kolekcji elementów:

using TodoAPI.Interfaces;
using TodoAPI.Models;

namespace TodoAPI.Services
{
    public class TodoRepository : ITodoRepository
    {
        private List<TodoItem> _todoList;

        public TodoRepository()
        {
            InitializeData();
        }

        public IEnumerable<TodoItem> All
        {
            get { return _todoList; }
        }

        public bool DoesItemExist(string id)
        {
            return _todoList.Any(item => item.ID == id);
        }

        public TodoItem Find(string id)
        {
            return _todoList.FirstOrDefault(item => item.ID == id);
        }

        public void Insert(TodoItem item)
        {
            _todoList.Add(item);
        }

        public void Update(TodoItem item)
        {
            var todoItem = this.Find(item.ID);
            var index = _todoList.IndexOf(todoItem);
            _todoList.RemoveAt(index);
            _todoList.Insert(index, item);
        }

        public void Delete(string id)
        {
            _todoList.Remove(this.Find(id));
        }

        private void InitializeData()
        {
            _todoList = new List<TodoItem>();

            var todoItem1 = new TodoItem
            {
                ID = "6bb8a868-dba1-4f1a-93b7-24ebce87e243",
                Name = "Learn app development",
                Notes = "Take Microsoft Learn Courses",
                Done = true
            };

            var todoItem2 = new TodoItem
            {
                ID = "b94afb54-a1cb-4313-8af3-b7511551b33b",
                Name = "Develop apps",
                Notes = "Use Visual Studio and Visual Studio Code",
                Done = false
            };

            var todoItem3 = new TodoItem
            {
                ID = "ecfa6f80-3671-4911-aabe-63cc442c1ecf",
                Name = "Publish apps",
                Notes = "All app stores",
                Done = false,
            };

            _todoList.Add(todoItem1);
            _todoList.Add(todoItem2);
            _todoList.Add(todoItem3);
        }
    }
}

Skonfiguruj implementację w programie Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddSingleton<TodoAPI.Interfaces.ITodoRepository, TodoAPI.Services.TodoRepository>();
builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Tworzenie kontrolera

Dodaj nowy kontroler do projektu TodoItemsController. Powinien dziedziczyć z ControllerBase. Route Dodaj atrybut wskazujący, że kontroler obsługuje żądania wysyłane do ścieżek rozpoczynających się od api/todoitems. Token [controller] w ścieżce jest zastępowany nazwą kontrolera (pomijając sufiks Controller) i jest szczególnie przydatny w trasach globalnych. Dowiedz się więcej o routingu.

Kontroler wymaga ITodoRepository do działania; żądaj utworzenia instancji tego typu za pośrednictwem konstruktora kontrolera. Podczas działania to wystąpienie jest udostępniane dzięki wsparciu platformy dla iniekcji zależności.

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

    public TodoItemsController(ITodoRepository todoRepository)
    {
        _todoRepository = todoRepository;
    }

Ten interfejs API obsługuje cztery różne czasowniki HTTP do wykonywania operacji CRUD (tworzenie, odczyt, aktualizacja, usuwanie) w źródle danych. Najprostszą z nich jest operacja Odczyt, która odpowiada żądaniu HTTP GET .

Testowanie interfejsu API przy użyciu narzędzia curl

Metodę interfejsu API można przetestować przy użyciu różnych narzędzi. W tym samouczku są używane następujące narzędzia wiersza polecenia typu open source:

  • curl: transferuje dane przy użyciu różnych protokołów, w tym HTTP i HTTPS. Program curl jest używany w tym samouczku do wywoływania interfejsu API przy użyciu metod GETHTTP , POST, PUTi DELETE.
  • jq: procesor JSON używany w tym samouczku do formatowania danych JSON, dzięki czemu są łatwe do odczytania z odpowiedzi API.

Instalowanie narzędzia curl i jq

Program curl jest wstępnie zainstalowany w systemie macOS i jest używany bezpośrednio w aplikacji terminalu systemu macOS. Aby uzyskać więcej informacji na temat instalowania programu curl, zobacz oficjalną witrynę internetową curl.

Pakiet jq można zainstalować z poziomu oprogramowania Homebrew z poziomu terminalu:

Zainstaluj oprogramowanie Homebrew, jeśli jeszcze nie jest zainstalowane, za pomocą następującego polecenia:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Postępuj zgodnie z instrukcjami przedstawionymi przez instalatora.

Zainstaluj pakiet jq przy użyciu oprogramowania Homebrew za pomocą następującego polecenia:

brew install jq

Aby uzyskać więcej informacji na temat instalacji oprogramowania Homebrew i jq, zobacz Homebrew i jq.

Odczytywanie elementów

Żądanie listy elementów odbywa się za pomocą żądania GET do List metody . Atrybut [HttpGet] metody List wskazuje, że ta akcja powinna obsługiwać tylko żądania GET. Trasa dla tej akcji to trasa określona na kontrolerze. Nie musisz używać nazwy akcji w ramach trasy. Wystarczy upewnić się, że każda akcja ma unikatową i jednoznaczną trasę. Atrybuty routingu można stosować zarówno na poziomie kontrolera, jak i metody w celu utworzenia określonych tras.

[HttpGet]
public IActionResult List()
{
    return Ok(_todoRepository.All);
}

W terminalu wywołaj następujące polecenie curl:

curl -v -X GET 'https://localhost:5001/api/todoitems/' | jq

Poprzednie polecenie curl zawiera następujące składniki:

  • -v: Aktywuje tryb szczegółowy, zapewniając szczegółowe informacje o odpowiedzi HTTP i jest przydatny do testowania API oraz rozwiązywania problemów.
  • -X GET: określa użycie metody HTTP GET dla żądania. Chociaż narzędzie curl często może wywnioskować docelową metodę HTTP, ta opcja sprawia, że jest jawna.
  • 'https://localhost:5001/api/todoitems/': jest to docelowy adres URL żądania. W tym przypadku jest to punkt końcowy interfejsu REST API.
  • | jq: ten segment nie jest powiązany bezpośrednio z curl. Operator potokowy | jest operatorem powłoki, który pobiera dane wyjściowe z polecenia po lewej stronie i przekazuje je do polecenia po prawej stronie. jq jest procesorem JSON wiersza polecenia. Chociaż nie jest to wymagane, jq zwracane dane JSON są łatwiejsze do odczytania.

Metoda List zwraca kod odpowiedzi 200 OK i wszystkie elementy listy rzeczy do zrobienia w formacie JSON.

[
  {
    "id": "6bb8a868-dba1-4f1a-93b7-24ebce87e243",
    "name": "Learn app development",
    "notes": "Take Microsoft Learn Courses",
    "done": true
  },
  {
    "id": "b94afb54-a1cb-4313-8af3-b7511551b33b",
    "name": "Develop apps",
    "notes": "Use Visual Studio and Visual Studio Code",
    "done": false
  },
  {
    "id": "ecfa6f80-3671-4911-aabe-63cc442c1ecf",
    "name": "Publish apps",
    "notes": "All app stores",
    "done": false
  }
]

Tworzenie elementów

Zgodnie z konwencją tworzenie nowych elementów danych przypisuje się czasownikowi HTTP POST. Metoda Create ma zastosowany atrybut [HttpPost] i akceptuje wystąpienie TodoItem. item Ponieważ argument jest przekazywany w treści POST-a, ten parametr określa [FromBody] atrybut.

Wewnątrz metody element jest sprawdzany pod kątem ważności i wcześniejszego istnienia w magazynie danych, a jeśli nie wystąpią żadne problemy, zostanie dodany przy użyciu repozytorium. Sprawdzanie ModelState.IsValid przeprowadza weryfikację modelu i powinno być wykonywane w każdej metodzie API, która akceptuje dane wejściowe użytkownika.

[HttpPost]
public IActionResult Create([FromBody]TodoItem item)
{
    try
    {
        if (item == null || !ModelState.IsValid)
        {
            return BadRequest(ErrorCode.TodoItemNameAndNotesRequired.ToString());
        }
        bool itemExists = _todoRepository.DoesItemExist(item.ID);
        if (itemExists)
        {
            return StatusCode(StatusCodes.Status409Conflict, ErrorCode.TodoItemIDInUse.ToString());
        }
        _todoRepository.Insert(item);
    }
    catch (Exception)
    {
        return BadRequest(ErrorCode.CouldNotCreateItem.ToString());
    }
    return Ok(item);
}

W przykładzie użyto enum zawierających kody błędów, które są przekazywane do klienta mobilnego.

public enum ErrorCode
{
    TodoItemNameAndNotesRequired,
    TodoItemIDInUse,
    RecordNotFound,
    CouldNotCreateItem,
    CouldNotUpdateItem,
    CouldNotDeleteItem
}

W terminalu przetestuj dodawanie nowych elementów, wywołując następujące polecenie curl przy użyciu czasownika POST i podając nowy obiekt w formacie JSON w treści żądania.

curl -v -X POST 'https://localhost:5001/api/todoitems/' \
--header 'Content-Type: application/json' \
--data '{
  "id": "6bb8b868-dba1-4f1a-93b7-24ebce87e243",
  "name": "A Test Item",
  "notes": "asdf",
  "done": false
}' | jq

Poprzednie polecenie curl zawiera następujące opcje:

  • --header 'Content-Type: application/json': Ustawia Content-Type nagłówek na application/json, wskazując, że treść żądania zawiera dane JSON.
  • --data '{...}': wysyła określone dane w treści żądania.

Metoda zwraca nowo utworzony element w odpowiedzi.

Aktualizowanie elementów

Modyfikowanie rekordów jest osiągane przy użyciu żądań PUT HTTP. Poza tą zmianą metoda jest prawie identyczna Edit z Create. Jeśli rekord nie zostanie znaleziony, Edit akcja zwróci NotFound odpowiedź (404).

[HttpPut]
public IActionResult Edit([FromBody] TodoItem item)
{
    try
    {
        if (item == null || !ModelState.IsValid)
        {
            return BadRequest(ErrorCode.TodoItemNameAndNotesRequired.ToString());
        }
        var existingItem = _todoRepository.Find(item.ID);
        if (existingItem == null)
        {
            return NotFound(ErrorCode.RecordNotFound.ToString());
        }
        _todoRepository.Update(item);
    }
    catch (Exception)
    {
        return BadRequest(ErrorCode.CouldNotUpdateItem.ToString());
    }
    return NoContent();
}

Aby przetestować polecenie curl, zmień czasownik na PUT. Określ zaktualizowane dane obiektu w treści żądania.

curl -v -X PUT 'https://localhost:5001/api/todoitems/' \
--header 'Content-Type: application/json' \
--data '{
  "id": "6bb8b868-dba1-4f1a-93b7-24ebce87e243",
  "name": "A Test Item",
  "notes": "asdf",
  "done": true
}' | jq

Ta metoda zwraca odpowiedź NoContent (204) w przypadku powodzenia, aby zapewnić spójność z istniejącym już wcześniej interfejsem API.

Usuwanie elementów

Usuwanie rekordów jest realizowane przez wykonywanie DELETE żądań do usługi i przekazywanie identyfikatora elementu do usunięcia. Podobnie jak w przypadku aktualizacji, żądania dotyczące elementów, które nie istnieją, otrzymują NotFound odpowiedzi. W przeciwnym razie pomyślne żądanie zwraca odpowiedź o kodzie NoContent (204).

[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
    try
    {
        var item = _todoRepository.Find(id);
        if (item == null)
        {
            return NotFound(ErrorCode.RecordNotFound.ToString());
        }
        _todoRepository.Delete(id);
    }
    catch (Exception)
    {
        return BadRequest(ErrorCode.CouldNotDeleteItem.ToString());
    }
    return NoContent();
}

Przetestuj przy użyciu narzędzia curl, zmieniając czasownik HTTP na DELETE i dołączając identyfikator obiektu danych, który chcesz usunąć, na końcu adresu URL. Treść żądania nie jest konieczna.

curl -v -X DELETE 'https://localhost:5001/api/todoitems/6bb8b868-dba1-4f1a-93b7-24ebce87e243'

Zapobieganie nadmiernemu publikowaniu

Obecnie przykładowa aplikacja uwidacznia cały TodoItem obiekt. Aplikacje produkcyjne zwykle ograniczają dane wejściowe i zwracane przy użyciu podzestawu modelu. Istnieje wiele powodów, a jednym z głównych jest bezpieczeństwo. Podzbiór modelu jest zwykle nazywany obiektem transferu danych (DTO), modelem wejściowym lub modelem widoku. DTO jest używane w tym artykule.

DTO może służyć do:

  • Zapobiegaj nadmiernemu publikowaniu.
  • Ukryj właściwości, których klienci nie powinni wyświetlać.
  • Pomiń niektóre właściwości, aby zmniejszyć rozmiar ładunku.
  • Spłaszczać grafy obiektów zawierające zagnieżdżone obiekty. Spłaszczone grafy obiektów mogą być wygodniejsze dla klientów.

Aby zademonstrować podejście DTO, zobacz Zapobieganie nadmiernemu przesyłaniu danych

Typowe konwencje internetowego interfejsu API

Podczas opracowywania usług zaplecza dla aplikacji, powinieneś ustalić spójny zestaw konwencji lub zasad dotyczących obsługi zagadnień przekrojowych. Na przykład w przedstawionej wcześniej usłudze żądania dotyczące określonych rekordów, które nie zostały znalezione, otrzymały odpowiedź NotFound, zamiast odpowiedzi BadRequest. Polecenia wysyłane do tej usługi, które dotyczyły typów powiązanych z modelem, zawsze sprawdzały ModelState.IsValid i zwracały dla BadRequest nieprawidłowych typów modeli.

Po zidentyfikowaniu typowych zasad dla interfejsów API można zwykle hermetyzować je w filtrze. Dowiedz się więcej o sposobie hermetyzacji typowych zasad interfejsu API w aplikacjach ASP.NET Core MVC.

Zobacz też