在AdonisJs裏面時間物件預設是使用luxon的DateTime,但使用上你會發現時間會跟現實差時區的八小時(小編人在台灣GM+8),解決辦法就是在環境變數上加上TZ變數(注意不是TimeZone),
2023年5月22日 星期一
AdonisJs時間戳怎麼老是早八小時
AdonisJs報錯Incorrect string value: '\xF0\xA6\x9F\x8C,\xE9...' for column 'table' at row 1
小編在進行文字儲存時,透過orm進行操作,最終指令如下
然後報錯:Incorrect string value:......
經過反覆測試發現問題在“𦟌”這個字上面,只要移除就沒事了。但想當然客戶不會接受這無理的需求,而且未來會不會有其他字無法存取不知道,所以只好再想辦法。
小編此時資料庫編碼為utf8mb4,理論上不應該呀,遵循chatGPT指示,嘗試把collation從utf8mb4_0900_ai_ci改成utf8mb4_unicode_520_ci,但問題還是沒有解決。
在苦無對策之際,我嘗試著把上面這句sql直接透過Navicat直接執行,竟然通過了!!所以問題並不在db,那就是在AdonisJs框架了,然後我想了一下,好像之前遇到要寫入表情符號時也遇過相同的錯誤訊息,最初db連線資訊預設是utf8mb3,那這樣似乎就說得通了,所以把config/database.ts的連線資訊加入charset,把它改成跟db一樣utf8mb4即可
2023年4月27日 星期四
AdonisJs自動注入2
前言
上之介紹過adonisjs/fold的inject,讀過inject的源碼可以知道inject會把宣告的class直接注入進目前class的inject內的instance,而且是static,然後底層透過Route呼叫controller在把instance一一實現出來。那這樣問題來了,如果不是透過Route想要實現自動注入該如何處理。
例如Seeder中偶爾會寫一些腳本,但最好的方法是呼叫service執行相對應的function讓程式走已經經過測試的流程,才不會出現漏改或資料不完整等資料問題。那seeder並非透過route來呼叫,所以無法使用inject,在此我開發了一個套件供大家簡單使用
套件是開源的,源碼也很簡單,下面給大家展示一下。這也不限於只能使用在adonisjs上,甚至在其他專案也可以使用
後記
有些人喜歡用抽象或介面去管理程式碼,所以設計成參數可以設定要注入什麼,不輸入則自動注入類別,增加一點彈性。
5/23更新
我發現上面的源碼無法把注入的service,建構子裡的物件一並注入,簡單說他就是只有一層,如果要用到第二層的東西就掛了,所以我改用遞回方式去注入物件,下載到舊版的小夥伴記得去更版
2023年4月20日 星期四
AdonisJs踩雷第二發Computed
故事是這樣的,我Model有一個欄位用來儲存json字串,然後訂了一個computed欄位自動分析json,所以我很直覺地把分析完的結果拿來修改,再轉為json字串存回去,結果搞了半天內容卻一點變化也沒有。
殊不知computed是不能修改的,底層大概率是用Object.freeze之類的把物件凍結了吧。之後有空再來研究,今天先做個紀錄。
AdonisJs踩雷的一天, Migration completed, but unable to release database lock...
如果在Migration時遇到如標題所示的錯誤訊息,網路上找也只能找到這則相關的資訊,https://github.com/adonisjs/core/discussions/1897
只能說我看完當下完全看不懂他在說些什麼。
經過反覆的測試,才漸漸理解,原來發生的原因是query出錯,更正確的說法是程式報錯,至於錯在哪裡並不會告訴你,可能跟底層跑migration的方式有關,所以比較理想的解法是盡可能讓migration的sql簡單不要報錯,至於那些寫在defer裡面的query跟scripts,請抽離並改用seeder去觸發(這時就會看到錯在哪了,可以順便debug)是比較理想的做法。
另外在跑腳本可能會因為執行時間太長而中止,所以請減少sql的數量,盡可能一次取回所有資料,能把update delete insert塞進一個sql裡面就盡可能塞,減少使用關聯,才能確保腳本跑得完
2023年3月14日 星期二
AdonisJs第九天Provider
前一篇有稍微提到Provider,這個功能,今天在稍微深入講一下。
Provider可以為 AdonisJS 應用程式提供許多不同的功能。AdonisJS 也提供了許多內置的 Provider,同時也允許開發人員創建自己的 Provider。
Provider需要實現 register ready boot shutdown四個生命週期。register 方法用於註冊 container,而 boot 方法則用於container已準備好的情況,ready shutdown上面都有各自的註解,目前小編也沒有什麼使用範例及情境就先暫時忽略 。
以下是一個簡單的 Provider 範例,在預設情況下,在創立專案的同時會附有/provider/AppProvider.ts檔案在專案中以利擴充:
在上面這個範例中我註冊了一個全域的變數,並把這個變數擴充了一個新的方法(transaction),另外又偵聽一個事件,只要有db操作,就會把sql打印出來。
這邊有一個值得注意的點是,在AppProvider觸發的時候,有些套件世上未初始化的,所以無法透過import方式引入,變得需要在boot時,用await import或require去引入套件,這是一個小雷。
在設置全域變數,ts當然也要做出相對應的設定,才不會在使用時看到一堆紅線,找到/global.d.ts的檔案(沒有責自己創建),輸入以下
當然小編不建議隨便把東西設為全域變數,這只是紀錄一下之前年少做過不懂事,踩過的坑。
最後還需檢查/.adonisrc.json裏面providers是否已經註冊了剛剛所編寫的AppProvider
當這一切準備好就可以進到使用的環節了
這樣就大功告成拉。
2023年3月12日 星期日
AdonisJs第八天自動注入
依賴注入可以說是laravel裡非常重要的特性,實踐出來的機制稱之為容器,或是ioc,adonisjs也有實踐這個功能,然而adonisjs實踐的可不只有容器,同時還有自動注入。只是上面兩個功能,在官網上幾乎都是一筆帶過,甚至自動注入官方網站根本就沒有提到,要不是去看4版的文件,不然就是去參考laravel的文件,再不就是去問狗了。今天再就此作一個紀錄。
adonisjs的container被包在Application這個全域變數當中,可以隨時調用
官方做法是建議在AppProvider.ts裡,將所有會用到的物件都加以宣告,要使用時就可以直接呼叫相對應的key
這個好處是降低物件之間的依賴,同時減少new複雜物件的重工。這網路都很多教學我就不再贅述,有興趣可以自行去查關鍵字"依賴注入"或"容器"。
然而每個要使用的物件service, repository, tools全部都要註冊這會不會太麻煩了一點。這會導致AppProvider.ts過於龐大,一日小編在網路查找資訊時看到了曙光。
adonisjs使用了ts的decorator,撰寫了inject方法,使用方式很簡單,源碼也不長,可以使用在class或method上,參數可打給不打(小編用到目前還沒給過),使用在class上就會把建構子上宣告的變數,依照類別自動注入,非常快速及方便。唯一要注意的是,被注入的物件上層,建議也使用inject decorator,不然宣告起來會很費事,範利如下
這還是只有兩層的情況下,如果TestRepository的建構子也有參數,那你還是乖乖使用inject完事吧。
只要container跟inject,基本上可以說是想用什麼就自動注入什麼,相依性可以說大大降低。可以說在專案上幾乎沒有container出馬的份,inject就可以完成所有工作。但container當然還是有他的市場,adonisjs改寫過轉譯器,我們常常在套件中看到@ios:開頭的套件,例如
import Application from '@ioc:Adonis/Core/Application'
這當然不是這個套件真實的名稱跟路徑,是因為轉譯器的效果。只要發現該抬頭的字串,轉譯器就會自動去尋找container所註冊過的同名物件,我們舉昨天class-validator為例子
轉譯器讓import也可以直接把container所註冊的所有物件,不用透過專案內的container也可以呼叫使用,這實在是非常聰明且方便的做法,也有降低耦合的效果。不過這缺點就是必須是npm套件,所以專案內還是用inject就好吧
3/25更新
我在某個教學網站上看到,其實可以不用上到npm,直接本地provider bind完就可以在專案中直接使用,至於有沒有成功那就留給大家有空去驗證了
參考網站:https://adocasts.com/lessons/service-providers-and-the-ioc-container
4/28更新
更多使用情境可以參考
https://dev.to/serjoagronovdev/how-to-inject-services-in-adonisjs-v5-constructor-method-2gge
2023年3月10日 星期五
AdonisJs第七天validate
參數驗證算是後端功能中非常重要的一環,可以過濾前端算計來的參數,也可以轉換成適當的類型。
AdonisJs中有內建validate,使用方式先依照規範把validator建立起來,寫出要接收的schema以及相對應的訊息,之後加以驗證即可,範例如下
這邊值得一題,驗證完會回傳驗證結果,並轉成相對應的物件,例如string, number, DateTime, File......因此不會接收前端傳回來多餘的內容。如果設計得宜,是非常好的保障。
ect. 如果messages沒有相對應的定義訊息,系統就會使用預設訊息拋出
schema的規範相當多元,也可以上訂傳進來是物件,陣列,或是把時間格式轉換成DateTime,又或者限制輸入的內容金桔現在陣列之中的enum
validate也可以搭配rules使用,rules也預設有非常多的規則,甚至可以自定義驗證規則
更多細節都可以上官網查詢,這邊就不一一贅述。
接下來要介紹一個套件,如果你使用過nest js那你一定對他撰寫驗證的檔案驗證新穎,我同樣抱持著adonis會不會也這麼剛好有類似的東西開始搜索,(先說adonis提供的所有工具已足夠應付幾乎90%專案會遇上的問題,但小編我們身為攻城屍要足夠貪婪,能好就絕對不要濫,能省時間能更方便就絕對要去追求!!),終於讓我找到adonis-class-validateor,不過這套有點小bug,在File的轉換上會報錯,經小編修正後為@lu7766lu7766/adonis-class-validateor,安裝後下指令node ace invoke @lu7766lu7766/adonis-class-validateor設置好相關配置,即可開始使用。一樣先上範例
使用class-validator有兩大好處。
第一個好處,邏輯較為集中,傳統設計分為schema,message兩大部分,一但參數多起來,在定義message時很常要滾輪往上看schema是如何設計,相當不便,使用class-validator可以一目瞭然。
第二個好處,因為小編習慣把所接收到的參數全部丟進service進行處理,在參數上類別用傳統的方式就必須一個一個重新定義,否則所有參數就會是any,無法發揮ts跟vscode的長處
如果使用class-validator就可以直接將參數類別定義為validator
簡單說就是善用ts的優勢,讓之後的操作上可以說會方便許多。
以上就是關於validate想要跟各位分享的內容。
3/25更新一下
3/26更新一下
name: string;
async: boolean;
allowUndefineds: boolean;
compiledOptions: Options;
}
3/26更新一下
2023年3月7日 星期二
AdonisJs第六天,全域取得context
在AdonisJs設計中,原本只有Cntroller Method可以取得context(簡寫ctx),但在某些特殊情境下需要取得ctx,且無法透過controller傳遞參數怎麼辦呢?
小編在官網上尋找答案只找到可以使用以下方法,但在實際運行卻什麼也取不到,程式碼如下:
import HttpContext from '@ioc:Adonis/Core/HttpContext'
class SomeService {
public async someOperation() {
const ctx = HttpContext.get()
}
}應該是缺少一點東西,小編把get改成getOrFail,讓他把錯誤印出來,錯誤訊息顯示
E_INVALID_ALS_ACCESS: HTTP context is not available. Set "useAsyncLocalStorage" to true inside "config/app.ts" file
所以應該是少了設定,只是這設定要加在哪裡,官方文件上只有文字描述要加設定,卻沒有告訴你怎麼做,加在哪。好在config/app.ts裡面設定也不多,一個一個嘗試後是加在
以下看個使用情境
基本上在user password改變的前提下都需要經過加密,但就是有某些特殊場景不用,但又懶得把邏輯拆出來到service見一次做一次,只好用這個訪法去完成。
另外還有一個解法,是當判斷
但這有一些問題,例如加密的方法如果改用不是argon lib加密,開頭就會變,或是他就是需要加密在加密。這些往往會變成bug,不過也是要看需求拉。以上就是這次簡短分享,也算做個筆記。