Взаимодействие React.JS и ASP.NET Core

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 21.11.2019

Продолжим работу с проектом из прошлой темы и рассмотрим непосредственно взаимодействие 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.

В итоге финальный проект будет выглядеть следующим образом:

React in ASP.NET Core 3

И для обращения к этим методам изменим файл 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>

Запустим приложение, и мы сможем просматривать, добавлять и удалять данные:

Использование React JS в ASP.NET Core Web API
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850