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 iOS Simulators and Android Emulators (Nawiązywanie połączenia z lokalnymi usługami sieci Web za pomocą 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).
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:
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ąć:
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.
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
GET
HTTP ,POST
,PUT
iDELETE
. - jq: procesor JSON używany w tym samouczku do formatowania danych JSON, 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.
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 '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 HTTPGET
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 JSON wiersza polecenia. Chociaż nie jest to wymagane,jq
zwracane dane JSON są łatwiejsze do odczytania.
Metoda List
zwraca kod odpowiedzi OK 200 OK i wszystkie elementy zadań do wykonania, serializowane jako 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 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 JSON w treś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'
: UstawiaContent-Type
nagłówek naapplication/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 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.