We present the second part of a series of articles devoted to the development on ASP.NET Core. In this tutorial, you will learn how to create server services using ASP.NET Core MVC to support mobile applications.
The first cycle of articles can be found here .
Mobile applications can easily exchange data with ASP.NET Core server services. Here you can download sample code for server services.
This material uses the Xamarin Forms ToDoRest application as a client. It includes separate clients for Android, iOS and Windows devices. From the link above you will find a guide that will help you create the application (and install the necessary free Xamarin tools), and you can also download a sample Xamarin solution. It includes the project of two ASP.NET web API services, which will be replaced by an ASP.NET Core application from this article (no changes are needed from the client).
The ToDoRest application supports listing, adding, deleting, and updating To-Do items. Each element is endowed with its own identifier, name, notes, and a property that indicates whether the element is executed.
In the basic representation of the elements, as shown above, there is the name of each element, and the presence of a flag indicates whether it has been executed.
Tap the + icon to open a dialog box for adding items:
Tap an item in the main list to open a dialog box to edit the name, notes and execution status, or to delete an item:
This sample defaults to using the services hosted at developer.xamarin.com , and allows you to read only. To test it yourself with the ASP.NET Core application that will be created in the next section and will work on your computer, you need to update the RestUrl
constant. Go to the ToDoREST
project and open the Constants.cs file. Replace RestUrl
with the IP address of your computer (this should not be localhost or 127.0.0.1, since the address is used from the device emulator, not from your PC). Also add the port number (5000). To ensure that the services work on the device, do not forget to turn off the firewall that blocks access to this port.
// URL of REST service (Xamarin ReadOnly Service) //public static string RestUrl = "http://developer.xamarin.com:8081/api/todoitems{0}"; // use your machine's IP address public static string RestUrl = "http://192.168.1.207:5000/api/todoitems/{0}";
Create a new ASP.NET Core web application in Visual Studio. Select a web API template and disable authentication. Name the project ToDoApi .
The application must respond to all requests on port 5000. Add to Program.cs .UseUrls("http://*:5000")
to get the following result:
var host = new WebHostBuilder() .UseKestrel() .UseUrls("http://*:5000") .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build();
Note: Be sure to start the application directly, and not through IIS Express, which by default ignores non-local requests. Run dotnet run
from the command line, or select the application name from the Debug Target drop-down menu on the Visual Studio toolbar.
Add a model class to represent the To-Do elements. Mark the required fields using the [Required]
attribute:
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 methods need a data processing method. Use the same IToDoRepository
interface as in the Xamarin sample:
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); } }
In this example, the implementation uses a private collection of elements:
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 = "Attend Xamarin University", Done = true }; var todoItem2 = new ToDoItem { ID = "b94afb54-a1cb-4313-8af3-b7511551b33b", Name = "Develop apps", Notes = "Use Xamarin Studio/Visual Studio", 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); } } }
Set up the implementation in Startup.cs :
public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc(); services.AddSingleton<IToDoRepository,ToDoRepository>(); }
Now you can proceed to create ToDoItemsController .
Learn more about creating web APIs in the article “Creating the first web API using ASP.NET Core MVC and Visual Studio” .
Add a new ToDoItemsController controller to the project. It must inherit the properties from Microsoft.AspNetCore.Mvc.Controller. Add the Route
attribute to indicate that the controller will process requests that are made to the paths and begin with api/todoitems
. The [controller]
token in the route is replaced with the controller name (without the Controller suffix); This is especially useful for global routes. Read more about routing .
The controller requires the IToDoRepository;
parameter IToDoRepository;
request an instance of this type through the controller constructor. In the runtime environment, this instance will be provided thanks to support for the dependency injection platform.
using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using ToDoApi.Interfaces; using ToDoApi.Models; namespace ToDoApi.Controllers { [Route("api/[controller]")] public class ToDoItemsController : Controller { private readonly IToDoRepository _toDoRepository; public ToDoItemsController(IToDoRepository toDoRepository) { _toDoRepository = toDoRepository; }
This API supports four HTTP verbs for create, read, update, and delete (CRUD) operations at the data source. The simplest operation is Read, which corresponds to the HTTP Get request.
To request a list of items, issue a GET request to the List
method. The [HttpGet]
attribute in the List
method indicates that this action should only process GET requests. The route for this action is the route specified on the controller. You do not need to use the name of the action as part of the route. You just need to make sure that each action has a unique and unambiguous route. Routing attributes to create specific routes can be applied at the level of both the controller and the method.
[HttpGet] public IActionResult List() { return Ok(_toDoRepository.All); }
The List
method List
200 OK response code and a list of all ToDo elements serialized as JSON.
You can test the new API method with a number of tools, such as Postman , as shown below:
By convention, the creation of new data elements is mapped to an HTTP POST command. The attribute [HttpPost];
applied to the Create
method [HttpPost];
in addition, the method takes a parameter ID and an instance of ToDoItem
. Command attributes such as [HttpPost]
can accept a route string (in this example, {id}
). The result will be the same as adding the [Route]
attribute to the action. Since the item
argument will be passed in the POST body, this parameter is decorated with the [FromBody]
attribute.
Inside the method, it is checked whether the element was correctly composed and whether it existed previously in the data warehouse; if there are no errors, it is added using the repository. ModelState.IsValid
performs model validation ; this should be done in every API method that accepts user input.
[HttpPost("{id}")] public IActionResult Create(string id, [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); }
The sample uses an enumeration of error codes transmitted to the mobile client.
public enum ErrorCode { TodoItemNameAndNotesRequired, TodoItemIDInUse, RecordNotFound, CouldNotCreateItem, CouldNotUpdateItem, CouldNotDeleteItem }
To check the addition of new items, use Postman: select the POST command, which provides a new object in JSON format in the request body. Also add a request header that specifies the Content-Type
for application/json
.
In the response, the method returns the newly created element.
You can change entries using HTTP PUT requests. In addition, the Edit
method is almost identical to Create
. Remember that if the record is not found, the Edit
action will NotFound
response (404).
[HttpPut("{id}")] public IActionResult Edit(string id, [FromBody] ToDoItem item) { try { if (item == null || !ModelState.IsValid) { return BadRequest(ErrorCode.TodoItemNameAndNotesRequired.ToString()); } var existingItem = _toDoRepository.Find(id); if (existingItem == null) { return NotFound(ErrorCode.RecordNotFound.ToString()); } _toDoRepository.Update(item); } catch (Exception) { return BadRequest(ErrorCode.CouldNotUpdateItem.ToString()); } return NoContent(); }
To test Postman, change the command to PUT and add the ID of the record being updated to the URL. Specify the data of the object being updated in the request body.
If successful, the method NoContent
(204) response, thereby ensuring consistency with the existing API.
To delete entries, you need to make DELETE requests to the service and pass the ID of the item to be deleted. After these updates, requests for non-existing elements will receive NotFound
responses, and a successful request will receive a NotFound
answer (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(); }
Remember that when testing the delete function, you do not need to add anything to the request body.
When developing server services for an application, we recommend creating a series of logical agreements or policies to resolve emerging issues. For example, in the services described above, requests for specific records that were not found received the NotFound
response, not BadRequest
. Similarly, the commands executed to the address of this service and transmitted in model types always checked ModelState.IsValid
and issued BadRequest
for invalid model types.
When you define a common policy for your API, you can encapsulate in the filter. In this article, you can learn how to encapsulate common API policies in ASP.NET Core MVC applications.
Source: https://habr.com/ru/post/319482/
All Articles