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 internetowych z symulatorów systemu iOS i emulatorów systemu Android, zobacz Połączenie z lokalnych usług sieci Web z symulatorów systemu iOS i emulatorów systemu Android.

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 Xamarin.Forms TodoRest jako natywnego klienta, który obejmuje oddzielnych klientów natywnych dla systemów Android, iOS i Windows. Możesz skorzystać z połączonego samouczka, aby utworzyć aplikację natywną (i zainstalować niezbędne bezpłatne narzędzia platformy Xamarin) i pobrać przykładowe rozwiązanie platformy Xamarin. Przykład platformy Xamarin zawiera projekt usług internetowego interfejsu API platformy ASP.NET Core, który zastępuje aplikacja ASP.NET Core tego artykułu (bez zmian wymaganych przez klienta).

Aplikacja 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ła jeszcze zakończona.

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

Naciśnięcie ikony powoduje otwarcie okna dialogowego dodawania + elementu:

Okno dialogowe Dodawanie elementu

Naciśnięcie elementu na ekranie listy głównej powoduje otwarcie okna dialogowego edycji, w którym można zmodyfikować ustawienia Nazwy, Notatki i Gotowe 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 uruchomionej na komputerze, zaktualizuj stałą RestUrl aplikacji.

Emulatory systemu Android nie działają na komputerze lokalnym i używają adresu IP sprzężenia zwrotnego (10.0.2.2), aby komunikować się z maszyną lokalną. Użyj narzędzia Xamarin.Essentials DeviceInfo , aby wykryć, jakie działanie systemu działa, aby użyć poprawnego adresu URL.

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

using Xamarin.Essentials;
using Xamarin.Forms;

namespace TodoREST
{
    public static class Constants
    {
        // URL of REST service
        //public static string RestUrl = "https://YOURPROJECT.azurewebsites.net:8081/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 RestUrl = DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000/api/todoitems/{0}" : "http://localhost:5000/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 do portu 5000, w tym ruch HTTP w postaci zwykłego tekstu dla naszego klienta mobilnego. UseHttpsRedirection Aktualizacja Startup.cs nie jest uruchamiana w programowania:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        // For mobile apps, allow http traffic.
        app.UseHttpsRedirection();
    }

    app.UseRouting();

    app.UseAuthorization();

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

Uwaga

Uruchom aplikację bezpośrednio, a nie za usługą IIS Express. Usługa IIS Express domyślnie ignoruje żądania inne niż lokalne. Uruchom polecenie dotnet uruchom z poziomu wiersza polecenia lub wybierz profil nazwy aplikacji z listy rozwijanej Debugowanie elementu docelowego na pasku narzędzi programu Visual Studio.

Dodaj klasę modelu reprezentującą elementy 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ą pewnego sposobu pracy z danymi. Użyj tego samego ITodoRepository interfejsu, z których korzysta oryginalny przykład platformy Xamarin:

using System.Collections.Generic;
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 używa tylko prywatnej kolekcji elementów:

using System.Collections.Generic;
using System.Linq;
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 for Mac",
                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 Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<ITodoRepository, TodoRepository>();
    services.AddControllers();
}

Tworzenie kontrolera

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

Kontroler wymaga ITodoRepository funkcji; zażądaj wystąpienia tego typu za pośrednictwem konstruktora kontrolera. W czasie wykonywania to wystąpienie jest udostępniane przy użyciu obsługi iniekcji zależności platformy.

[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: JSprocesor ON używany w tym samouczku do formatowania JSdanych ON, dzięki czemu można łatwo odczytać z odpowiedzi interfejsu 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.

jq można zainstalować z Homepoziomu naparu z terminalu:

Zainstaluj Homebrew, jeśli jeszcze nie został zainstalowany, 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 polecenia brew za pomocą Homenastępującego polecenia:

brew install jq

Aby uzyskać więcej informacji na Hometemat instalacji brew 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 'http://localhost:5000/api/todoitems/' | jq

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

  • -v: Aktywuje tryb pełny, podając szczegółowe informacje o odpowiedzi HTTP i jest przydatny do testowania interfejsu API i 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.
  • 'http://localhost:5000/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. Potok | jest operatorem powłoki, który pobiera dane wyjściowe z polecenia po lewej stronie i "potoki" do polecenia po prawej stronie. jq jest procesorem ON wiersza JSpolecenia. Chociaż nie jest to wymagane, jq zwracane JSdane ON są łatwiejsze do odczytania.

Metoda List zwraca kod odpowiedzi OK 200 OK i wszystkie elementy zadań do wykonania, serializowane jako JSWŁĄCZONE:

[
  {
    "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 for Mac",
    "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 jest mapowane na czasownik HTTP POST . Metoda Create ma [HttpPost] zastosowany atrybut i akceptuje TodoItem wystąpienie. item Ponieważ argument jest przekazywany w treści post, 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 walidację modelu i powinno być wykonywane w każdej metodzie interfejsu 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 kodów błędów 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 ON w JStreści żądania.

curl -v -X POST 'http://localhost:5000/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 JSdane WŁĄCZONE.
  • --data '{...}': wysyła określone dane w treści żądania.

Metoda zwraca nowo utworzony element w odpowiedzi.

Aktualizowanie elementów

Modyfikowanie rekordów odbywa się przy użyciu żądań HTTP PUT . 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 'http://localhost:5000/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 NoContent odpowiedź (204) po pomyślnym zakończeniu, aby zapewnić spójność ze wstępnie istniejącym 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 NoContent odpowiedź (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, aby usunąć go na końcu adresu URL. Treść żądania nie jest wymagana.

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

Zapobieganie nadmiernemu delegowaniu

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, dla których jest to ważne. 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 delegowaniu.
  • Ukryj właściwości, których klienci nie powinni wyświetlać.
  • Pomiń niektóre właściwości, aby zmniejszyć rozmiar ładunku.
  • Spłaszczane wykresy 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 delegowaniu

Typowe konwencje internetowego interfejsu API

Podczas opracowywania usług zaplecza dla aplikacji warto utworzyć spójny zestaw konwencji lub zasad dotyczących obsługi problemów obejmujących wiele cięć. Na przykład w pokazanej wcześniej usłudze żądania dotyczące określonych rekordów, które nie zostały znalezione, otrzymały NotFound odpowiedź, a BadRequest nie odpowiedź. Podobnie polecenia wykonywane w tej usłudze, które przeszły w typach powiązanych modelu, 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.

Dodatkowe zasoby