套件組合技經驗:JSON Server

前陣子因為在開發環境需要有個簡易又不會太粗糙的 mock server,所以快速用短短的時間把幾個套件組合串起來,成為基本可用的迷你 project,最主要還是拜豐富 NodeJS 的生態系所賜,很多東西都有現成可用幾乎不用多寫幾行程式碼,原則上就是可以推到開發環境。 一開始先找到最熱門的 json server 就是 typicode 的專案,跑起來基本上已經符合需求,所以從這邊開始來組合。 如果是用程式碼方式建立 json server 來做客製化,其實骨子裡就是 express web framework,所以用法都是一樣: const https = require("https"); const devcert = require("devcert"); const jsonServer = require("json-server"); const server = jsonServer.create(); server.use(jsonServer.defaults()); server.use(jsonServer.rewriter(require("./routes.json"))); server.use(jsonServer.router("api.json")); async function start() { let ssl = await devcert.certificateFor("api.dev"); https.createServer(ssl, server).listen(8443); } start(); 開發環境希望也是走 HTTPS 方式所以用到 devcert 掛了一個自簽的開發用憑證就可以跑 HTTPS 了,如上幾乎就是開箱即用的 server。 由於需要 db.json 檔裡面就是擺好被視為資料庫的 JSON 內容,然後依 request 的 HTTP method 進行資料的 CRUD,但也因為這個很棒的特性所以為了我自己的需求只好魔改原本程式變成 json-server-deformation。...

July 4, 2022 · Keanu Pang

試玩 Hummingbird 的 middleware

之前為了測試 Hummingbird 這個輕量 server framework 所以做了一個叫 LocalWeb 的 web project,主要是用來讓使用者在開發環境下方便地瀏覽與下載檔案;結果沒想到內建的 middleware 就幾乎把所有事情都搞定了。 當然還是要再加點工才可以方便使用,例如當我點進目錄時,希望可以一覽目錄下的檔案清單,所以就客製化做了個 middleware 掛上來,把目錄下的檔案展開成清單做成連結讓使用者可以下載,當使用者點到檔案時就直接串到 HBFileMiddleware 下載檔案這樣。 第一次嘗試實作 Hummingbird 的 middleware 所以花了點時間研究,目前實作版本可以參考這 DirectoryIndexMiddleware ,還好進入門檻不高就是,花最多時間反而是在刻呈現頁面的 html。 因為想呈現那種傳統 Apache web server 檢視目錄下的復古感,於是參考 HBResponseGenerator 這個 protocol,是用來產生 response 結果頁,基本上只要實作 func response(from request: HBRequest) 把刻好的 html 產生成 HBResponse 結果就可以。 成品像是這樣 DirectoryIndexPage 會在剛剛的 DirectoryIndexMiddleware 裡被用到回傳 response。 為了求快所以先不套用什麼 template engine,直接用 String 拼出來,之後有時間再研究看看 Stencil 還是 Leaf 哪個好用。 雛型差不多完成之後,想說要掛個 HTTPS 功能結果好像沒有那麼直覺。本來以為只要把 Configuration 裡的 tlsOptions 設定上去就好,然後就會被噴 warning 訊息: tlsOptions set in Configuration will not be applied to a BSD sockets server....

June 28, 2022 · Keanu Pang

建立 DynamoDB 的 Incremental ID 機制

需要在 DynamoDB 的某個 table 的 Hash Key 是用 auto increment 的方式儲存 id,不過如果只單靠一張 table 有點難完成這樣的機制,所以需要再生另一張 table 用來存放目前的值,讓原本的 table 可以去參考以取得新增後的數值回來儲存。 可以參考這篇:How to make a UUID in DynamoDB?,做法大概就是像這樣: Each time you want to generate a new id, you would do the following: Do a GetItem on NextIdTable to read the current value of Counter → curValue Do a PutItem on NextIdTable to set the value of Counter to curValue + 1. Make this a conditional > PutItem so that it will fail if the value of Counter has changed....

December 12, 2017 · Keanu Pang

Connect-Redis 套件更新 Session 過期時間的方式

在使用 Express 架設網站時,且因為 cluster 的考量,會將 Session cookie 的資訊往外拉,通常都是使用 Redis 來儲存使用者的 Session。在 Express 裡會透過 connect-redis 的套件來串接兩者,以及使用者登入的檢查就使用 passport 套件來幫我們完成。 在 Express 裡的設定結構大概會是這樣︰ let sessionMiddleware = session({ store: new RedisStore({ host: hostname, port: port, db: db * 1, ttl: 3600 }), resave: false, saveUninitialized: false }); app.middleware(‘session’, sessionMiddleware); 這時候的困難點會是當未登入的使用者可能需要 session cookie 的 maxAge 會比較短期,例如需要是處在 recaptcha 的驗證階段;直到使用者順利登入後,才需要更新 session cookie 的 maxAge 為合理的時間。也就是 Redis 的 TTL 設定是動態的方式調整。 參考這裡的 討論 的做法則會是固定將 TTL 設為 0,並在使用者登入後才更新 maxAge。所以 RedisStore 的設定將會是︰...

November 14, 2016 · Keanu Pang

自定 Retrofit 的 DELETE Annotation

一般使用 Jersey 或 RESTEasy 所建立的 JAX-RS Web Service,是允許 client 呼叫 DELETE method 時可以加上 Message Body,就跟 POST method 一樣我們可以取得 Body 裡的內容。 但 Client 如果是使用 Retrofit 來呼叫 Web Service 的 DELETE method 並且加上 Body annotation 夾帶資料過去時,卻會得到如下的訊息而被中止︰ Non-body HTTP method cannot contain @Body or @TypedOutput. 對 Retrofit 來說是照著 W3C 的標準去實作,於是這樣的行為就跟一般坊間的 http client 不太一樣。 最快的方法就是自定一個可以允許夾帶資料的 DELETE Annotation,讓 Retrofit 可以參照︰ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import retrofit.http.RestMethod; @Target(METHOD) @Retention(RUNTIME) @RestMethod(value = “DELETE”, hasBody = true) public @interface DELETE { String value(); } 然後在自己的 Retrofit Service 使用這個 DELETE annotation 就可以了︰...

May 4, 2016 · Keanu Pang