Angular 嵌入 redoc API 文件

原本是打算把 redoc-cli 產生出來的文件直接嵌進去的,可是這樣子在文件有變動時,就會又要再做一次產生、嵌入,這樣不太好。最好還是可以自動依據寫好的 OpenAPI specification 來自動產出,這才是比較好的作法。

第一個待解決的問題是怎麼把 OpenAPI specification JSON 放到 Angular 專案裡,並且可以讀出來使用。關於這個,我是找到 How To Read Local JSON Files In Angular 這篇文章,方法挺簡單的,就把 json 檔案丟到 src/assets 下,然後直接在程式裡用 import 。

// src/app/xxx/xxx.component.ts
import SampleJson from '../../assets/SampleJson.json’;

// 後續程式碼就直接引用 SampleJSON 即可。

但修改完,TypeScript compiler 會有錯誤訊息,這得要改 tsconfig.json 在 compilerOptions 裡加入 resolveJsonModule 跟 esModuleInterop

{ "compilerOptions": { "resolveJsonModule": true, "esModuleInterop": true } }

第二個問題是,angular template 裡不能直接寫 script tag,這個可以實作 AfterViewInit ,然後用 DOMElement 來動態插入 (參考來源:https://stackoverflow.com/questions/38088996/adding-script-tags-in-angular-component-template/43559644)。細節的說明,我就直接寫在下面程式的註解裡。

import {
  Component,
  OnInit,
  ViewEncapsulation,
  ElementRef,
  AfterViewInit } from '@angular/core’;
// (1) 剛剛前面提到的,引用 OpenAPI specification JSON
import APIJson from '../../assets/api.json';

@Component({
  selector: 'app-documentation',
  templateUrl: './documentation.component.html',
  styleUrls: ['./documentation.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class DocumentationComponent implements OnInit, AfterViewInit {  // (2) Component 要實作 AfterViewInit 這介面

  constructor(private elementRef: ElementRef) { }  // (3) 引入 ElementRef ,Angular 會幫忙作動態注入

  ngAfterViewInit(): void {
    // (4) 這裡就是在頁面載入後要作的事情
    this.insertSpec();
    this.insertScript();
  }

  insertScript(): void {
    // (5) 插入 script element,這裡要載入 redoc 的腳本
    const redocScript = document.createElement('script');
    redocScript.type = 'text/javascript';
    redocScript.src = 'https://unpkg.com/redoc@next/bundles/redoc.standalone.js’;  // (6) 這腳本的網址是閱讀 redoc-cli 的原始碼以後取得的。
    this.elementRef.nativeElement.appendChild(redocScript);

    // (7) 插入另外一個 script element,這裡主要是執行 redoc,讓 redoc 能解析 json 並顯示出文件。
    const initScript = document.createElement('script');
    initScript.type = 'text/javascript';
    initScript.textContent = `
    const __redoc_state=${JSON.stringify(APIJson)};
    var container = document.getElementById('redoc');
    Redoc.hydrate(__redoc_state, container);
    `;  // (8) 字串要允許多行,可以使用 backquote ` https://stackoverflow.com/questions/35225399/multiline-string
    this.elementRef.nativeElement.appendChild(initScript);
  }

  insertSpec(): void {
    // (9) 插入 element ,讓腳本知道要根據哪個 element 來作處理。
    let s = document.createElement('redoc');
    s.setAttribute('spec-url', 'assets/api.yml');
    this.elementRef.nativeElement.appendChild(s);
  }

  ngOnInit(): void {
  }
}

至此就大功告成啦。

P.S. redoc 在 1.x 時,是有支援 Angular 的,但到了 2.x ,就改用 react 了。

用 redoc 來顯示OpenAPI規格文件

之前有找過怎樣輸出 OpenAPI 規格為文件,有試過 swagger-codegen 先轉成 HTML 再轉 PDF,但其實效果不好,也一直沒找到滿意的。

今天再來找一次,找到 redoc,依照 github 網站上的說明來作,用 redoc 提供的 docker image 就可以跑起 API 文件的 Live 網站,效果挺好的。

docker run -p 8081:80 --rm -v $(pwd)/api.yml:/usr/share/nginx/html/api.yml -e SPEC_URL=api.yml redocly/redoc

網站上還有提到一個 redoc-cli,說明說可以 bundle 成一個靜態HTML文件。有 homebrew ,就想說 homebrew 會不會有這個 formula,找了以後,發現是沒有,只好認命的用 npm 來裝

npm install npx npm install redoc-cli 

裝 npx 是為了可以方便的呼叫 node module 裡的指令來用,這部份的使用說明可以參考這篇:Introducing npx: an npm package runner 。 裝好以後,就可以使用了:

npx redoc-cli bundle api.yml 

輸出的檔案固定是 redoc-static.html,用瀏覽器開啟就可以了。