2024/11/19

Auto-filling JWT Token in NestJS Swagger Documentation


import {
    DocumentBuilder,
    SwaggerModule as NestSwaggerModule
} from '@nestjs/swagger'

     // 建立Swagger的設定
        const config = new DocumentBuilder()
            .addSecurity('JWT', {
                type: 'http',
                scheme: 'bearer',
                bearerFormat: 'JWT',
                in: 'header'
            })
            .build()

        // 設定Swagger的路徑
        NestSwaggerModule.setup(path, app, document, {
            swaggerOptions: {
                // 永久儲存授權
                persistAuthorization: true,
                // 預設分類不展開
                docExpansion: 'none',
                onComplete: () => {
                    // 設定是否為登入請求
                    let isLoginRequest = false

                    // 取得fetch
                    const originalFetch = window.fetch

                    // 覆寫fetch
                    window.fetch = async function (...args) {
                        // 執行原本的fetch
                        const response = await originalFetch.apply(this, args)

                        try {
                            // 如果為SingIn Request
                            if (isLoginRequest) {
                                // clone response
                                const clone = response.clone()
                                // 取得Content-Type
                                const contentType =
                                    response.headers.get('content-type')

                                // 如果為JSON
                                if (contentType?.includes('application/json')) {
                                    // 取得JSON
                                    const data = await clone.json()

                                    // 檢查 token
                                    const token = data?.data?.token
                                    // 如果有token
                                    if (token) {
                                        // 設定 Token
                                        ;(window as any).ui.preauthorizeApiKey(
                                            'JWT',
                                            token
                                        )

                                        // 重置標記
                                        isLoginRequest = false
                                    }
                                }
                            }
                        } catch (error) {
                            console.error('Error processing response:', error)
                        }

                        return response
                    }

                    // 添加點擊事件監聽
                    ;(window as any).document.addEventListener(
                        'click',
                        (e: any) => {
                            if (
                                e.target.matches(
                                    '.btn.execute.opblock-control__btn'
                                )
                            ) {
                                // 取得URL的Query
                                const urlQuery = window.location.hash.slice(1)
                                // 如果URL的Query包含 _singIn_
                                if (urlQuery.includes('_singIn_')) {
                                    // 如果是登入請求
                                    isLoginRequest = true
                                }
                            }
                        }
                    )
                }
            },
            customSiteTitle: 'API Documentation'
        })

2024/11/16

Implement a Current Language Decorator in NestJS

再Nestjs要取得當前Request語系可以透過Decorator去取得
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
import { Request } from 'express'

/**
 * 取得語系
 * @param data {unknown}
 * @example
 * @Controller('users')
 * export class UserController {
 *   @Get('profile')
 *   getProfile(@CurrentUser() userId: number) {
 *     return { userId };
 *   }
 * }
 *
 * // 與其他guard結合使用
 * @Controller('users')
 * export class UserController {
 *   @UseGuards(JwtAuthGuard)
 *   @Get('profile')
 *   getProfile(@CurrentUser() userId: number) {
 *     return { userId };
 *   }
 * }
 */
export const Lang = createParamDecorator(
    /**
     * Factory
     * @param data {unknown}
     * @param ctx {ExecutionContext}
     * @return {string}
     */
    (data: unknown, ctx: ExecutionContext): string => {
        //取得Http上下文
        const httpContext = ctx.switchToHttp()
        // 取得Request
        const request: Request = httpContext.getRequest()
        // 回傳語系 找不到就預設台灣
        return (request?.headers?.lang as string) || 'zh-TW'
    }
)

NestJS Request LifeCycle

 NestJS 請求生命週期:詳細解說



NestJS 是一個流行的 Node.js 框架,它專注於模組化和可維護性的伺服器端邏輯管理。理解 NestJS 如何運作的關鍵之一,就是了解請求的生命週期。在本文中,我們將逐步介紹每個階段,詳細說明一個請求從進入到結束所經歷的過程。


### 請求進入 (Incoming Request)


每個生命週期的開始都是一個 **請求進入**。這是應用程式接收到 HTTP 請求的初始點。這一步標誌著客戶端互動(通常通過瀏覽器或 API 客戶端)進入 NestJS 應用程式,並觸發整個處理過程。


### 中介軟體 (Middleware)


在請求進入系統後,它會經過 **中介軟體** 的處理。中介軟體用於執行例如日誌記錄、請求驗證,或向請求物件附加額外屬性等任務。在 NestJS 中,中介軟體可以分為兩種類型:

Implementing Global HTTP Status 200 Response in NestJS

根據不同的 HTTP 方法,NestJS 會自動設置對應的 HTTP 狀態碼,但基於特定需求,我會另作考量和調整
import {
    CallHandler,
    ExecutionContext,
    Injectable,
    NestInterceptor
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { Response } from 'express'

/**
 * Response Interceptor
 * @class {ResponseInterceptor}
 * @implements {NestInterceptor}
 */
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
    /**
     * Intercept
     * @param context {ExecutionContext} - 執行上下文
     * @param next {CallHandler} - 呼叫處理程序
     * @return {Observable} 返回 Observable
     */
    intercept(context: ExecutionContext, next: CallHandler): Observable {
        // 取得 Http 上下文
        const contextHttp = context.switchToHttp()
        // 取得 Response 物件
        const response: Response = contextHttp.getResponse()
        // 設定成Http Status 200
        response.status(200)

        return next.handle()
    }
}
main.ts