Создание внутренних служб для собственных мобильных приложений в ASP.NET Core
Мобильные приложения могут взаимодействовать с внутренними службами ASP.NET Core. Инструкции по подключению локальных веб-служб из симуляторов iOS и эмуляторов Android см. в статье о подключении к локальным веб-службам из симуляторов iOS и эмуляторов Android.
Просмотреть или скачать пример кода внутренней службы
Пример собственного мобильного приложения
В этом руководстве показано, как создавать внутренние службы с помощью ASP.NET Core для поддержки собственных мобильных приложений. Он использует приложение Xamarin.Forms TodoRest в качестве собственного клиента, которое включает отдельные собственные клиенты для Android, iOS и Windows. Вы можете следовать связанному руководству, чтобы создать собственное приложение (и установить необходимые бесплатные средства Xamarin) и скачать пример решения Xamarin. Пример Xamarin включает проект служб веб-API ASP.NET Core, который заменяет приложение ASP.NET Core этой статьи (без изменений, необходимых клиенту).
Функции
Приложение TodoREST поддерживает перечисление, добавление, удаление и обновление элементов To-Do. Каждый элемент имеет идентификатор, имя, заметки и свойство, указывающее, выполнен ли он.
В предыдущем примере основное представление элементов содержит имя каждого элемента и указывает, выполняется ли оно с помощью флажка.
Если выбрать значок +
, открывается диалоговое окно добавления элемента:
При выборе элемента на экране основного списка открывается диалоговое окно редактирования, где можно изменить параметры имени, заметок и готовности элемента, а также удалить его:
Чтобы протестировать приложение ASP.NET Core, созданное в следующем разделе, работающем на компьютере, обновите константу приложения RestUrl
.
Эмуляторы Android не выполняются на локальном компьютере и используют IP-адрес обратного цикла (10.0.2.2) для взаимодействия с локальным компьютером. Используйте Xamarin.Essentials DeviceInfo , чтобы определить, какая операционная система работает, чтобы использовать правильный URL-адрес.
Перейдите TodoREST
к проекту и откройте Constants.cs
файл. Файл Constants.cs
содержит следующую конфигурацию.
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}";
}
}
При необходимости можно развернуть веб-службу в облачной службе, например Azure, и обновить ее RestUrl
.
Создание проекта ASP.NET Core
Создайте веб-приложение ASP.NET Core в Visual Studio. Выберите шаблон веб-API. Назовите проект TodoAPI.
Приложение должно отвечать на все запросы, сделанные на порт 5000, включая трафик HTTP для нашего мобильного клиента. Обновление Startup.cs
так UseHttpsRedirection не выполняется в разработке:
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();
});
}
Примечание.
Запустите приложение напрямую, а не за IIS Express. Служба IIS Express игнорирует не локальные запросы по умолчанию. Запустите dotnet run из командной строки или выберите профиль имени приложения в раскрывающемся списке "Целевой объект отладки" на панели инструментов Visual Studio.
Добавьте класс модели для представления элементов задач. Пометьте обязательные поля с помощью атрибута [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 нужен определенный способ для работы с данными. Используйте тот же интерфейс ITodoRepository
, который использует исходный пример 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);
}
}
В этом примере реализация использует просто частную коллекцию элементов:
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);
}
}
}
Настройка реализации в Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITodoRepository, TodoRepository>();
services.AddControllers();
}
Создание контроллера
Добавьте новый контроллер в проект TodoItemsController. Он должен наследоваться от ControllerBase. Добавьте атрибут, указывающий Route
, что контроллер обрабатывает запросы, сделанные к путям, начиная с api/todoitems
. Токен [controller]
в маршруте заменяется на имя контроллера (суффикс Controller
опускается) и особенно удобен для глобальных маршрутов. Дополнительные сведения о маршрутизации.
Для работы контроллеру нужен ITodoRepository
; запросите экземпляр этого типа через конструктор контроллера. Во время выполнения этот экземпляр предоставляется с помощью поддержки платформы внедрения зависимостей.
[ApiController]
[Route("api/[controller]")]
public class TodoItemsController : ControllerBase
{
private readonly ITodoRepository _todoRepository;
public TodoItemsController(ITodoRepository todoRepository)
{
_todoRepository = todoRepository;
}
Этот API поддерживает четыре различных HTTP-команды для выполнения операций CRUD (создание, чтение, обновление, удаление) с источником данных. Самым простым из них является операция чтения, которая соответствует HTTP-запросу GET
.
Тестирование API с помощью curl
Вы можете протестировать метод API с помощью различных средств. В этом руководстве используются следующие открытый код средства командной строки:
- curl: передает данные с помощью различных протоколов, включая HTTP и HTTPS. Curl используется в этом руководстве для вызова API с помощью методов
GET
HTTP ,POST
иPUT
DELETE
. - jq: обработчик JSON, используемый в этом руководстве для форматирования данных JSON, чтобы легко считывать ответ API.
Установка curl и jq
curl предварительно установлен в macOS и используется непосредственно в приложении терминала macOS. Дополнительные сведения об установке curl см . на официальном веб-сайте curl.
jq можно установить из Homebrew из терминала:
Установите Homebrew, если он еще не установлен, с помощью следующей команды:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Следуйте инструкциям, представленным установщиком.
Установите jq с помощью Homebrew с помощью следующей команды:
brew install jq
Дополнительные сведения об установке Homebrew и jq см. в разделе Homebrew и jq.
Чтение элементов
Запрос списка элементов, выполняется с помощью отправки запроса GET в метод List
. Атрибут[HttpGet]
в методе List
указывает, что это действие должно обрабатывать только запросы GET. Маршрут для этого действия соответствует маршруту, указанному на контроллере. Использовать имя действия в составе маршрута необязательно. Нужно лишь убедиться, что каждое действие имеет уникальный и однозначный маршрут. Атрибуты маршрутизации можно применять на уровне как контроллера, так и метода для создания определенных маршрутов.
[HttpGet]
public IActionResult List()
{
return Ok(_todoRepository.All);
}
В терминале вызовите следующую команду curl:
curl -v -X GET 'http://localhost:5000/api/todoitems/' | jq
Предыдущая команда curl включает следующие компоненты:
-v
: активирует подробный режим, предоставляя подробные сведения о ответе HTTP и полезно для тестирования и устранения неполадок API.-X GET
: указывает использование метода HTTPGET
для запроса. Хотя curl часто может выводить предполагаемый метод HTTP, этот параметр делает его явным.'http://localhost:5000/api/todoitems/'
: это целевой URL-адрес запроса. В этом экземпляре это конечная REST точка API.| jq
: этот сегмент не связан с curl напрямую.|
Канал — это оператор оболочки, который принимает выходные данные команды слева и "каналы" в команду справа.jq
— это обработчик JSON командной строки. Хотя и не требуется,jq
возвращаемые данные JSON проще считывать.
Метод List
возвращает код отклика 200 OK и все элементы Todo, сериализованные в формате 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
}
]
Создание элементов
По соглашению создание новых элементов данных сопоставляется с http-командой POST
. Метод Create
имеет примененный к нему атрибут [HttpPost]
и принимает экземпляр TodoItem
. Так как аргумент item
передается в текст POST, этот параметр указывает атрибут [FromBody]
.
Внутри метода элемент проверяется на допустимость и предшествующее существование в хранилище данных, а при отсутствии проблем он добавляется с помощью репозитория. Проверка ModelState.IsValid
приводит к проверке модели и должна выполняться в каждом методе API, принимающем вводимые пользователем данные.
[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);
}
В примере используется enum
содержащий коды ошибок, передаваемые мобильному клиенту:
public enum ErrorCode
{
TodoItemNameAndNotesRequired,
TodoItemIDInUse,
RecordNotFound,
CouldNotCreateItem,
CouldNotUpdateItem,
CouldNotDeleteItem
}
В терминале проверьте добавление новых элементов, вызвав следующую команду curl с помощью POST
команды и предоставив новый объект в формате JSON в тексте запроса.
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
Предыдущая команда curl включает следующие параметры:
--header 'Content-Type: application/json'
: задаетContent-Type
заголовок в значениеapplication/json
, указывающее, что текст запроса содержит данные JSON.--data '{...}'
: отправляет указанные данные в тексте запроса.
Метод возвращает вновь созданный элемент в отклике.
Обновление элементов
Изменение записей выполняется с помощью HTTP-запросов PUT
. За исключением этого изменения, метод Edit
практически идентичен Create
. Если запись не найдена, Edit
действие возвращает NotFound
ответ (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();
}
Чтобы проверить с помощью curl, измените команду PUT
на . Укажите обновленные данные объекта в тексте запроса.
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
Этот метод возвращает отклик NoContent
(204) при успешном выполнении, чтобы обеспечить согласованность с ранее существовавшими API.
Удаление элементов
Удаление записей выполняется путем выполнения DELETE
запросов к службе и передачи идентификатора элемента, который требуется удалить. Как и в случае с обновлениями, запросы на элементы, которые не существуют, получают NotFound
ответы. В противном случае успешный 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();
}
Проверьте с помощью curl, изменив HTTP-команду DELETE
на и добавив идентификатор объекта данных для удаления в конце URL-адреса. В тексте запроса ничего не требуется.
curl -v -X DELETE 'http://localhost:5000/api/todoitems/6bb8b868-dba1-4f1a-93b7-24ebce87e243'
Предотвращение избыточной публикации
В настоящее время пример приложения предоставляет весь объект TodoItem
. Рабочие приложения обычно ограничивают вводимые данные и возвращают их с помощью подмножества модели. Это связано с несколькими причинами, и безопасность является основной. Подмножество модели обычно называется объектом передачи данных (DTO), моделью ввода или моделью представления. В этой статье используется DTO.
DTO можно использовать для следующего:
- Предотвращение избыточной публикации.
- Скрытие свойств, которые клиенты не должны просматривать.
- Опустить некоторые свойства для уменьшения размера полезных данных.
- Сведение графов объектов, содержащих вложенные объекты. Сведенные графы объектов могут быть удобнее для клиентов.
Чтобы продемонстрировать подход DTO, см. статью "Запрет чрезмерной публикации"
Общие соглашения веб-API
При разработке внутренних служб для вашего приложения вы хотите создать согласованный набор соглашений или политик для обработки перекрестных проблем. Например, в службе, показанной ранее, запросы на определенные записи, которые не были найдены, получили NotFound
ответ, а не BadRequest
ответ. Аналогичным образом, команды, выполненные для этой службы, которые передавались в привязанные типы модели, всегда проверяли ModelState.IsValid
и возвращали BadRequest
для недопустимых типов модели.
Определив общую политику для своих API, вы обычно можете инкапсулировать ее в фильтр. Дополнительные сведения о том, как инкапсулировать общие политики API в приложения ASP.NET Core MVC.
Дополнительные ресурсы
ASP.NET Core