Nest.Js將所有路由導向index(Angular)

前言

使用Nest.Js做後端;Angular做前端。最後Angular打包後放置於Nest.Js做為主頁面呈現,是一種滿容易出現的作法
但如果直接無腦的將所有路由導向首頁,就會發現連一開始做好的api都全部死掉了,因為都被導回首頁了XD
稍稍研究了一下,比較好的作法就是採用middleware了
但實際用時,卻發現api還是無法回傳

後來才發現原來Nest.Js middleware的exclude用法特別奇怪
而這部份在github上也有不少討論(見Middleware Exclude behaviour not working)
但原作者似乎認為這是express的特性,沒有打算調整
而官方文件說明也不甚清晰
最後整個討論也沒下文就結束了
於是只好自己嘗試實作這個需求

一、將前端打包,並放置指定路徑,設定Nest.Js靜態路徑

我打包後的Angular,放在/dist/frontend底下
即前、後端各自打包後,將前端的dist放到後端的/dist/frontend

打開Nest.Js的main.ts
在一開始宣告app的地方,create要加上泛型<NestExpressApplication>,指定為express
此時就可以使用useStaticAssets來指定靜態路徑

1
2
3
4
const app = await NestFactory.create<NestExpressApplication>(AppModule, {cors: true});

// 放置Angular build完後的路徑
app.useStaticAssets(path.join(`${process.cwd()}/dist/frontend`));

二、增加FrontendMiddleware,指定網址非/api/*,導回首頁

由於我在Nest.Js採用restful api,所有api路由固定/api/*。還算好處理
路由開頭只要非api的,一律導回首頁

1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class FrontendMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: () => void) {
if (req.originalUrl.indexOf('api') !== -1) {
next();
} else {
res.sendFile(`${process.cwd()}/dist/frontend/index.html`);
}
}
}

注意!
自動提示語法,會提示req.ip。但Nest.Js取出來是沒有值的!
要使用req.originalUrl才會得到路由

三、啟用middleware

打開app.module.ts,加入此FrontendMiddleware

1
2
3
4
5
6
7
8
9
10
11
12
13
@Module({
//…略…
})
export class AppModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(FrontendMiddleware).forRoutes(
{
path: '/**',
method: RequestMethod.ALL,
},
);
}
}

完成!

後記

目前這樣的寫法,若 Angular 有個 user 頁面,路由是/user
照理說,我將http://{域名或ip}/user貼給別人,是直接觸發後端middware,而導回首頁
必須要由首頁點擊指向user的連結,才會再走Angular自己的route來導向

但我這樣做完後,直接貼上http://{域名或ip}/user,居然就會直接進到前端我所期望的頁面!

雖然覺得有錯,但又不知道錯在哪邊。而目前的結果又是我所期望的方式@@…
所以就…將錯就錯用著來了!

如果有人知道原因的話,再煩請告知了XD

2020/7/15補充更新:
發現若在main.ts使用app.setGlobalPrefix('api');
在browser輸入子網址(如http://{域名或ip}/user)時會直接像是打api那樣,最後變成找不到網站
而前述作法,/api/是寫在每一controller裡的,也較保有各別修改的靈活性!

參考資料

Stack Overflow/How to redirect all routes to index.html (Angular) in nest.js?
github/req.url is always ‘/‘ in Middleware
Middleware Exclude behaviour not working