網頁

Doxygen程式碼文件產生器

Doxygen程式碼文件產生器

API Help

有個需求,需要提供API文件給其他團隊。評估線上與離線方案:

線上式:

  • Postman:適合團隊協同合作。但團隊外不是很合適。
  • Swagger:需額外在專案裡開Endpoint與還會造成額外的(安全)維護成本。

離線式:

  • DocFX
  • Sandcastle

都有在考慮範圍,但試過之後效果不理想。

doxygen

後來發現一套doxygen,除了GUI使用方便之外,另外還能透過指令(doxygen.exe)很方便整合進CI/CD的環境。

以下用我常用的 Northwind - ProductsController 為例來設定 doxygen,並透過 GUI 與 doxygen.exe 來產生API文件(前提是好好寫註解)。

注意一下,<example><code>~~~</code></example>這樣的階層才能產生正確效果。也就是,以下範例除了POST,其他的<example/>不會有效果。
其他XML註解的效果,可能還要多測試。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using northwindSample.Models;

namespace northwindSample.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly NorthwindContext _context;

        public ProductsController(NorthwindContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 取得所有產品資料
        /// </summary>
        /// <returns>所有產品清單</returns>
        /// <example>
        /// GET /api/Products
        /// </example>
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
        {
            if (_context.Products == null)
            {
                return NotFound();
            }
            return await _context.Products.ToListAsync();
        }

        /// <summary>
        /// 取得某一筆產品資料
        /// </summary>
        /// <param name="id">產品編號</param>
        /// <returns>單一產品資料</returns>
        /// <example>
        /// GET /api/products/{id}
        /// </example>
        [HttpGet("{id}")]
        public async Task<ActionResult<Product>> GetProduct(int id)
        {
            if (_context.Products == null)
            {
                return NotFound();
            }
            var product = await _context.Products.FindAsync(id);

            if (product == null)
            {
                return NotFound();
            }

            return product;
        }

        /// <summary>
        /// 更新指定的產品資料
        /// </summary>
        /// <param name="id">產品編號</param>
        /// <param name="product">Product物件</param>
        /// <returns>HTTP狀態碼</returns>
        /// <remarks>
        /// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        /// </remarks>
        /// <example>
        /// PUT /api/products/{id}
        ///
        /// {
        ///     Product Object
        /// }
        /// </example>
        [HttpPut("{id}")]
        public async Task<IActionResult> PutProduct(int id, Product product)
        {
            if (id != product.ProductId)
            {
                return BadRequest();
            }

            _context.Entry(product).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        /// <summary>
        /// 新增一筆產品資料
        /// </summary>
        /// <param name="product">Product物件</param>
        /// <returns>HTTP狀態碼</returns>
        /// <remarks>
        /// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        /// <example>
        /// <code>
        /// POST /api/products
        ///
        /// {
        ///     Product Object
        /// }
        /// </code>
        /// </example>
        /// </remarks>
        /// <example>
        /// <code>
        /// POST /api/products
        ///
        /// {
        ///     Product Object
        /// }
        /// </code>
        /// </example>
        [HttpPost]
        public async Task<ActionResult<Product>> PostProduct(Product product)
        {
            if (_context.Products == null)
            {
                return Problem("Entity set 'NorthwindContext.Products'  is null.");
            }
            _context.Products.Add(product);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetProduct", new { id = product.ProductId }, product);
        }

        /// <summary>
        /// 刪除指定的產品資料
        /// </summary>
        /// <param name="id">產品編號</param>
        /// <returns>HTTP狀態碼</returns>
        /// <example>
        /// DELETE /api/products/{id}
        /// </example>
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            if (_context.Products == null)
            {
                return NotFound();
            }
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            _context.Products.Remove(product);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool ProductExists(int id)
        {
            return (_context.Products?.Any(e => e.ProductId == id)).GetValueOrDefault();
        }
    }
}

開啟Doxygen GUI,這個Doxygen GUI是幫你產生doxyfile組態檔,底層也是透過doxygen.exe來執行。

Wizard 頁籤

Doxygen_GUI_Wizard_Project

Project是填入最重要的幾項資料:

  • working directory:類似暫存目錄
  • Project name:專案名稱
  • Project version: 文件版本
  • Source Code: 原始碼目錄。
    • San recursively:如果沒勾選,只會Scan當下目錄裡的.cs檔案。
  • Destination directory: 文件輸出目錄
Doxygen_GUI_Wizard_Mode

選擇模式

  • extraction mode: Documented entities only(預設) / All Entities
    • 測試專案太小,試不太出來兩者的差異。
  • Select programming language: Optimize for Java or C# output
Doxygen_GUI_Wizard_Output

輸出。我們只需要HTML,因此只保留HTML選項。

Doxygen_GUI_Wizard_Diagrams

類別圖。保留預設值。

Expert

Doxygen_GUI_Expert
  • OUTPUT_LANGUAGE 可調整輸出UI的語系。
  • FULL_PATH_NAME 是否輸出檔案路徑。(可取消勾選,以免曝過過多資訊)

Run

點擊Run doxygen之後,點擊Show HTML output,查看產出文件的效果。

Doxygen_GUI_Run

組態檔

Doxygen GUI產生沒問題之後,就可以一直寫註解及測試效果。完成之後,可以把組態保留。

File --> Save as ... --> Doxyfile

下次開啟Doxygen GUI就能匯入Doxyfile來使用。

doxygen.exe

C:\Program Files\doxygen\bin

當我們有了組態檔之後,就能拿來透過指令碼方式來產生文件:

PS C:\> doxygen.exe .\Doxyfile
doxygen.exe

都沒問題之後,就能把 Doxyfile 加入版控,讓後續的CI/CD去做自動化產生文件的工作。

結論

doxygen幾個優點:

  • 設定簡單,入手容易。
  • 非依賴DLL,直接掃.cs檔案,文件產生快速。
  • 支援指令碼模式,讓CI/CD自動化產生與更新文件可行。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。