Это интересное время, если вы пользователь Mac OS и имеете опыт разработки в .NET. Границы между технологиями, используемыми приложением, и средой, в которой оно размещается, теперь стираются. До сих пор мой рабочий процесс разработки всегда включал сеанс удаленного рабочего стола с некоторым Windows Server, на котором я размещал серверные компоненты моей системы на основе .NET. Но поскольку Microsoft объявила, что .NET переходит на открытый исходный код и с появлением контейнерных сред, теперь вы можете запустить всю среду разработки на вашем Mac!

В этой статье я хотел поделиться концепциями и этапами настройки, которые можно использовать для создания полнофункциональной современной среды веб-разработки на основе .NET, полностью размещенной на Mac OS. В частности, мы хотим иметь возможность настроить среду со следующими технологическими компонентами:

  • Современная интерфейсная среда веб-разработки, которая упрощает разработку в стиле одностраничных приложений (SPA). В частности, мы будем настраивать и настраивать замену Webpack, Babel, React и Hotmodule.
  • ASP.NET Core для разработки уровня сервиса RESTful. На этом уровне мы будем использовать Entity Framework Core в качестве нашей инфраструктуры объектно-реляционного сопоставления (ORM).
  • Microsoft SQL Server для нашей реляционной базы данных. Вы можете легко использовать MySQL, SQLite и т. Д., Но я получаю удовольствие от того, что SQL Server работает на моем Mac, и я надеюсь, что вы тоже! Мы также установим Microsoft SQL Operations Studio (предварительная версия). Это бесплатный инструмент, который работает в Mac OS и позволяет нам управлять нашими объектами SQL Server.

Что касается интегрированной среды разработки (IDE), в этой статье будет использоваться Visual Studio Code. В будущем, если вы планируете писать какие-либо отображаемые на стороне сервера представления с использованием ASP.NET MVC, я рекомендую использовать Visual Studio для Mac, так как вы оцените поддержку синтаксиса Razor. На момент написания этой статьи этого не было в Visual Studio Code. Но для наших целей Visual Studio Code подойдет.

Что ж, это много технологий, которые мы собираемся использовать. Но не беспокойтесь, если вы не использовали один или несколько из них или не понимаете некоторые из упомянутых модных словечек или концепций. Эта статья является исчерпывающей, и мы расскажем о концепциях и технологиях по мере их освоения.

Мы построим нашу среду разработки по принципу «от бэкэнда к клиентскому интерфейсу», начиная с нашей реляционной базы данных. Как только мы это сделаем, мы создадим Movies API, чтобы возвращать вызывающему простой список названий фильмов. Наконец, мы создадим несколько простых компонентов React, которые вызывают API и отображают список заголовков фильмов.

Microsoft SQL Server

Какие? Microsoft SQL Server работает на моем Mac? Кошки и собаки живут вместе и все такое? Что ж, сейчас такие вещи возможны, поскольку стремление отделять приложения от инфраструктуры набирает обороты. Для тех из вас, кто плохо знаком с этой концепцией, подумайте о времени, когда у вас было приложение, которое нормально работало в одной среде, а затем вы отправляете его в другую среду, которая, как вы клянетесь, является точным зеркалом того, в котором приложение работает успешно, только чтобы обнаружить, что это не работает. Затем вы идете искать иголку в стоге сена, пытаясь определить, какой параметр конфигурации вы, возможно, пропустили. Когда вы думаете об этом, мы обычно прибегаем к настройкам приложения, таким как строки подключения и пути к файлам. Но конфигурация среды включает в себя такие вещи, как конфигурация серверного программного обеспечения, настройки ОС, переменные среды и т. Д. Отсутствие любого из них может действительно сорвать процесс развертывания.

Возможно, вы слышали о Докере. Думайте о Docker как о платформе, которую вы можете запустить на своем Mac (или Windows или Linux), которая может запускать образы программного обеспечения в несколько изолированной среде. Например, если бы у Microsoft был образ SQL Server, вы могли бы запустить его в Docker на своем Mac. Это именно то, что мы будем использовать, потому что у Microsoft действительно есть образ SQL Server. Начнем с установки Docker, а затем установим SQL Server.

Начните с загрузки установщика Docker Community Edition для Mac. Вам нужно будет создать бесплатную учетную запись в Docker Store, чтобы получить доступ к странице загрузки. Установка такая же, как и при любой другой установке на основе .dmg.

После запуска Docker вы увидите, что он появится в строке меню.

Docker имеет интерфейс командной строки, который позволяет вам делать такие вещи, как загружать образы контейнеров в Docker, запускать и останавливать их, проверять работающие образы и т. Д. Чтобы загрузить последний образ SQL Server, выполните следующую команду Docker из Терминала:

sudo docker pull microsoft/mssql-server-linux:2017-latest

Команда pull загрузит образ из Docker Hub (репозиторий образов) и зарегистрирует его в вашей установке Docker. Чтобы запустить его, вы запускаете следующую команду Docker из Терминала:

sudo docker run -e 'ACCEPT_EULA=Y' \
-e 'SA_PASSWORD=Passw0rd!' \
-p 1433:1433 \
--name mssql \
-d microsoft/mssql-server-linux:2017-latest

По моему опыту, установка завершится ошибкой, если вы не предоставите пароль, который соответствует требованиям безопасности SQL Server по умолчанию. Также обратите внимание, что опция —- name позволяет дать вашему контейнеру понятное имя. Вы будете использовать это имя во многих других командах, поэтому укажите одно короткое, приятное и узнаваемое.

Чтобы убедиться, что ваш контейнер существует и работает, используйте следующую команду:

docker ps -a

В нем будут перечислены все установленные вами контейнеры, а в столбце STATUS будет указано, запущен ли образ (или когда последний раз выходил из образа). По умолчанию контейнер не запускается автоматически при перезагрузке Mac, поэтому, если вы обнаружите, что ваш контейнер не запущен, используйте следующую команду (заменив понятное имя, которое вы использовали для своего контейнера SQL Server):

docker start mssql

Итак, как мы взаимодействуем с нашим SQL Server? Что ж, есть способ запустить оболочку bash внутри контейнера, которая, по сути, помещает вас в эту среду. Оттуда вы можете использовать sqlcmd (расположенный в каталоге изображения / opt / mssql-tools / bin /). Но если вы знакомы с SQL Server Management Studio, вам стоит попробовать Microsoft Operations Studio. Он предоставит вам графический интерфейс для взаимодействия с вашим сервером. Скачайте его бесплатно. В настоящее время загрузка осуществляется в виде zip-архива. Вы дважды щелкаете zip-архив, чтобы получить приложение SQL Operations Studio.app, которое вы хотите переместить в папку Приложения. После этого просто запустите приложение.

Если вы раньше использовали Visual Studio Code, то увидите, что SQL Operations Studio выглядит и ощущается очень знакомой. Команда SQL Operations Studio использует ту же модель расширяемости, что и Visual Studio Code, что позволяет разработчикам создавать все виды полезных расширений.

Изнутри SQL Operations Studio вы должны определить соединение с вашей базой данных. Вы можете сделать это, щелкнув значок «Новое соединение» в верхней части панели «Серверы». Введите учетные данные, которые вы использовали при первом запуске образа SQL Server.

По сравнению с SQL Management Studio вы обнаружите, что многие параметры контекстного меню, такие как «Создать базу данных» и т. Д., Отсутствуют, но вы всегда можете открыть окно запроса и использовать свои команды T-SQL для создания и удаления объектов базы данных. В этой статье мы будем полагаться на Entity Framework Core, который будет обрабатывать создание всех объектов базы данных за нас. А пока смотрите - Microsoft SQL Server работает на Mac OS!

SDK .NET Core

Далее нам нужно скачать и установить .NET Core. Если вы не знакомы с .NET Core, это подмножество .NET Framework, построенное на меньших пакетах NuGet и работающее в Windows, Linux и Mac OS. С .NET Core мы можем выбрать именно ту функциональность, которая нужна нашему приложению, путем извлечения только необходимых нам пакетов NuGet. Это более гибкий подход к выбору функций, и мы не в конечном итоге платим за производительность за функции, которые нам не нужны, как это происходит при использовании монолитной .NET Framework.

Перейдите на страницу загрузки .NET Core и загрузите .NET Core SDK для Mac OS. На момент написания этой статьи версия .NET Core была v2.1.300. Обязательно загрузите и установите SDK, а не только среду выполнения. SDK поставляется с полезными утилитами командной строки, которые позволят нам создать проект .NET Core, как мы будем делать позже в этом руководстве. Вы можете проверить установку, введя следующую команду dotnet в Терминале:

dotnet --version

Вы должны увидеть установленную версию dotnet.

Код Visual Studio

Теперь, когда у нас установлен .NET Core SDK, пора подумать о том, какой редактор мы можем использовать для создания нашего веб-приложения. Вы можете использовать любой редактор по своему усмотрению. Полезно, если ваш редактор имеет встроенную командную оболочку и библиотеку расширений, которая позволяет вам работать с C #. В этом руководстве мы будем использовать код Visual Studio.

Visual Studio Code - это бесплатный текстовый редактор с открытым исходным кодом и моделью расширяемости, которая позволяет третьим сторонам расширять его функциональность. Например, хотя Visual Studio Code имеет встроенную поддержку JavaScript, TypeScript и Node.js, вы можете использовать расширение для добавления поддержки C #, Java и т. Д. Расширения не обязательно связаны только с языковой поддержкой. Вы можете найти расширения, которые делают отличные вещи, такие как линтинг вашего кода, настройка значков папок и файлов, добавление альтернативных тем в ваш редактор и т. Д.

Вы можете скачать Visual Studio Code здесь. После того, как вы его запустили, нам просто нужно включить поддержку C # через расширение. Фактическое расширение для этого C # для кода Visual Studio. В представлении Расширения (⇧⌘X) введите C #, и он будет одним из первых результатов поиска. Установите расширение, и все будет готово.

API ASP.NET Core с Entity Framework Core

Теперь давайте создадим простой проект RESTful API в Visual Studio Code, используя ASP.NET Core и Entity Framework Core. Одна из приятных особенностей Visual Studio Code - это интегрированный Терминал, из которого мы можем управлять папками и выполнять команды dotnet.

Включите Терминал, набрав ⌃`. Оттуда создайте папку в любом месте файловой системы, где вы хотите хранить файлы вашего проекта. В моей системе есть папка Projects, содержащая все мои проекты кода. Создайте папку с именем «MoviesAPI». Мы просто собираемся создать очень простой API, который позволит вызывающему абоненту получать список фильмов.

В каталоге введите следующую команду:

dotnet new

Команда dotnet new предоставляет вам список шаблонов проектов, поставляемых с .NET Core SDK. Особый интерес представляет ASP.NET Core с шаблоном React.js. Это довольно надежный шаблон, который создаст полноценный проект React с JavaScriptServices, который представляет собой набор клиентских технологий для ASP.NET Core. Это определенно то, что вы захотите проверить после того, как намочите ноги. Но эта статья больше о настройке конкретной среды разработки и понимании того, как это сделать с нуля. Таким образом, мы продолжим, создав простой проект ASP.NET Core и собирая его по частям, чтобы мы могли понять, как создать настраиваемую среду.

В папке MoviesAPI введите в Терминале следующее:

dotnet new web

Это создаст пустой проект ASP.NET Core с минимальной конфигурацией.

Расширение C #, которое вы установили в среду Visual Studio Code, обнаружит файлы, созданные с помощью SDK, и распознает эту папку как содержащую проект .NET. Вы должны увидеть такое диалоговое окно:

Нажмите «Да», так как это приведет к включению ресурсов Visual Studio Code, которые позволят вам создавать и отлаживать приложение из среды редактора. Как только это будет завершено, убедитесь, что это приложение работает. В командной строке введите следующую команду:

dotnet run

.NET Core запустит веб-сервер разработки, прослушивающий порты 5000 (HTTP) и 5001 (HTTPS). Запустите браузер и перейдите по адресу http: // localhost: 5000, и вы должны увидеть текст Hello World.

Вы можете нажать Ctrl + C, чтобы остановить процесс приложения, и использовать команду dotnet run, чтобы запустить его снова.

Так как же превратить наше базовое приложение в API? Нам нужно сделать несколько шагов. Во-первых, нам нужно настроить ссылку на пакет NuGet Entity Framework Core. Во-вторых, нам нужно настроить Entity Framework Core для работы с нашим экземпляром SQL Server. В-третьих, мы воспользуемся подходом, основанным на коде, при настройке классов сущностей. Как ORM, Entity Framework позаботится о создании нашей базы данных за нас. Наконец, мы настроим наше веб-приложение для использования шаблона проектирования MVC для маршрутизации входящих HTTP-запросов к соответствующему контроллеру, который будет работать с Entity Framework Core для извлечения данных из базы данных и отправки данных обратно вызывающей стороне в формате JSON. .

Entity Framework Core использует модель поставщика, которая обеспечивает функциональность ORM с множеством различных источников данных. Вы можете использовать MySQL, SQLite или даже поставщик базы данных в памяти. В нашем случае, поскольку мы потратили время на настройку SQL Server, давайте использовать Entity Framework Core с поставщиком SQL Server. Установив ссылку на пакет Microsoft.EntityFrameworkCore.SqlServer NugGet, будут извлечены все необходимые зависимости пакета. В папке проекта введите в командной строке следующее:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 2.1.0

После загрузки пакета расширение C # в Visual Studio Code снова запускается, предлагая вам запустить команду Restore для получения зависимостей. Идите вперед и нажмите кнопку «Восстановить», чтобы восстановить эти зависимости.

Теперь, когда наша ORM установлена, мы должны начать с создания любых классов сущностей, с которыми мы будем работать при взаимодействии с нашей базой данных. Создайте в проекте папку с именем «Entities» и в ней создайте новый класс с именем «Movie».

Используя атрибуты из пространства имен System.ComponentModel.DataAnnotations, мы можем определить класс сущности, который предоставляет Entity Framework Core (в данном случае поставщик SQL Server), который будет использовать для определения этой сущности в базе данных.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MoviesAPI.Entities
{
     public class Movie
     {
          [Key]
          [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
          public int Id { get; set; }
          [Required]
          [MaxLength(100)]
          public string Name { get; set; }
          [Required]
          public int Year { get; set; }
     }
}

Теперь, когда наш класс сущности определен, нам нужно создать DbContext и зарегистрировать его с помощью встроенной системы внедрения зависимостей в нашем приложении. Создайте папку с именем Services и создайте новый класс с именем MoviesDbContext.

using Microsoft.EntityFrameworkCore;
using MoviesAPI.Entities;
namespace MoviesAPI.Services
{
     public class MoviesDbContext : DbContext
     {
          public DbSet<Movie> Movies { get; set; }
          public MoviesDbContext(
               DbContextOptions<MoviesDbContext> options)
               : base(options)
          {
               Database.EnsureCreated();
          }
     }
}

Для удобства нам также понадобится способ наполнить нашу базу данных некоторыми предварительными данными. К сожалению, текущая версия Entity Framework Core не поддерживает это, но реализовать ее достаточно просто. Создайте класс с именем MoviesDbContextExtensions и поместите в него код заполнения:

using System.Collections.Generic;
using System.Linq;
using MoviesAPI.Entities;
namespace MoviesAPI.Services
{
     public static class MoviesDbContextExtensions
     {
          public static void CreateSeedData
               (this MoviesDbContext context)
          {
               if (context.Movies.Any())
                    return;
               var movies = new List<Movie>()
               {
                    new Movie()
                    {
                         Name = "Avengers: Infinity War",
                         Year = 2018
                    },
                    new Movie()
                    {
                         Name = "Thor: Ragnarock",
                         Year = 2017
                    },
                    new Movie()
                    {
                         Name = "Black Panther",
                         Year = 2018
                    }
               };
               context.AddRange(movies);
               context.SaveChanges();
          }
     }
}

Чтобы зарегистрировать DbContext в системе внедрения зависимостей, мы просто добавляем его к IServicesCollection в методе ConfigureServices() класса Startup. Вот как выглядит наш класс Startup. На следующих этапах мы добавим дополнительную конфигурацию.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MoviesAPI.Services;
namespace MoviesAPI
{
     public class Startup
     {
          public void ConfigureServices(IServiceCollection services
          {
               var connectionString =
"Server=localhost;Database=MoviesDB;User Id=sa;Password=Passw0rd!";
               services
                 .AddDbContext<MoviesDbContext>(o =>
                   o.UseSqlServer(connectionString));
          }
          public void Configure(IApplicationBuilder app,
               IHostingEnvironment env,
               MoviesDbContext moviesDbContext)
          {
               if (env.IsDevelopment())
               {
                    app.UseDeveloperExceptionPage();
               }
               app.UseStaticFiles();
               moviesDbContext.CreateSeedData();
               app.Run(async (context) =>
               {
                    await context
                           .Response
                           .WriteAsync("Hello World!");
               });
          }
     }
}

Для краткости строка подключения жестко запрограммирована, но в реальном приложении мы могли бы извлечь эту строку из зашифрованной записи в файле конфигурации или рассмотреть возможность сохранения этой информации в переменной среды.

Также обратите внимание, что в методе Configure() внедряется наш MovieDbContext, и с нашим методом расширения мы вызываем метод CreateSeedData() для генерации некоторых начальных данных, если они еще не существуют. Мы также звоним UseStaticFiles(). Этот компонент промежуточного программного обеспечения позволяет нашему приложению обслуживать статические ресурсы из папки wwwroot. Вы могли заметить, что эта папка была создана шаблоном проекта, но в настоящее время она пуста. В конце концов, мы будем добавлять наши клиентские ресурсы в эту папку.

На этом этапе ваше окно проводника должно выглядеть так:

Теперь, когда приложение запускается, ASP.NET Core вызовет метод Configure(), который, в свою очередь, создаст экземпляр нашего класса MoviesDbContext. Мы видим, что во время создания этого объекта выполняется вызов, чтобы убедиться, что база данных создана. Поставщик SQL Server создаст базу данных в соответствии со строкой подключения, указанной в методе ConfigureServices(). В нашей строке подключения указано, что база данных должна называться MoviesDB. Затем при вызове нашего метода расширения CreateSeedData() поставщик будет использовать определение класса сущности для создания таблицы. По соглашению имя таблицы будет Movies, так как наш класс сущности был назван Movie. Все это происходит, когда мы запускаем приложение, так что давайте попробуем.

Во-первых, убедитесь, что ваш контейнер SQL Server работает в Docker. Мы видели, как это сделать ранее. Затем запустите SQL Operations Studio и установите соединение с экземпляром SQL Server. Если вы развернете узел «Базы данных», вы должны увидеть только категорию «Системные базы данных».

Хорошо, теперь из Visual Studio Code, используя встроенный Терминал, используйте команду dotnet run. Вы должны увидеть следующий результат:

[~/Development/Projects/MoviesAPI] dotnet run
Using launch settings from /Users/nealpatel/Development/Projects/MoviesAPI/Properties/launchSettings.json...
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 2.1.0-rtm-30799 initialized 'MoviesDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (722ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      CREATE DATABASE [MoviesDB];
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (228ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
      IF SERVERPROPERTY('EngineEdition') <> 5
      BEGIN
          ALTER DATABASE [MoviesDB] SET READ_COMMITTED_SNAPSHOT ON;
      END;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (6ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE [Movies] (
          [Id] int NOT NULL IDENTITY,
          [Name] nvarchar(100) NOT NULL,
          [Year] int NOT NULL,
          CONSTRAINT [PK_Movies] PRIMARY KEY ([Id])
      );
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (6ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT CASE
          WHEN EXISTS (
              SELECT 1
              FROM [Movies] AS [m])
          THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
      END
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (4ms) [Parameters=[@p0='?' (Size = 100), @p1='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [Movies] ([Name], [Year])
      VALUES (@p0, @p1);
      SELECT [Id]
      FROM [Movies]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@p0='?' (Size = 100), @p1='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [Movies] ([Name], [Year])
      VALUES (@p0, @p1);
      SELECT [Id]
      FROM [Movies]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@p0='?' (Size = 100), @p1='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [Movies] ([Name], [Year])
      VALUES (@p0, @p1);
      SELECT [Id]
      FROM [Movies]
      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
Hosting environment: Development
Content root path: /Users/nealpatel/Development/Projects/MoviesAPI
Now listening on: https://localhost:5001
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

Это выглядит довольно хорошо, поскольку мы видим, что не только наша база данных была создана, но и исходные данные были помещены в базу данных. Используя SQL Operations Studio, обновите список баз данных, и вы должны увидеть новый MoviesDB !.

ASP.NET Core MVC

Теперь, когда у нас есть данные, давайте настроим приложение для использования шаблона MVC для маршрутизации входящих HTTP-запросов. В этом простом примере мы хотим создать действие контроллера, которое будет возвращать вызывающему список фильмов в нашей базе данных. По умолчанию мы будем использовать формат данных JSON.

Вверху класса Startup добавьте директиву using для ссылки на пространство имен Microsoft.AspNetCore.Mvc. Затем добавьте строку в ConfigureServices(), чтобы зарегистрировать службу MVC в платформе. Тело вашего метода должно выглядеть так:

public void ConfigureServices(IServiceCollection services)
{
     var connectionString = "Server=localhost;Database=MoviesDB;User Id=sa;Password=Passw0rd!";
     services.AddDbContext<MoviesDbContext>(o =>
          o.UseSqlServer(connectionString));
     services.AddMvcWithDefaultRoute();
}

Теперь, когда служба зарегистрирована, мы можем настроить промежуточное ПО для использования MVC для маршрутизации запросов к действию контроллера, которое мы создадим. Выполните вызов метода расширения UseMvc() IApplicationBuilder в теле метода Configure(). Тело вашего метода должно выглядеть так:

public void Configure(IApplicationBuilder app,
               IHostingEnvironment env,
               MoviesDbContext moviesDbContext)
{
     if (env.IsDevelopment())
     {
          app.UseDeveloperExceptionPage();
     }
     app.UseStaticFiles();
     moviesDbContext.CreateSeedData();
     app.UseMvc();
}

Чтобы завершить наш API, мы создадим новый класс контроллера, который будет обслуживать запросы данных фильма. Создайте новую папку с именем Controllers и в ней создайте новый класс MoviesController.

using Microsoft.AspNetCore.Mvc;
using MoviesAPI.Services;
namespace MoviesAPI.Controllers
{
     [Route("api/[controller]")]
     public class MoviesController : Controller
     {
          private MoviesDbContext _context;
          public MoviesController(MoviesDbContext context)
          {
               _context = context;
          }
          public IActionResult GetMovies()
          {
               return Ok(_context.Movies);
          }
     }
}

Этот простой класс использует маршрутизацию на основе атрибутов для маршрутизации запросов, соответствующих «/ api / movies», этому классу контроллера. Используя наш класс MoviesDbContext (внедренный в наш конструктор ядром ASP.NET), мы можем получить записи Movie из базы данных и вернуть их вызывающей стороне, заключив их в ответ с кодом состояния 200.

Мы могли бы использовать такой инструмент, как Postman, для отправки запроса GET к нашему API, но использование веб-браузера не менее хорошо. Запустите приложение и укажите в браузере http: // localhost: 5000 / api / movies, и вы увидите, что наш API работает!

На этом серверная часть нашей среды разработки завершена! Выполнив только эти шаги настройки, у вас есть технология сохранения данных (Microsoft SQL Server), ORM (Microsoft Entity Framework Core) и приложение RESTful API. На стороне клиента, где мы сейчас настроим современную среду веб-разработки.

Базовое представление ASP.NET MVC

Мы начнем с подготовки базовой HTML-страницы для рендеринга. Для этого создайте новый класс HomeController и поместите его в папку Controllers. Обратите внимание, что этот класс будет обрабатывать URL-запросы, соответствующие «/ home».

using Microsoft.AspNetCore.Mvc;
namespace MoviesAPI.Controllers
{
     [Route("/home")]
     public class HomeController : Controller
     {
          public IActionResult Index()
          {
               return View();
          }
     }
}

Затем мы создаем представление по умолчанию для этого действия контроллера. Создайте файл с именем Index.cshtml в / Views / Home /.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width" />
    <title>Movies API</title>
  </head>
  <body>
    <h1>Movies API</h1>
    <div id="container"></div>
  </body>
</html>

Теперь, если вы запустите приложение, запрос http: // localhost: 5000 приведет к просмотру HTML. Между тем, запрос / api / movies по-прежнему приведет к ответу в формате JSON.

wwwroot

Вы могли заметить, что когда мы создавали веб-приложение, в шаблоне проекта автоматически создавалась папка с именем wwwroot. Это будет корневая папка, из которой наше приложение будет обслуживать такие ресурсы, как таблицы стилей, скрипты, изображения и т. Д. Вы могли заметить, что в нашем файле представления Index.cshtml мы создали элемент div, который будет служить узлом, который мы будем смонтировать наш компонент React на.

Создайте папку с именем source в папке wwwroot. В этой исходной папке создадим минимальный файл app.js. Мы изменим наше представление, включив в него ссылку на скрипт app.js.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width" />
    <title>Movies API</title>
  </head>
  <body>
    <h1>Movies API</h1>
    <div id="container"></div>
    <script src="~/source/app.js"></script>
  </body>
</html>

Node.js и диспетчер пакетов узлов (npm)

До сих пор мы управляли зависимостями пакетов в проекте с помощью диспетчера пакетов NuGet. Это нормально, поскольку мы настраивали зависимости проекта от пакетов .NET Core, и NuGet хорошо для этого подходит. Однако, когда дело касается клиентского кода и особенно сложных JavaScript-фреймворков, таких как React и Angular, лучше использовать другой менеджер пакетов, который больше подходит для управления зависимостями на стороне клиента. В этой статье мы будем использовать диспетчер пакетов узлов (npm), но другие варианты включают Bower и Yarn.

npm устанавливается вместе с Node.js. Чтобы проверить, установлен ли у вас узел, введите node -v в командной строке. Чтобы проверить, установлен ли у вас npm, введите в командной строке npm -v. Если у вас их нет, скачайте и установите Node.js.

npm использует файл с именем package.json для хранения ссылок на пакеты, которые используются для проекта. Создайте файл с именем package.json в корневой папке приложения со следующим содержимым:

{
  "name": "moviesapi",
  "version": "1.0.0",
  "private": true,
  "devDependencies": {
  },
  "dependencies": {
  }
}

Webpack

Первая функция, которую мы хотим включить в процесс разработки на стороне клиента, - это сборщик. Webpack - популярный инструмент разработки, который берет написанные нами модули JavaScript, определяет их взаимозависимости и создает один или несколько пакетов. Например, если у нас есть 20 модулей JavaScript, некоторые из которых зависят от других, мы можем использовать Webpack для создания единого пакета, на который мы будем ссылаться из нашего файла HTML. В результате снижаются накладные расходы HTTP, поскольку из браузера выполняется только один запрос для всего кода JavaScript, и нам, как разработчикам, не нужно беспокоиться о порядке, в котором мы включаем наши сценарии, поскольку Webpack сортирует это для нас.

Чтобы установить веб-пакет, введите следующую команду:

npm install [email protected] --save-dev

Все, что нам нужно сделать оттуда, - это создать файл конфигурации, который указывает Webpack, что мы хотим связать, и где он должен разместить этот пакет. По умолчанию Webpack будет искать файл webpack.config.js для получения информации о конфигурации. Создайте файл с этим именем в корне приложения и дайте ему следующее содержимое:

const path = require('path');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
    path: path.resolve(__dirname, 'wwwroot/dist'),
    filename: 'bundle.js'
  }
};

Этот файл конфигурации найдет указанную нами точку входа (app.js), определит зависимости от других модулей транзитивно и создаст bundle.js в / wwwroot / dist /. Перед выполнением этого процесса нам нужно добавить скрипт в package.json.

{
  "name": "moviesapi",
  "version": "1.0.0",
  "private": true",
  "scripts": {
    "wbp": "webpack"
  },
  "devDependencies": {
  },
  "dependencies": {
  }
}

После этого вы можете просто запустить npm run wbp. Webpack будет использовать наш файл конфигурации и создать папку dist в wwwroot с файлом с именем bundle.js. Все, что нам нужно сделать, это обновить наш HTML, чтобы он ссылался на этот файл вместо app.js.

Вавилон

Хотя Webpack объединяет наши модули вместе, он не несет ответственности за то, чтобы браузеры, выполняющие объединенный код, могли интерпретировать этот код. Это означает, что если бы мы писали наши модули с использованием новых функций из ECMAScript 6 (ES6), Webpack мог бы без проблем объединить наши модули, но все равно браузеры, на которых работает наш код, смогут интерпретировать этот код. Некоторые старые браузеры не полностью поддерживают синтаксис ES6, поэтому это может стать проблемой, если мы будем использовать синтаксис ES6. Babel - это так называемый транспилятор, который использует синтаксис ECMAScript 6 (или новее) и преобразует этот код в код ES5, который могут интерпретировать старые браузеры. Переложив эту ответственность на Babel, мы можем писать код сценария, используя новейшие функции стандарта ECMAScript, и не беспокоиться о поддержке старых браузеров.

Для установки Babel мы используем поддержку «загрузчиков» в Webpack. Загрузчики применяют преобразования к исходному коду. Мы можем настроить Webpack для использования загрузчика Babel и применить его к файлам .js, которые объединяются. Введите следующую команду, которая, по сути, устанавливает 3 пакета Babel:

npm install --save-dev [email protected] @babel/core @babel/preset-env

После установки нам нужно изменить webpack.config.js для настройки загрузчика:

const path = require('path');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
    path: path.resolve(__dirname, 'wwwroot/dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { test: /\.js?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};

Реагировать

React - это библиотека JavaScript, которая позволяет нам создавать пользовательские интерфейсы на основе компонентов, используя только JavaScript. В стиле SPA наблюдается сильная тенденция к использованию React. Кстати, мотивация для написания этой статьи возникла после того, как я увлекся React. Когда я познакомился с React и начал строить сложные иерархии компонентов, я захотел связать все это вместе с моим опытом работы с Entity Framework Core и ASP.NET Core для создания приложений с полным стеком.

Давайте добавим React и ReactDOM в нашу среду. После того, как мы добавим их, мы напишем несколько простых компонентов React, чтобы убедиться, что все работает правильно.

Используя npm, мы можем выполнить следующую команду:

npm install react react-dom --save-dev

При написании компонентов React мы будем использовать синтаксис JSX. В этом нет необходимости, но это сокращает объем кода, который нам нужно писать, и делает вещи более удобочитаемыми. Это означает, что нам нужно будет использовать предустановку Babel для транспиляции кода JSX. Выполните следующую команду, чтобы установить предустановку:

npm install @babel/preset-react --save-dev

Как только предустановка будет установлена, мы можем добавить ее в конфигурацию нашего загрузчика веб-пакетов. Мы должны изменить тест так, чтобы загрузчик работал с файлами .jsx, а также включить новый пресет в массив пресетов. Изменения выделены полужирным шрифтом ниже:

const path = require('path');
module.exports = {
  entry: './wwwroot/source/app.js',
  output: {
    path: path.resolve(__dirname, 'wwwroot/dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react', '@babel/preset-env']
          }
        }
      }
    ]
  }
};

Имея React, мы собираемся создать два компонента. Первым компонентом будет простой компонент-контейнер под названием MovieList. По сути, это будет просто заголовок для списка фильмов, но он также является контейнером для нескольких экземпляров компонента Movie. Каждый компонент Movie будет отображать одну запись Movie из нашей базы данных.

В wwwroot / source создайте файл с именем movie.jsx. Это будет наш компонент Movie. Наш компонент Movie просто отобразит название и год фильма.

import React from 'react';
class Movie extends React.Component{
     render() {
          return (
               <div>{this.props.movieName} ({this.props.movieYear})</div>
          );
      }
};
export default Movie;

Теперь в wwwroot / source создайте файл с именем movieList.jsx. Он представляет собой компонент-контейнер, который будет извлекать данные из нашего API, сохранять их в состоянии и сопоставлять эти данные с коллекцией компонентов Movie.

import React from 'react';
import Movie from './movie.jsx'
class MovieList extends React.Component {
     constructor(props) {
          super(props);
          this.state = { data: [] };
     }
     loadMovies() {
          const xhr = new XMLHttpRequest();
          xhr.open('get', this.props.url, false);
          xhr.onload = () => {
               const data = JSON.parse(xhr.responseText);
               this.setState({ data: data });
          };
          xhr.send();
     }
     componentWillMount() {
          this.loadMovies();
     }
     render() {
          const movies = this.state.data.map(movie => (
          <Movie key={movie.id} movieName={movie.name} movieYear={movie.year} />
          ));
          return (
               <div>{movies}</div>
          );
     }
};
export default MovieList;

И, наконец, мы модифицируем наш файл app.js. Он будет использовать ReactDOM для монтирования компонента приложения в DOM.

import React from 'react';
import ReactDOM from 'react-dom';
import MovieList from './movieList.jsx';
class App extends React.Component {
     render() {
          return (
               <div>
                    <MovieList url="/api/movies" />
               </div>
          );
     }
}
ReactDOM.render(
     <App />,
     document.getElementById('container')
);

Нам нужно запустить наш сценарий wbp, npm run wbp, чтобы связать эти три модуля в bundle.js. Повторно запустите приложение и укажите в браузере http: // localhost: 5000, и вы должны увидеть, что наши компоненты React отображают список фильмов!

Замена Hotmodule

Чтобы завершить эту (длинную) статью, мы захотим включить замену Hotmodule. Благодаря этой функции мы сможем вносить изменения в наш код JavaScript, и нам не придется каждый раз повторно объединять код. Фактически, при правильной настройке вам даже не нужно перезагружать окно браузера, так как любые внесенные вами изменения будут отображаться автоматически.

Чтобы заставить это работать в нашей среде ASP.NET Core, нам нужно будет выполнить некоторую конфигурацию на сервере, а некоторую - на клиенте.

Начните с установки пакетов webpack-hot-middleware и aspnet-webpack:

npm install [email protected] --save-dev
npm install [email protected] --save-dev

Вернувшись к нашему классу Startup.cs, нам нужно настроить это промежуточное ПО. Мы модифицируем его как таковой (изменения выделены жирным шрифтом):

public void Configure(IApplicationBuilder app,
     IHostingEnvironment env,
     MoviesDbContext moviesDbContext)
{
     if (env.IsDevelopment())
     {
          app.UseDeveloperExceptionPage();
          var options = new WebpackDevMiddlewareOptions()
               {
                    HotModuleReplacement = true
               };
          app.UseWebpackDevMiddleware(options);
     }
     app.UseStaticFiles();
     moviesDbContext.CreateSeedData();
     app.UseMvcWithDefaultRoute();
}

Нам нужно будет изменить наш webpack.config.js, потому что для замены Hotmodule требуется «основная» точка входа и значение «publicPath» в настройках вывода (изменения выделены полужирным шрифтом):

const path = require('path');
module.exports = {
  entry: { 'main': './wwwroot/source/app.js' },
  output: {
    path: path.resolve(__dirname, 'wwwroot/dist'),
    filename: 'bundle.js',
    publicPath: 'dist/'
  },
  module: {
    rules: [
      { test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react', '@babel/preset-env']
          }
        }
      }
    ]
  }
};

Измените app.js, чтобы Hotmodule Replacement принимал обновления:

import React from 'react';
import ReactDOM from 'react-dom';
import MovieList from './movieList.jsx';
class App extends React.Component {
     render() {
          return (
               <div>
                    <MovieList url="/api/movies" />
               </div>
          );
     }
}
ReactDOM.render(
     <App />,
     document.getElementById('container')
);
module.hot.accept();

Вот и все! Перестройте пакет еще раз, остановите и запустите веб-приложение, и вы снова увидите список фильмов. Если вы внесете какие-либо изменения в JavaScript, он немедленно отобразится в DOM без необходимости обновления!

Заключение

Что ж, это было много шагов, и я надеюсь, что вы смогли их выполнить. Теперь вы настроены на расширение функциональности на стороне сервера с помощью пакетов NuGet или разработку на стороне клиента с помощью npm, чтобы специализироваться на инструментах и ​​технологиях, которые вам нравятся! Надеюсь, эта статья оказалась для вас полезной и вам понравится полноценная веб-разработка на основе .NET в Mac OS!