Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
В рамках данной статьи продолжим работу с проектом из прошлой темы. На примере ASP.NET MVC 5 создадим простейший интерфейс управления данными. Хотя здесь используется MVC, но все те же действия могут быть легко перенесены и в Web API.
Добавим в проект класс модели Phone, с которой будем работать:
public class Phone { public string Id { get; set; } public string Name { get; set; } public int Price { get; set; } }
И изменим код контроллера HomeController:
using ReactApp.Models; using System; using System.Collections.Generic; using System.Web.Mvc; using System.Linq; namespace ReactApp.Controllers { public class HomeController : Controller { static List<Phone> 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 }, }; public ActionResult Index() { return View(); } public ActionResult GetPhones() { return Json(data, JsonRequestBehavior.AllowGet); } [HttpPost] public ActionResult AddPhone(Phone phone) { phone.Id = Guid.NewGuid().ToString(); data.Add(phone); return Json(phone); } [HttpDelete] public ActionResult DeletePhone(string id) { Phone phone = data.FirstOrDefault(x=>x.Id==id); if (phone != null) { data.Remove(phone); return Json(phone); } return HttpNotFound(); } } }
В качестве источника данных здесь используется массив объектов Phone, но при необходимости его можно заменить на контекст EF для работы с базой данных.
Метод Index обрабатывает запрос к главной странице. Метод GetPhones()
возвращает набор объектов в формате json. Метод AddPhone()
обрабатывает post-запросы и выполняет добавление объекта в массив. И метод DeletePhone()
удаляет объект из массива по id, обрабатывая
запросы DELETE.
ASP.NET MVC 5 имеет одну особенность, которая состоит в том, что по умолчанию запросы PUT, DELETE, OPTIONS отключены. И нам надо их явным образом подключить, если мы хотим использовать их в своем проекте. Для этого в файл конфигурации web.config в узел <system.webServer> надо добавить обработчик:
<remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
То есть полностью узел будет выглядеть следующим образом:
<system.webServer> <handlers> <remove name="Babel" /> <add name="Babel" verb="GET" path="*.jsx" type="React.Web.BabelHandlerFactory, React.Web" preCondition="integratedMode" /> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="HEAD,POST,DEBUG,PUT,DELETE,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>
Если мы используем Web API, то там соответственно ничего не надо добавлять в файл конфигурации.
В проекте в папке Scripts определим следующий файл 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.getUrl, 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) { var data = new FormData(); data.append("name", phone.name); data.append("price", phone.price); var xhr = new XMLHttpRequest(); xhr.open("post", this.props.postUrl, true); xhr.onload = function () { if (xhr.status == 200) { this.loadData(); } }.bind(this); xhr.send(data); } } // удаление объекта onRemovePhone(phone) { if (phone) { var data = new FormData(); data.append("id", phone.Id); var xhr = new XMLHttpRequest(); xhr.open("delete", this.props.deleteUrl, true); xhr.onload = function () { if (xhr.status == 200) { this.loadData(); } }.bind(this); xhr.send(data); } } 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 getUrl="/home/getphones" postUrl="/home/addphone" deleteUrl="/home/deletephone" />, document.getElementById("content") );
Здесь определен компонент PhonesList, который выводит список объектов. Для вывода каждого отдельного объекта предназначен компонент Phone. Для создания формы добавления нового объекта используется компонент PhoneForm.
Для отправки запросов к серверу здесь применяется стандартный объект XMLHttpRequest. Хотя также можно использовать функции из jquery или какие-то специальные библиотеки.
При рендеринге компоненту PhonesList передается ряд адресов для взаимодействия с сервером. После завершения рендеринга компонента срабатывает метод
componentDidMount()
, в котором производится загрузка начальных данных.
В методе onAddPhone()
получаем пришедший из компонента PhoneForm объект и посылаем его на сервер.
В методе onRemovePhone()
получаем пришедший из компонента Phone объект и также посылаем его на сервер.
В обоих этих методах в случае успешного запроса выполняем повторную загрузку данных и переустанавливаем состояние компонента. В то же время надо отметить, что методы контроллера HomeController при добавлении/удалении возвращают добавленный/удаленный объект, и в принципе вместо повторной загрузки всех данных мы можем получать этот объект и уже в самом компоненте добавлять или удалять его из массива phones, тем самым снизив количество запросов и объем передаваемых данных.
Представление Index.cshtml остается тем же, что и в прошлой теме:
@{ Layout = null; } <html> <head> <title>Hello React</title> </head> <body> <div id="content"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script> <script src='@Url.Content("~/Scripts/app.jsx")'></script> </body> </html>
Запустим приложение, и мы сможем просматривать, добавлять и удалять данные: