Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Продолжим работу с проектом из прошлой темы и рассмотрим непосредственно взаимодействие ASP.NET Core и React.JS. Для этого используем контроллеры Web API.
Добавим в проект папку Models и в ней определим класс модели Phone, с которой будем работать:
public class Phone { public string Id { get; set; } public string Name { get; set; } public int Price { get; set; } }
Далее для контроллеров добавим в проект папку Controllers, а в нее добавим новый контроллер PhonesController:
using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc; using ReactApp.Models; namespace ReactApp.Controllers { [Route("api/[controller]")] public class PhonesController : Controller { static readonly List<Phone> data; static PhonesController() { data = new List<Phone> { new Phone { Id = Guid.NewGuid().ToString(), Name="iPhone 7", Price=52000 }, new Phone { Id = Guid.NewGuid().ToString(), Name="Samsung Galaxy S7", Price=42000 }, }; } [HttpGet] public IEnumerable<Phone> Get() { return data; } [HttpPost] public IActionResult Post(Phone phone) { phone.Id = Guid.NewGuid().ToString(); data.Add(phone); return Ok(phone); } [HttpDelete("{id}")] public IActionResult Delete(string id) { Phone phone = data.FirstOrDefault(x => x.Id == id); if (phone == null) { return NotFound(); } data.Remove(phone); return Ok(phone); } } }
В роли источника данных здесь используется статический список, который инициализируется двумя объектами Phone.
Для управления данными здесь применяются три метода: Get()
возвращает список объектов, Post
добавляет один объект и
Delete()
удаляет объект по id.
В итоге финальный проект будет выглядеть следующим образом:
И для обращения к этим методам изменим файл app.jsx:
class Phone extends React.Component{ constructor(props){ super(props); this.state = {data: props.phone}; this.onClick = this.onClick.bind(this); } onClick(e){ this.props.onRemove(this.state.data); } render(){ return <div> <p><b>{this.state.data.name}</b></p> <p>Цена {this.state.data.price}</p> <p><button onClick={this.onClick}>Удалить</button></p> </div>; } } class PhoneForm extends React.Component{ constructor(props){ super(props); this.state = {name: "", price:0}; this.onSubmit = this.onSubmit.bind(this); this.onNameChange = this.onNameChange.bind(this); this.onPriceChange = this.onPriceChange.bind(this); } onNameChange(e) { this.setState({name: e.target.value}); } onPriceChange(e) { this.setState({price: e.target.value}); } onSubmit(e) { e.preventDefault(); var phoneName = this.state.name.trim(); var phonePrice = this.state.price; if (!phoneName || phonePrice<=0) { return; } this.props.onPhoneSubmit({ name: phoneName, price: phonePrice}); this.setState({name: "", price:0}); } render() { return ( <form onSubmit={this.onSubmit}> <p> <input type="text" placeholder="Модель телефона" value={this.state.name} onChange={this.onNameChange} /> </p> <p> <input type="number" placeholder="Цена" value={this.state.price} onChange={this.onPriceChange} /> </p> <input type="submit" value="Сохранить" /> </form> ); } } class PhonesList extends React.Component{ constructor(props){ super(props); this.state = { phones: [] }; this.onAddPhone = this.onAddPhone.bind(this); this.onRemovePhone = this.onRemovePhone.bind(this); } // загрузка данных loadData() { var xhr = new XMLHttpRequest(); xhr.open("get", this.props.apiUrl, true); xhr.onload = function () { var data = JSON.parse(xhr.responseText); this.setState({ phones: data }); }.bind(this); xhr.send(); } componentDidMount() { this.loadData(); } // добавление объекта onAddPhone(phone) { if (phone) { const data = new FormData(); data.append("name", phone.name); data.append("price", phone.price); var xhr = new XMLHttpRequest(); xhr.open("post", this.props.apiUrl, true); xhr.onload = function () { if (xhr.status === 200) { this.loadData(); } }.bind(this); xhr.send(data); } } // удаление объекта onRemovePhone(phone) { if (phone) { var url = this.props.apiUrl + "/" + phone.id; var xhr = new XMLHttpRequest(); xhr.open("delete", url, true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status === 200) { this.loadData(); } }.bind(this); xhr.send(); } } render(){ var remove = this.onRemovePhone; return <div> <PhoneForm onPhoneSubmit={this.onAddPhone} /> <h2>Список смартфонов</h2> <div> { this.state.phones.map(function(phone){ return <Phone key={phone.id} phone={phone} onRemove={remove} /> }) } </div> </div>; } } ReactDOM.render( <PhonesList apiUrl="/api/phones" />, document.getElementById("content") );
Здесь определен компонент PhonesList, который выводит список объектов. Для вывода каждого отдельного объекта предназначен компонент Phone. Для создания формы добавления нового объекта используется компонент PhoneForm.
Для отправки запросов к серверу здесь применяется стандартный объект XMLHttpRequest. Хотя также можно использовать функции из jquery или какие-то специальные библиотеки.
При рендеринге компоненту PhonesList передается ряд адресов для взаимодействия с сервером. После завершения рендеринга компонента срабатывает метод
componentDidMount()
, в котором производится загрузка начальных данных.
В методе onAddPhone()
получаем пришедший из компонента PhoneForm объект и посылаем его на сервер.
В методе onRemovePhone()
получаем пришедший из компонента Phone объект и также посылаем его на сервер.
В обоих этих методах в случае успешного запроса выполняем повторную загрузку данных и переустанавливаем состояние компонента. В то же время надо отметить, что методы контроллера HomeController при добавлении/удалении возвращают добавленный/удаленный объект, и в принципе вместо повторной загрузки всех данных мы можем получать этот объект и уже в самом компоненте добавлять или удалять его из массива phones, тем самым снизив количество запросов и объем передаваемых данных.
И также изменим класс Startup, чтобы задействовать контроллеры:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using React.AspNet; using JavaScriptEngineSwitcher.ChakraCore; using JavaScriptEngineSwitcher.Extensions.MsDependencyInjection; namespace ReactApp { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddReact(); services.AddJsEngineSwitcher(options => options.DefaultEngineName = ChakraCoreJsEngine.EngineName).AddChakraCore(); services.AddControllers(); } public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseReact(config => { }); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
Веб-страница index.html остается той же, что и в прошлой теме:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Hello React</title> </head> <body> <div id="content"></div> <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.development.js"></script> <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.development.js"></script> <script src="js/app.jsx"></script> </body> </html>
Запустим приложение, и мы сможем просматривать, добавлять и удалять данные: