https://habr.com/ru/post/460109/- JavaScript
- Программирование
- Angular
- TypeScript
Если вы занимаетесь frontend разработкой, то наверняка вам знакома следующая мизансцена: сроки по проекту стремительно сжимаются, ваше руководство, или заказчик, а-то и оба вместе желают увидеть глазами работающее приложение прямо сейчас, пусть и с ненастоящими данными. При этом back, возможно, и есть, но именно api слой взаимодействия с front'ом отсутствует от слова совсем.
Итак, недавно я столкнулся с такой ситуацией, и я разрабатываю frontend под angular (сидящие вокруг на стульях люди вяло похлопали, кто-то понимающе кивнул).
Теперь попробую серьезно. С одной стороны ситуация нередкая, и решений может быть выбрано много.
В голову приходило несколько вариантов решения:
- Захардкодить данные на уровне компонент
- Захардкодить данные на уровне resolver сервисов, приколотить их к нужным роутам
- Захардкодить данные на уровне сервисов поставщиков данных
- Запилить api, и, согласно оговоренным контрактам, возвращать захардкоженные данные
Но любой из данных вариантов казался неизящным костылем, для каждого из которых находились весомые недостатки.
- Первый вариант отпал сразу — абсолютно неудобное непереиспользуемое решение, по мере развития проекта все придется переписывать.
- Данное решение могло иметь место, но опять таки структура проекта и логика работы компонент будет переписываться.
- Возможный вариант, можно даже возвращать заглушечные данные асинхронно, имитируя обращение к серверу, однако, как и в предыдущих вариантах решения проблемы, наши interceptor'ы (если они есть, а они есть) оставались бы не у дел, и выходит, что имитация работы с бэком становится неполной.
- Последний вариант казался весьма приемлемым, лишен проблем, которые есть у предыдущих вариантов, но писать хардкод в backend проект не хотелось просто из гигиенических соображений.
В итоге был выбран иной вариант: поднять отдельный web сервер, который возвращал бы данные согласно роутам и контрактам, и настроить отдельную конфигурацию сборки и исполнения angular. Благо и то и другое оказалось сделать нетрудно.
Для реализации mock сервера был выбран
express.
Давайте с него и начнем.
Выбираем место где мы хотим писать код для moсk сервера, например в директория mock-server рядом с проектом ng.
Далее нужно инициализировать проект и добавить пакет с express.
npm init
npm install --save express
Далее добавим код, который нам будет возвращать данные. Создаем файл index.js, берем код из первого попавшегося tutorial.
const express = require("express");
const app = express();
app.get("/url", (req, res, next) => {
res.json(["Tony", "Lisa", "Michael", "Ginger", "Food"]);
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
Запустим сервер
node index.js
Проверим с помощью postman:
Все супер, сервер работает. Теперь давайте настроим один из роутов, так как будто мы запрашиваем данные из настоящего api. Допустим, нам нужен список всех книг, наполним книгами файл books.json
[
{
"rn": 0,
"id": "0",
"name": "Jungle",
"type": 0,
"wells": 10042,
"default": false,
"hidden": false,
"author": "Admin"
},
{
"rn": 1,
"id": "1",
"name": "Main",
"type": 1,
"wells": 156,
"default": true,
"hidden": false,
"author": "User"
}
]
И обновим файл приложения:
const express = require("express");
const app = express();
app.get("/api/book/", (req, res, next) => {
const books = require('./books');
res.json(books);
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
И проверим:
Отлично.
Теперь приступим к приложению angular.
Добавим в файлы environments/environment*.ts конфигурацию, которая хранит адрес до бэка.
environment.ts:
export const environment = {
production: false,
backend: 'http://localhost:5000/'
}
environment.prod.ts
export const environment = {
production: true,
backend: 'http://localhost:5000/'
}
В нормальном режиме, и в проде и в режиме разработки мы будем искать .net core api на 5000 порту, что и описано выше. Далее опишем конфигурацию для временного бэка
environment.mock.ts
export const environment = {
production: false,
backend: 'http://localhost:3000/'
}
Здесь, как видно мы ищем api на 3000 порту, где мы будем запускать express.
Теперь нам нужен interceptor, который будет направлять обращения к бэку на правильный сервер, в зависимости от конфигурации.
@Injectable()
export class RequestInterceptor implements HttpInterceptor {
baseUrl: string;
constructor() {
this.baseUrl = environment.backend;
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(this.modifyRequest(req));
}
private modifyRequest = (req: HttpRequest<any>): HttpRequest<any> => {
if (req.url.startsWith('api/')) {
const url = this.baseUrl;
req = req.clone({
url: url + req.url
});
}
return req;
}
}
Осталось настроить новую конфигурацию сборки и запуска приложения для работы с mock сервером.
Для этого нам надо немного подправить angular.json.
В секции вашего проекта, architect/build/configurations добавим новую конфигурацию сборки mock, и опишем замену файлов environment для этой конфигурации. Также для для режима serve создадим конфигурацию mock и укажем нужный вариант сборки
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"your-project": {
/*****/
"architect": {
"build": {
/*****/
"configurations": {
"production": {
/*****/
},
"mock": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.mock.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-project:build"
},
"configurations": {
"production": {
"browserTarget": "your-project:build:production"
},
"mock": {
"browserTarget": "your-project:build:mock"
}
}
}
},
/*****/
}
}
}
Вот и все, теперь осталось запустить проект в нужной конфигурации
ng serve --configuration=mock
и проверить куда улетают обращения к бэку:
Все отлично.
На самом деле данная конструкция нам еще очень поможет, когда мы будем прикручивать к проекту интеграционные и e2e тесты. Об этом постараюсь написать в ближайшее время.