История о том, как подружить два отдельных проекта ASP.NET Core Web API и Angular 5, и заставить их работать, как одно целое.
Данная статья рассчитана на новичков, которые делают первые шаги в изучении Angular в связке с .NET Core.
Если вы используете Visual Studio для разработки, то наверное уже встречались с готовыми шаблонами проектов с подключенным Angular. Данные шаблоны позволяют в пару кликов создать приложение, которое уже имеет настроенный роутер и несколько готовых компонент. Вам не нужно тратить время на минимальную настройку рабочего приложения: вы уже имеете рабочий WebPack, отдельный модуль для общих компонент, настроенный роутер, подключенный Bootstrap. Возможно, вы подумаете: «Супер! Круто! Половина дела сделана!». Но на самом деле все немного сложнее…
Сложность и подводные камни заключаются в том, что у такого подхода и в стандартных шаблонах есть несколько существенных недостатков:
Итак, что мы будем использовать? Нам потребуются следующие вещи:
Теперь нам следует проверить, все ли установлено и готово к работе. Для этого откройте командную строку (терминал) и выполните подряд команды, перечисленные ниже:
В данной статье я буду выполнять все действия через командную строку и VS Code, так как он поддерживает .NET Core. Однако, если для вас предпочтительна Visual Studio 2017 для работы с .NET проектами, то можете смело создать и редактировать проект через нее.
Создаем корневую папку проекта Project, открываем ее в VS Code, запускаем терминал сочетанием клавиш Ctrl + ~ (тильда, буква ё). Пока ничего сложного:)
Теперь нам нужно создать проект. Для этого выполняем команду:
Проверяем все ли работает. Через терминал переходим в папку с только что созданным проектом, после выполняем команду:
Если на прошлом шаге все прошло успешно и в консоль было выведено Now listening on: localhost:5000, значит сервер успешно запущен. Перейдем по адресу localhost:5000/api/values (тестовый контроллер, который создается автоматически). Вы должны увидеть JSON с тестовыми данными.
Возвращаемся в VS Code и в терминале нажимаем Ctrl + C, чтобы остановить работу сервера.
Теперь создадим проект Angular. Для этого будем использовать команды Angular CLI, VS Code и встроенный терминал.
В терминале переходим в корневую папку нашего проекта Project и создаем новый проект с названием Project.Angular (придется немного подождать):
Перейдем в терминале в папку только что созданного проекта и запустим его:
Если на прошлом шаге все прошло успешно и в консоль было выведено NG Live Development Server is listening on localhost:4200, значит сервер успешно запущен. Перейдем по адресу localhost:4200. Вы должны увидеть тестовую страничку Angular.
Возвращаемся в VS Code и в терминале нажимаем Ctrl + C, вводим Y, чтобы остановить работу сервера.
Теперь нам нужно настроить две вещи: proxy.config.json для перенаправления запросов к серверу на нужный порт, и самое главное — настройка сборки в папку wwwroot.
Создаем в корне проекта Project.Angular файл с названием proxy.config.json и добавляем в него следующее содержимое:
Данная настройка указывает на то, что все запросы начинающиеся с /api/… будут попадать на localhost:5000/. То есть результирующим запросом будет localhost:5000/api/…
Укажем Angular, что в режиме разработки нам нужно использовать этот proxy.config. Для этого открываем файл package.json (который находится там же, в корне), находим команду scripts -> start и заменяем значение на:
В дальнейшем для запуска проекта Angular будем использовать команду npm start вместе ng serve. Команда npm start является сокращением для команды, которая указана у вас в package.json.
Последним шагом будет простая настройка сборки (по команде) проекта в папку wwwroot .NET Core Web API проекта. В открытом файле package.json находим команду scripts -> build и заменяем значение на следующее:
Для выполнения этого действия выполните в терминале команду npm run build. Результатом будет собранные файлы проекта в папке wwwroot.
Осталось научить серверную работать со статическими файлами и разрешать запросы с другого порта.
Открываем Startup.cs и добавляем в метод Configure строчки, позволяющие серверу обрабатывать статические файлы:
В Startup.cs, в метод Configure, добавляем строку, позволяющую серверу принимать запросы с порта 4200:
В методе ConfigureServices добавляем поддерку CORS:
В конечном итоге файл Startup.cs должен иметь содержимое, которое представлено ниже:
Готово! Теперь вы можете смело обращаться к вашим API контроллерам из Angular проекта. Также, вызвав команду npm run build для Angular проекта, у вас будет версия Web API приложения готовая для деплоя.
Это было краткое руководство по тому, что нужно сделать, чтобы иметь два отдельных проекта, и заставить их работать как одно целое.
Настройка CORS и автоматизация сборки даже далеко не претендует на продакшен версию. Однако, вы теперь знаете, куда смотреть и копать. Надеюсь моя статья окажется для кого-то полезной. Лично мне ее как раз и не хватало, когда я пытался наладить общение между этими двумя технологиями.
В данной статье я не осветил несколько моментов, связанных с маршрутизацией в Web API проекте, более гибкой настройкой CORS, автоматической сборкой и т.д. В планах написать более подробную статью, как собрать уже продакшен версию такого варианта приложения. Если вдруг у вас возникнут вопросы, то пишите в комментарии или на любой из контактов, указанных в профиле, я постараюсь вам помочь.
Вступление
Данная статья рассчитана на новичков, которые делают первые шаги в изучении Angular в связке с .NET Core.
Если вы используете Visual Studio для разработки, то наверное уже встречались с готовыми шаблонами проектов с подключенным Angular. Данные шаблоны позволяют в пару кликов создать приложение, которое уже имеет настроенный роутер и несколько готовых компонент. Вам не нужно тратить время на минимальную настройку рабочего приложения: вы уже имеете рабочий WebPack, отдельный модуль для общих компонент, настроенный роутер, подключенный Bootstrap. Возможно, вы подумаете: «Супер! Круто! Половина дела сделана!». Но на самом деле все немного сложнее…
Сложность и подводные камни заключаются в том, что у такого подхода и в стандартных шаблонах есть несколько существенных недостатков:
Различные best-practice советуют нам разделять приложение на два отдельных проекта, что в нашем случае — .NET Core Web API и Angular проекты. Основными преимуществами такого подхода будет следующее:
- Жесткая связь веб-интерфейса с серверной часть
- Сильно усложненная минимально рабочая версия приложения
- Отсутствие возможности использовать Angular CLI
- Лишние предустановленные пакеты
- Нарушение некоторых принципов из Angular Style Guide
При таком раскладе конечных сценария продакшена два:
- Два независимых друг от друга проекта, что позволит нам в дальнейшем реализовать альтернативный интерфейс, не трогая проект с серверной частью
- Суженный глобальный search scope, что позволяет эффективнее и проще производить поиск
- Абстрагированность от рабочего окружения, в котором разрабатывается серверная часть, Visual Studio например — мы можем использовать VS Code, Sublime Text, Atom или другой удобный для вас редактор
Моей задачей являлся как раз второй сценарий, так был он был более предпочтительным по экономическим соображениям. И вот, когда я пытался разобраться с тем, каким же все-таки образом подружить .NET Core Web API проект с Angular проектом, так, чтобы во время разработки у нас было два отдельных проекта, а в продакшене — всего один, а конкретно .NET Core веб-сайт, то я так и не смог найти полноценного руководства «с нуля до рабочего приложения». Пришлось собирать по кусочкам решения с англоговорящих форумов и блогов. Если у вас вдруг появилась такая же задача, то достаточно будет прочитать мою статью.
- Вы хостите веб-интерфейс на одном адресе, а сервер — на другом
- Либо собираете магическим образом проекты в один и хостите только его
Поехали!
Итак, что мы будем использовать? Нам потребуются следующие вещи:
Если у вас уже установлена Visual Studio 2017 и при установке вы выбирали .NET Core Development, то .NET Core SDK у вас уже есть и устанавливать его не нужно. Однако Node.js отдельно придется установить даже если был выбран Node.js Development. Npm установится вместе с Node.js. Angular CLI устанавливается глобально из командной строки через npm (инструкция есть по ссылке выше).
- .NET Core SDK — 2.0 версии или выше
- Node.js — 8.9.0 версии или выше
- npm — 5.5.0 версии или выше
- Angular CLI — 1.6.5 версии или выше
- Visual Studio Code
Теперь нам следует проверить, все ли установлено и готово к работе. Для этого откройте командную строку (терминал) и выполните подряд команды, перечисленные ниже:
dotnet --version #Версия .NET Core
node --version #Версия Node.js
npm --version #Версия npm
ng --version #Версия Angular CLI
Результат командной строки (версии могут отличаться, ничего страшного) 

Создаем проект .NET Core Web API
В данной статье я буду выполнять все действия через командную строку и VS Code, так как он поддерживает .NET Core. Однако, если для вас предпочтительна Visual Studio 2017 для работы с .NET проектами, то можете смело создать и редактировать проект через нее.
Шаг первый
Создаем корневую папку проекта Project, открываем ее в VS Code, запускаем терминал сочетанием клавиш Ctrl + ~ (тильда, буква ё). Пока ничего сложного:)
Окно VS Code и запущенный терминал 

Шаг второй
Теперь нам нужно создать проект. Для этого выполняем команду:
dotnet new webapi -n Project.WebApi
Окно VS Code с открытым проектом и запущенный терминал 

Шаг третий
Проверяем все ли работает. Через терминал переходим в папку с только что созданным проектом, после выполняем команду:
dotnet run
Окно VS Code с открытым проектом и запущенный терминал 

Шаг четвертый
Если на прошлом шаге все прошло успешно и в консоль было выведено Now listening on: localhost:5000, значит сервер успешно запущен. Перейдем по адресу localhost:5000/api/values (тестовый контроллер, который создается автоматически). Вы должны увидеть JSON с тестовыми данными.
Результат в браузере 

Шаг пятый
Возвращаемся в VS Code и в терминале нажимаем Ctrl + C, чтобы остановить работу сервера.
Окно VS Code с открытым проектом и запущенный терминал 

Создаем проект Angular
Теперь создадим проект Angular. Для этого будем использовать команды Angular CLI, VS Code и встроенный терминал.
Шаг первый
В терминале переходим в корневую папку нашего проекта Project и создаем новый проект с названием Project.Angular (придется немного подождать):
cd ..\
ng new Project.Angular
Окно VS Code с открытым проектом и запущенный терминал 

Шаг второй
Перейдем в терминале в папку только что созданного проекта и запустим его:
cd ./Project.Angular
ng serve --open
Окно VS Code с открытым проектом и запущенный терминал 

Шаг третий
Если на прошлом шаге все прошло успешно и в консоль было выведено NG Live Development Server is listening on localhost:4200, значит сервер успешно запущен. Перейдем по адресу localhost:4200. Вы должны увидеть тестовую страничку Angular.
Результат в браузере 

Шаг четвертый
Возвращаемся в VS Code и в терминале нажимаем Ctrl + C, вводим Y, чтобы остановить работу сервера.
Окно VS Code с открытым проектом и запущенный терминал 

Настраиваем проект Angular
Теперь нам нужно настроить две вещи: proxy.config.json для перенаправления запросов к серверу на нужный порт, и самое главное — настройка сборки в папку wwwroot.
Шаг первый
Создаем в корне проекта Project.Angular файл с названием proxy.config.json и добавляем в него следующее содержимое:
{
"/api/*": {
"target": "http://localhost:5000/",
"secure": false,
"logLevel": "debug"
}
}
proxy.config.json
{
"/api/*": {
"target": "http://localhost:5000/",
"secure": false,
"logLevel": "debug"
}
}
Данная настройка указывает на то, что все запросы начинающиеся с /api/… будут попадать на localhost:5000/. То есть результирующим запросом будет localhost:5000/api/…
Шаг второй
Укажем Angular, что в режиме разработки нам нужно использовать этот proxy.config. Для этого открываем файл package.json (который находится там же, в корне), находим команду scripts -> start и заменяем значение на:
{
...
scripts: {
...
"start": "ng serve --proxy-config proxy.config.json",
}
}
package.json
{
{
"name": "project.angular",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.config.json",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
"devDependencies": {
"@angular/cli": "1.6.7",
"@angular/compiler-cli": "^5.2.0",
"@angular/language-service": "^5.2.0",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.5.3"
}
}
}
В дальнейшем для запуска проекта Angular будем использовать команду npm start вместе ng serve. Команда npm start является сокращением для команды, которая указана у вас в package.json.
Шаг третий
Последним шагом будет простая настройка сборки (по команде) проекта в папку wwwroot .NET Core Web API проекта. В открытом файле package.json находим команду scripts -> build и заменяем значение на следующее:
{
...
scripts: {
...
"build": "ng build --prod --output-path ../Project.WebApi/wwwroot",
}
}
package.json
{
{
"name": "project.angular",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.config.json",
"build": "ng build --prod --output-path ../Project.WebApi/wwwroot",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
"devDependencies": {
"@angular/cli": "1.6.7",
"@angular/compiler-cli": "^5.2.0",
"@angular/language-service": "^5.2.0",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.5.3"
}
}
}
Для выполнения этого действия выполните в терминале команду npm run build. Результатом будет собранные файлы проекта в папке wwwroot.
Настраиваем проект .NET Core Web API
Осталось научить серверную работать со статическими файлами и разрешать запросы с другого порта.
Шаг первый
Открываем Startup.cs и добавляем в метод Configure строчки, позволяющие серверу обрабатывать статические файлы:
app.UseDefaultFiles();
app.UseStaticFiles();
Метод Configure в Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
}
Шаг второй
В Startup.cs, в метод Configure, добавляем строку, позволяющую серверу принимать запросы с порта 4200:
app.UseCors(builder => builder.WithOrigins("http://localhost:4200"));
Метод Configure в Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCors(builder => builder.WithOrigins("http://localhost:4200"));
app.UseMvc();
}
Шаг третий
В методе ConfigureServices добавляем поддерку CORS:
services.AddCors();
Метод ConfigureServices в Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc();
}
В конечном итоге файл Startup.cs должен иметь содержимое, которое представлено ниже:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Project.WebApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddCors(); // <-- Добавили это
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles(); // <-- Это
app.UseStaticFiles(); // <-- Вот это
app.UseCors(builder => builder.WithOrigins("http://localhost:4200")); // <-- И вот так:)
app.UseMvc();
}
}
}
Готово! Теперь вы можете смело обращаться к вашим API контроллерам из Angular проекта. Также, вызвав команду npm run build для Angular проекта, у вас будет версия Web API приложения готовая для деплоя.
Заключение
Это было краткое руководство по тому, что нужно сделать, чтобы иметь два отдельных проекта, и заставить их работать как одно целое.
Настройка CORS и автоматизация сборки даже далеко не претендует на продакшен версию. Однако, вы теперь знаете, куда смотреть и копать. Надеюсь моя статья окажется для кого-то полезной. Лично мне ее как раз и не хватало, когда я пытался наладить общение между этими двумя технологиями.
В данной статье я не осветил несколько моментов, связанных с маршрутизацией в Web API проекте, более гибкой настройкой CORS, автоматической сборкой и т.д. В планах написать более подробную статью, как собрать уже продакшен версию такого варианта приложения. Если вдруг у вас возникнут вопросы, то пишите в комментарии или на любой из контактов, указанных в профиле, я постараюсь вам помочь.