Dela via


Skapa serverdelstjänster för interna mobilappar med ASP.NET Core

Av James Montemagno

Mobilappar kan kommunicera med ASP.NET Core-serverdelstjänster. Anvisningar om hur du ansluter lokala webbtjänster från iOS-simulatorer och Android-emulatorer finns i Ansluta till lokala webbtjänster från Android-emulatorer och iOS-simulatorer.

Visa eller ladda ned exempelkod för serverdelstjänster

Exempel på intern mobilapp

Den här självstudien visar hur du skapar serverdelstjänster med hjälp av ASP.NET Core för att stödja ursprungliga mobilappar. Den använder en .NET MAUI app som sin interna klient. Exemplet innehåller ett ASP.NET Core Web API Services-projekt, som den här artikeln visar hur du skapar.

To Do Rest-program som körs på en Android Smartphone

Funktioner

TodoREST-appen stöder listning, tillägg, borttagning och uppdatering av att göra-objekt. Varje objekt har ett ID, ett namn, anteckningar och en egenskap som anger om det har gjorts ännu.

I föregående exempel visar huvudvyn för objekten varje objekts namn och anger om det görs med en bockmarkering.

Om du trycker på ikonen + navigerar du till sidan lägg till objekt:

Lägg till objekt-dialogruta

Om du trycker på ett objekt på huvudsidan navigerar du till en redigeringssida där objektets namn, anteckningar och gjorda inställningar kan ändras, eller så kan objektet tas bort:

dialogrutan Redigera objekt

Testa det själv mot ASP.NET Core-appen som skapades i nästa avsnitt genom att uppdatera appens RestUrl-konstant om du hostar den online. Annars kommunicerar appen med ASP.NET Core-appen som finns lokalt på datorn.

Android-emulatorer körs inte på den lokala datorn och använder en loopback-IP (10.0.2.2) för att kommunicera med den lokala datorn. Använd .NET MAUIDeviceInfo-klass för att identifiera operativsystemet som appen körs på för att använda rätt URL.

Gå till TodoREST-projektet och öppna filen Constants.cs. Filen Constants.cs innehåller följande konfiguration.

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}}";
    }
}

Du kan också distribuera webbtjänsten till en molntjänst som Azure och uppdatera RestUrl.

Skapa ASP.NET Core-projektet

Skapa ett nytt ASP.NET Core-webbprogram i Visual Studio. Välj webb-API-mallen. Ge projektet namnet TodoAPI.

Nytt ASP.NET-webbprogram dialogruta med projektmallen för webb-API vald

Appen bör svara på alla begäranden som görs via HTTPS till port 5001.

Not

Kör appen direkt i stället för bakom IIS Express. IIS Express ignorerar icke-lokala begäranden som standard. Kör dotnet run från en kommandotolk eller välj programnamnsprofilen i listrutan Debug Target i Visual Studio-verktygsfältet.

Lägg till en modellklass som representerar att göra-objekt. Markera obligatoriska fält med attributet [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; }
    }
}

API-metoder kräver att du definierar för att arbeta med data. Använd samma ITodoRepository gränssnitt som exemplet använder:

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);
    }
}

I det här exemplet använder lagringsplatsens implementering bara en privat samling objekt:

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);
        }
    }
}

Konfigurera implementeringen i 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();

Skapa kontrollanten

Lägg till en ny kontrollant i projektet, TodoItemsController. Den bör ärva från ControllerBase. Lägg till ett Route-attribut som anger att kontrollanten hanterar begäranden som görs till sökvägar som börjar med api/todoitems. Tokenen [controller] i rutten ersätts med namnet på kontroller (utan Controller-suffixet) och är särskilt användbar för globala rutter. Läs mer om routning.

Kontrollanten kräver en ITodoRepository för att fungera. begär en instans av den här typen via styrenhetens konstruktor. Vid körning tillhandahålls den här instansen med ramverkets stöd för beroendeinmatning.

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

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

Det här API:et stöder fyra olika HTTP-verb för att utföra CRUD-åtgärder (Skapa, Läsa, Uppdatera, Ta bort) på datakällan. Det enklaste av dessa är läsåtgärden, som motsvarar en HTTP-GET begäran.

Testa API:et med curl

Du kan testa API-metoden med hjälp av en mängd olika verktyg. I den här självstudien används följande kommandoradsverktyg med öppen källkod:

  • curl: Överför data med hjälp av olika protokoll, inklusive HTTP och HTTPS. curl används i den här självstudien för att anropa API:et med hjälp av HTTP-metoder GET, POST, PUToch DELETE.
  • jq: En JSON-processor som används i den här handledningen för att formatera JSON-data så att det är lätt att läsa från API-svaret.

Installera curl och jq

curl är förinstallerat på macOS och används direkt i macOS Terminal-programmet. Mer information om hur du installerar curl finns på den officiella webbplatsen för curl.

jq kan installeras från Homebrew från terminalen:

Installera Homebrew, om inte redan installerat, med följande kommando:

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

Följ instruktionerna som visas av installationsprogrammet.

Installera jq med Homebrew med följande kommando:

brew install jq

Mer information om installation av Homebrew och jq finns i Homebrew och jq.

Läsa objekt

Begäran om en lista över objekt görs med en GET-begäran till metoden List. Attributet [HttpGet] på metoden List anger att den här åtgärden endast ska hantera GET-begäranden. Vägen för den här åtgärden är den väg som anges på kontrollanten. Du behöver inte nödvändigtvis använda åtgärdsnamnet som en del av rutten. Du behöver bara se till att varje åtgärd har en unik och entydig väg. Routningsattribut kan användas på både kontrollant- och metodnivå för att bygga upp specifika vägar.

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

Anropa följande curl-kommando i terminalen:

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

Föregående curl-kommando innehåller följande komponenter:

  • -v: Aktiverar utförligt läge med detaljerad information om HTTP-svaret och är användbart för API-testning och felsökning.
  • -X GET: Anger användningen av METODEN HTTP GET för begäran. Även om curl ofta kan härleda den avsedda HTTP-metoden, gör det här alternativet det explicit.
  • 'https://localhost:5001/api/todoitems/': Det här är begärans mål-URL. I det här fallet är det en REST API-slutpunkt.
  • | jq: Det här segmentet är inte relaterat till curl direkt. Röret | är en skaloperator som tar utdata från kommandot till vänster och leder det till kommandot till höger. jq är en JSON-processor på kommandoraden. Även om det inte krävs gör jq de returnerade JSON-data lättare att läsa.

Metoden List returnerar en 200 OK-svarskod och alla Todo-objekt, serialiserade som 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
  }
]

Skapa objekt

Enligt konventionen mappas skapandet av nya dataobjekt till HTTP-POST verbet. Metoden Create har ett [HttpPost] attribut som tillämpas på den och accepterar en TodoItem-instans. Eftersom argumentet item skickas i brödtexten i POST anger den här parametern attributet [FromBody].

I metoden kontrolleras objektet för giltighet och tidigare existens i datalagret, och om inga problem uppstår läggs det till med hjälp av lagringsplatsen. Kontrollera ModelState.IsValid utför modellverifieringoch bör utföras i varje API-metod som accepterar användarinmatning.

[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);
}

Exemplet använder en enum som innehåller felkoder som skickas till den mobila klienten:

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

Testa att lägga till nya objekt i terminalen genom att anropa följande curl-kommando med hjälp av verbet POST och ange det nya objektet i JSON-format i begärandetexten.

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

Föregående curl-kommando innehåller följande alternativ:

  • --header 'Content-Type: application/json': Anger Content-Type-huvudet till application/json, vilket anger att begärandetexten innehåller JSON-data.
  • --data '{...}': Skickar angivna data i begärandetexten.

Metoden returnerar det nyligen skapade objektet i svaret.

Uppdatera objekt

Ändring av poster uppnås med HTTP-PUT-förfrågningar. Förutom den här ändringen är metoden Edit nästan identisk med Create. Om posten inte hittas returnerar åtgärden Edit ett NotFound(404) svar.

[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();
}

Om du vill testa med curl ändrar du verbet till PUT. Ange uppdaterade objektdata i brödtexten i begäran.

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

Den här metoden returnerar ett NoContent-svar (204) när det lyckas, för att säkerställa konsekvens med det redan existerande API:et.

Ta bort objekt

Du kan ta bort poster genom att göra DELETE begäranden till tjänsten och skicka ID:t för objektet som ska tas bort. Precis som med uppdateringar får förfrågningar för objekt som inte existerar ett NotFound-svar. Annars returnerar en lyckad begäran ett NoContent (204) svar.

[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();
}

Testa med curl genom att ändra HTTP-verbet till DELETE och lägga till ID:t för dataobjektet som ska tas bort i slutet av URL:en. Inget krävs i begärandetexten.

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

Förhindra överpublicering

För närvarande exponerar exempelappen hela TodoItem objektet. Produktionsappar begränsar vanligtvis de data som matas in och returneras med hjälp av en delmängd av modellen. Det finns flera orsaker till detta och säkerheten är viktig. Delmängden av en modell kallas vanligtvis för ett dataöverföringsobjekt (DTO), indatamodell eller vymodell. DTO- används i den här artikeln.

En DTO kan användas för att:

  • Förhindra överpublicering.
  • Dölj egenskaper som klienter inte ska visa.
  • Utelämna vissa egenskaper för att minska nyttolaststorleken.
  • Platta ut objektdiagram som innehåller kapslade objekt. Utplattade objektdiagram kan vara enklare för klienter.

Information om hur du demonstrerar DTO-metoden finns i Förhindra överpublicering

Vanliga webb-API-konventioner

När du utvecklar serverdelstjänsterna för din app vill du komma med en konsekvent uppsättning konventioner eller principer för hantering av övergripande problem. I den tjänst som visades tidigare mottog till exempel begäranden om specifika poster som inte hittades ett NotFound svar i stället för ett BadRequest svar. Kommandon som utförs på den här tjänsten, där modellbundna typer används, kontrollerar alltid ModelState.IsValid och returnerar en BadRequest för ogiltiga modelltyper.

När du har identifierat en gemensam princip för dina API:er kan du vanligtvis kapsla in den i ett filter. Läs mer om hur du kapslar in vanliga API-principer i ASP.NET Core MVC-program.

Se även