Citizen是一個基於MVC的Web應用程序框架,旨在有興趣快速構建快速,可擴展的網站的人,而不是在Node的膽量上挖掘或將搖擺不定的Jenga Tower拼湊在一起,由50個不同的包裝製成。
使用Citizen作為傳統服務器端網絡應用程序,模塊化單頁應用程序(SPA)或RESTFUL API的基礎。
從0.9.x到1.0.x的過渡發生了許多破裂的變化。請諮詢ChangElog以獲取逐項列表,並徹底查看此更新的文檔。
顯然,這比任何NPM/github readme所包含的內容都要多。我正在為該文檔的網站工作。
我在個人網站和OriginalTrilogy.com上使用公民。 OT.com在一個$ 30的雲託管計劃上處理一個公民實例的$ 30雲託管計劃,處理中等數量的流量(每月幾十萬次觀看次數),該應用程序/過程一次每次運行幾個月而不會崩潰。這很穩定。
這些命令將為您的Web應用程序創建一個新目錄,安裝Citizen,使用其腳手架實用程序來創建應用程序的骨架,然後啟動Web服務器:
$ mkdir myapp && cd myapp
$ npm install citizen
$ node node_modules/citizen/util/scaffold skeleton
$ node app/start.js如果一切順利,您將在控制台中看到Web服務器正在運行的確認。訪問瀏覽器中的http://127.0.0.1:3000,您會看到一個裸露的索引模板。
公民在其默認模板引擎中使用模板文字。您可以安裝合併,更新模板配置,並相應地修改默認視圖模板。
有關配置選項,請參見配置。有關更多公用事業,可以幫助您開始使用,請參閱公用事業。
app/
config/ // These files are all optional
citizen.json // Default config file
local.json // Examples of environment configs
qa.json
prod.json
controllers/
hooks/ // Application event hooks (optional)
application.js
request.js
response.js
session.js
routes/ // Public route controllers
index.js
helpers/ // Utility modules (optional)
models/ // Models (optional)
index.js
views/
error/ // Default error views
404.html
500.html
ENOENT.html
error.html
index.html // Default index view
start.js
logs/ // Log files
access.log
error.log
web/ // public static assets
進口公民並啟動您的應用程序:
// start.js
import citizen from 'citizen'
global . app = citizen
app . start ( )從終端運行:
$ node start.js您可以使用配置文件,啟動選項和/或自定義控制器配置配置公民應用程序。
配置目錄是可選的,並且包含以JSON格式的配置文件,該文件同時驅動公民和您的應用程序。您可以在此目錄中擁有多個公民配置文件,從而允許基於環境的不同配置。公民根據以下層次結構建立其配置:
host密鑰,如果找到一個鍵,則使用文件配置擴展了默認配置。host密鑰,它將尋找一個名為Citizen.json的文件並加載該配置。假設您想在本地開發環境中在端口8080上運行公民,並且您的應用程序將連接到本地數據庫。您可以使用以下內容創建一個名為local.json(或Dev.json,無論您想要的)的配置文件:
{
"host" : "My-MacBook-Pro.local" ,
"citizen" : {
"mode" : "development" ,
"http" : {
"port" : 8080
}
} ,
"db" : {
"server" : "localhost" , // app.config.db.server
"username" : "dbuser" , // app.config.db.username
"password" : "dbpassword" // app.config.db.password
}
}此配置僅在本地計算機上運行時僅擴展默認配置。使用此方法,您可以將多個配置文件從不同的環境提交到同一存儲庫。
數據庫設置將通過app.config.db在您的應用程序中的任何地方訪問。 citizen host保留用於框架;創建自己的節點以存儲您的自定義設置。
您可以通過app.start()在啟動時設置應用程序的配置。如果有配置文件,啟動配置將擴展配置文件。如果沒有配置文件,則啟動配置擴展了默認的公民配置。
// Start an HTTPS server with a PFX file
app . start ( {
citizen : {
http : {
enabled : false
} ,
https : {
enabled : true ,
pfx : '/absolute/path/to/site.pfx'
}
}
} ) 要在路由控制器級別設置自定義配置,請導出一個config對象(更多關於路由控制器和“路由控制器”部分中的操作)。
export const config = {
// The "controller" property sets a configuration for all actions in this controller
controller : {
contentTypes : [ 'application/json' ]
}
// The "submit" property is only for the submit() controller action
submit : {
form : {
maxPayloadSize : 1000000
}
}
} 以下表示公民的默認配置,該配置是通過您的配置擴展的:
{
host : '' ,
citizen : {
mode : process . env . NODE_ENV || 'production' ,
global : 'app' ,
http : {
enabled : true ,
hostname : '127.0.0.1' ,
port : 80
} ,
https : {
enabled : false ,
hostname : '127.0.0.1' ,
port : 443 ,
secureCookies : true
} ,
connectionQueue : null ,
templateEngine : 'templateLiterals' ,
compression : {
enabled : false ,
force : false ,
mimeTypes : [
'application/javascript' ,
'application/x-javascript' ,
'application/xml' ,
'application/xml+rss' ,
'image/svg+xml' ,
'text/css' ,
'text/html' ,
'text/javascript' ,
'text/plain' ,
'text/xml'
]
} ,
sessions : {
enabled : false ,
lifespan : 20 // minutes
} ,
layout : {
controller : '' ,
view : ''
} ,
contentTypes : [
'text/html' ,
'text/plain' ,
'application/json' ,
'application/javascript'
] ,
forms : {
enabled : true ,
maxPayloadSize : 524288 // 0.5MB
} ,
cache : {
application : {
enabled : true ,
lifespan : 15 , // minutes
resetOnAccess : true ,
encoding : 'utf-8' ,
synchronous : false
} ,
static : {
enabled : false ,
lifespan : 15 , // minutes
resetOnAccess : true
} ,
invalidUrlParams : 'warn' ,
control : { }
} ,
errors : 'capture' ,
logs : {
access : false , // performance-intensive, opt-in only
error : {
client : true , // 400 errors
server : true // 500 errors
} ,
debug : false ,
maxFileSize : 10000 ,
watcher : {
interval : 60000
}
} ,
development : {
debug : {
scope : {
config : true ,
context : true ,
cookie : true ,
form : true ,
payload : true ,
route : true ,
session : true ,
url : true ,
} ,
depth : 4 ,
showHidden : false ,
view : false
} ,
watcher : {
custom : [ ] ,
killSession : false ,
ignored : / (^|[/\]).. / // Ignore dotfiles
}
} ,
urlPath : '/' ,
directories : {
app : < appDirectory > ,
controllers : < appDirectory > + '/controllers',
helpers : < appDirectory > + '/helpers',
models : < appDirectory > + '/models',
views : < appDirectory > + '/views',
logs : new URL('../../../logs', import.meta.url).pathname
web : new URL('../../../web', import.meta.url).pathname
}
}
} 這是對公民的設置及其所做的事情的完整摘要。
啟動服務器時,除了公民的http和https配置選項外,還可以提供與Node的HTTP.Createserver()和https.createserver()相同的選項。
唯一的區別是您如何通過密鑰文件。正如您在上面的示例中看到的那樣,您將密鑰文件的文件路徑傳遞給公民。公民為您閱讀文件。
| 環境 | 類型 | 預設值 | 描述 |
|---|---|---|---|
host | 細繩 | '' | 要在不同環境中加載不同的配置文件,公民依靠服務器的主機名作為鍵。在啟動時,如果Citizen找到了與服務器主機名匹配的host密鑰的配置文件,它將選擇該配置文件。這不是與HTTP服務器hostname混淆(請參見下文)。 |
| 公民 | |||
mode | 細繩 | 首先檢查NODE_ENV ,否則production | 應用模式確定某些運行時行為。可能的值是production和development生產模式沉默控制台日誌。開發模式啟用詳細的控制台日誌,URL調試選項和熱模塊更換。 |
global | 細繩 | app | 在開始文件中初始化公民的公約將框架分配給全局變量。您將在整個文檔中看到的默認值是app 。如果要使用另一個名稱,則可以更改此設置。 |
contentTypes | 大批 | [ 'text/html', 'text/plain', 'application/json', 'application/javascript' ] | 根據客戶的Accept請求標題,每個請求的響應格式允許列表。在為單個路由控制器或操作配置可用格式時,必須提供整個可用格式的數組。 |
errors | 細繩 | capture | 當您的應用程序引發錯誤時,默認行為是讓公民嘗試從錯誤中恢復並保持應用程序運行的行為。設置此選項exit ,告訴公民記錄錯誤並退出該過程。 |
templateEngine | 細繩 | templateLiterals | 公民使用[模板文字](https://ddeveloper.mozilla.org/en-us/docs/web/javascript/reference/reference/template_literals)語法用於默認情況下的視圖呈現。可選地,您可以安裝合併並使用其支持的任何引擎(例如,安裝柄欄並將templateEngine設置為handlebars )。 |
urlPath | 細繩 | / | 表示通往您應用的URL路徑。如果您希望通過http://yoursite.com/my/app訪問您的應用程序,並且您不會使用另一台服務器作為代理請求的前端,則此設置應為/my/app (不要忘記領先的Slash)。路由器工作需要此設置。 |
| http | |||
enabled | 布爾 | true | 啟用HTTP服務器。 |
hostname | 細繩 | 127.0.0.1 | 可以通過HTTP訪問應用程序的主機名。您可以指定一個空字符串以在任何主機名中接受請求。 |
port | 數字 | 3000 | 公民HTTP服務器上的端口號聆聽請求。 |
| https | |||
enabled | 布爾 | false | 啟用HTTPS服務器。 |
hostname | 細繩 | 127.0.0.1 | 可以通過HTTPS訪問應用程序的主機名。默認值是本地主機,但是您可以指定一個空字符串以在任何主機名中接受請求。 |
port | 數字 | 443 | 公民HTTPS服務器上的端口號聆聽請求。 |
secureCookies | 布爾 | true | 默認情況下,HTTPS請求中設置的所有cookie都是安全的。將此選項設置為false以覆蓋該行為,使所有Cookie都不安全,並要求您在Cookie指令中手動設置secure選項。 |
connectionQueue | 整數 | null | 排隊的最大傳入請求數量。如果未指定,操作系統將確定隊列限制。 |
| 會議 | |||
enabled | 布爾 | false | 啟用用戶會話範圍,該範圍將每個訪問者分配一個唯一的ID,並允許您將與該ID關聯的數據存儲在應用程序服務器中。 |
lifespan | 正整數 | 20 | 如果啟用了會話,則此數字表示用戶會話的長度,以分鐘為單位。如果用戶在此時間不活躍的情況下,會議會自動過期。 |
| 佈局 | |||
controller | 細繩 | '' | 如果使用全局佈局控制器,則可以在此處指定該控制器的名稱,而不是在所有控制器中使用next指令。 |
view | 細繩 | '' | 默認情況下,佈局控制器將使用默認佈局視圖,但是您可以在此處指定其他視圖。使用沒有文件擴展名的文件名。 |
| 表格 | |||
enabled | 布爾 | true | 公民為簡單表格提供基本的有效負載處理。如果您希望使用單獨的表單軟件包,請將其設置為false 。 |
maxPayloadSize | 正整數 | 524288 | 最大表單有效載荷大小,字節。設置最大有效載荷大小,以防止您的服務器被表單輸入數據超載。 |
| 壓縮 | |||
enabled | 布爾 | false | 啟用GZIP和DEFLATE壓縮,以進行渲染的視圖和靜態資產。 |
force | 布爾或字符串 | false | 即使他們不報告接受壓縮格式,也強制GZIP或Deflate編碼。許多代理和防火牆都打破了確定GZIP支持的接受編碼標頭,並且由於所有現代客戶都支持GZIP,因此通常可以通過將其設置為gzip來安全,但您也可以強迫deflate 。 |
mimeTypes | 大批 | 請參閱上面的默認配置。 | 如果啟用壓縮,將壓縮的一系列MIME類型。有關默認列表,請參見上面的示例配置。如果要添加或刪除項目,則必須完整替換數組。 |
| 快取 | |||
control | 包含密鑰/值對的對象 | {} | 使用此設置為路由控制器和靜態資產設置高速緩存標頭。關鍵是資產的路徑名,值是高速緩存標頭。有關詳細信息,請參見客戶端緩存。 |
invalidUrlParams | 細繩 | warn | 路由緩存選項可以指定有效的URL參數,以防止不良URL被緩存,並且invalidUrlParams確定在遇到不良URL或丟棄客戶端錯誤時是否記錄警告。有關詳細信息,請參見緩存請求和控制器操作。 |
| cache.application | |||
enabled | 布爾 | true | 啟用通過cache.set()和cache.get()方法訪問的內存緩存。 |
lifespan | 數字 | 15 | 緩存的應用程序資產的時間長度在幾分鐘內保留在內存中。 |
resetOnAccess | 布爾 | true | 每當訪問緩存時,確定是否在緩存資產上重置緩存計時器。設置為false時,緩存的物品到達lifespan時到期。 |
encoding | 細繩 | utf-8 | 當您將文件路徑傳遞到CACHE.SET()時,編碼設置確定讀取文件時應使用哪些編碼。 |
synchronous | 布爾 | false | 當您將文件路徑傳遞到CACHE.SET()時,此設置確定是否應同步或異步讀取文件。默認情況下,文件讀取為異步。 |
| cache.static | |||
enabled | 布爾 | false | 服務靜態文件時,公民通常會從磁盤上讀取每個請求的文件。您可以通過將其設置為true來大大加快靜態文件的速度,該文件將文件緩衝器緩存在內存中。 |
lifespan | 數字 | 15 | 緩存的靜態資產的時間長度在幾分鐘內保留在記憶中。 |
resetOnAccess | 布爾 | true | 確定是否在訪問緩存時是否在緩存的靜態資產上重置緩存計時器。設置為false時,緩存的物品到達lifespan時到期。 |
| 紀錄 | |||
access | 布爾 | false | 啟用HTTP訪問日誌文件。默認情況下禁用,因為訪問日誌可以快速爆炸,理想情況下應由Web服務器處理。 |
debug | 布爾 | false | 啟用調試日誌文件。可用於調試生產問題,但詳細的內容(與開發模式下的控制台可以看到相同的日誌)。 |
maxFileSize | 數字 | 10000 | 以千字節確定日誌文件的最大文件大小。當達到限制時,日誌文件將重命名為時間戳,並創建新的日誌文件。 |
| logs.Error | |||
client | 布爾 | true | 啟用400級客戶端錯誤的記錄。 |
server | 布爾 | false | 啟用500級服務器/應用程序錯誤的記錄。 |
status | 布爾 | false | 控制狀態消息在生產模式下是否應登錄到控制台。 (開發模式始終登錄到控制台。) |
| logs.watcher | |||
interval | 數字 | 60000 | 對於不支持文件事件的操作系統,此計時器確定以毫秒之前的歸檔前進行更改的日誌文件的頻率。 |
| 發展 | |||
| development.debug | |||
scope | 目的 | 此設置確定在開發模式下的調試輸出中記錄了哪些範圍。默認情況下,啟用了所有範圍。 | |
depth | 正整數 | 3 | 當公民將對象傾倒在調試內容中時,它會使用Node的Util.Insproct進行檢查。此設置確定了檢查的深度,這意味著將檢查和顯示的節點數量。較大的數字意味著更深的檢查和較慢的性能。 |
view | 布爾 | false | 將其設置為true,將調試信息直接轉儲到HTML視圖中。 |
enableCache | 布爾 | false | 開發模式禁用緩存。將此設置更改為true ,以在開發模式下啟用緩存。 |
| Development.Watcher | |||
custom | 大批 | 您可以告訴公民的熱模塊更換以觀看自己的自定義模塊。此數組可以包含具有watch (應用程序目錄中模塊的相對目錄路徑)的對象,並assign (您分配這些模塊的變量)屬性。例子:[ { "watch": "/util", "assign": "app.util" } ] | |
Citizen使用Chokidar作為其文件觀察者,因此對日誌和開發模式的watcher選項也接受Chokidar允許的任何選項。
這些設置通過app.config.host和app.config.citizen公開公開。
該文檔假設您的全局應用變量名稱是app 。相應調整。
app.start() | 啟動公民Web應用程序服務器。 |
app.config | 您在啟動時提供的配置設置。公民的設置在app.config.citizen中。 |
app.controllersapp.modelsapp.views | 您不太可能需要直接訪問控制器和視圖,但是引用app.models而不是手動從公民內置的熱模塊更換中受益。 |
app.helpers | 將所有助手/實用程序模塊放在app/helpers/助手中。 |
app.cache.set()app.cache.get()app.cache.exists()app.cache.clear() | 公民內部使用的應用程序緩存和密鑰/值存儲,也可用於您的應用。 |
app.log() | 公民使用的基本控制台和文件記錄,用於您使用。 |
公民URL結構確定了要開火的路線控制器和動作,通過URL參數,並為SEO友好型內容提供了一些空間,可以將其作為唯一標識符加倍。結構看起來像這樣:
http://www.site.com/controller/seo-content/action/myAction/param/val/param2/val2
例如,假設您的網站的基本URL是:
http://www.cleverna.me
默認路由控制器是index ,默認操作是handler() ,因此上述等同於以下內容:
http://www.cleverna.me/index/action/handler
如果您有article路由控制器,則可以這樣要求:
http://www.cleverna.me/article
公民通過了由名稱/值對組成的URL參數,而不是查詢字符串。如果您必須通過237的文章ID和2頁數,則將名稱/值對附加到URL:
http://www.cleverna.me/article/id/237/page/2
有效的參數名稱可能包含字母,數字,下劃線和破折號,但必須以字母或下劃線開始。
默認控制器操作是handler() ,但是您可以使用action參數指定替代操作(稍後再詳細介紹):
http://www.cleverna.me/article/action/edit
公民還可以選擇將相關內容插入您的URL,例如:
http://www.cleverna.me/article/My-Clever-Article-Title/page/2
此SEO內容必須始終遵循控制器名稱,並在任何名稱/值對之前,包括控制器操作。您可以通過route.descriptor或在url範圍內(在這種情況下在url.article )中訪問它,這意味著您可以將其用作唯一標識符(有關路由控制器部分中的URL參數的更多信息)。
URL參數action和direct為框架保留,因此請勿將其用於應用程序。
公民依靠一個簡單的模型觀察器慣例。上面提到的文章模式可能使用以下結構:
app/
controllers/
routes/
article.js
models/
article.js // Optional, name it whatever you want
views/
article.html // The default view file name should match the controller name
給定URL需要至少一個路由控制器,並且路由控制器的默認視圖文件必須共享其名稱。模型是可選的。
給定路由控制器的所有視圖都可以在app/views/目錄中存在,也可以將其放置在其名稱與清潔組織控制器的名稱匹配的目錄中:
app/
controllers/
routes/
article.js
models/
article.js
views/
article/
article.html // The default view
edit.html // Alternate article views
delete.html
有關視圖部分中的視圖的更多信息。
模型和視圖是可選的,不一定需要與特定控制器相關聯。如果您的路由控制器要將其輸出傳遞給另一個控制器以進行進一步處理和最終渲染,則無需包括匹配視圖(請參閱Controller Next Next指令)。
公民路線控制器只是一個JavaScript模塊。每個路由控制器至少需要一個導出作為所請求路線的動作。默認操作應命名為handler() ,當URL中未指定任何訴訟時,公民稱其為公民。
// Default route controller action
export const handler = async ( params , request , response , context ) => {
// Do some stuff
return {
// Send content and directives to the server
}
} Citizen Server處理handler()處理初始請求並傳遞它4個參數:一個包含請求參數的params對象,Node.js request和response對像以及當前請求的上下文。
params對象的屬性config | 您的應用程序的配置,包括當前控制器操作的任何自定義 |
route | 所請求的路線的詳細信息,例如URL和路由控制器的名稱 |
url | 從URL得出的任何參數 |
form | 從帖子收集的數據 |
payload | 原始請求有效載荷 |
cookie | 隨著請求發送的餅乾 |
session | 會話變量,如果啟用了會話 |
除了可以在您的控制器中訪問這些對像外,它們還自動包含在視圖上下文中,因此您可以將它們在視圖模板中引用為本地變量(“視圖部分中的更多詳細信息)。
例如,基於上一篇文章URL ...
http://www.cleverna.me/article/My-Clever-Article-Title/id/237/page/2
...您將擁有以下params.url像傳遞給您的控制器:
{
article : 'My-Clever-Article-Title' ,
id : '237' ,
page : '2'
}控制器名稱成為引用描述符的URL範圍中的屬性,這使其非常適合用作唯一標識符。它也可以在params.route對像中作為params.route.descriptor提供。
context參數包含鏈中先前控制器生成的任何數據或指令使用其return語句。
要返回控制器訴訟的結果,請包括一個return聲明,其中包含要傳遞給公民的任何數據和指令。
使用上述URL參數,我可以從模型中檢索文章內容並將其傳遞回服務器:
// article controller
export const handler = async ( params ) => {
// Get the article
const article = await app . models . article . get ( {
article : params . url . article ,
page : params . url . page
} )
const author = await app . models . article . getAuthor ( {
author : article . author
} )
// Any data you want available to the view should be placed in the local directive
return {
local : {
article : article ,
author : author
}
}
}可以使用action URL參數請求替代操作。例如,也許我們希望採取不同的動作並查看來編輯文章:
// http://www.cleverna.me/article/My-Clever-Article-Title/id/237/page/2/action/edit
// article controller
export const handler = async ( params ) => {
// Get the article
const article = await app . models . article . get ( {
article : params . url . article ,
page : params . url . page
} )
const author = await app . models . article . getAuthor ( {
author : article . author
} )
// Return the article for view rendering using the local directive
return {
local : {
article : article ,
author : author
}
}
}
export const edit = async ( params ) => {
// Get the article
const article = await app . models . article . get ( {
article : params . url . article ,
page : params . url . page
} )
// Use the /views/article/edit.html view for this action
return {
local : {
article : article
} ,
view : 'edit'
}
}您將要在return聲明中傳遞給公民的所有數據。您要在視圖中呈現的所有數據都應傳遞給一個稱為local對象的公民,如上所示。可以將其他對像傳遞給公民,以設置為服務器提供指令的指令(請參閱控制器指令)。您甚至可以將自己的對象添加到上下文中,並將它們從控制器傳遞到控制器(更多的控制器鏈條部分。)。
模型是可選的模塊,它們的結構完全取決於您。公民不會直接與您的模型交談;為了您的方便,它僅將它們存儲在app.models中。如果願意,您也可以手動將它們導入控制器。
將以下函數放置在app/models/article.js中時,將通過app.models.article.get()在您的應用中訪問。
// app.models.article.get()
export const get = async ( id ) => {
let article = // do some stuff to retrieve the article from the db using the provided ID, then...
return article
}默認情況下,公民使用模板文字來查看渲染。您可以安裝consolidate.js並使用任何受支持的模板引擎。只需相應地更新templateEngine配置設置。
在article.html中,您可以引用將放置在路由控制器的返回語句中的local對像中的變量。公民還會自動將來自params對象的屬性從參數對象注入您的視圖上下文,因此您可以訪問這些對像作為局部變量(例如url範圍):
<!-- article.html -->
<!doctype html >
< html >
< body >
< main >
< h1 >
${local.article.title} — Page ${url.page}
</ h1 >
< h2 > ${local.author.name}, ${local.article.published} </ h2 >
< p >
${local.article.summary}
</ p >
< section >
${local.article.text}
</ section >
</ main >
</ body >
</ html > 默認情況下,服務器呈現其名稱匹配控制器的視圖。要呈現不同的視圖,請在返回語句中使用view指令。
所有視圖都進入/app/views 。如果控制器具有多個視圖,則可以在以該控制器命名的目錄中組織它們。
app/
controllers/
routes/
article.js
index.js
views/
article/
article.html // Default article controller view
edit.html
index.html // Default index controller view
您可以通過在請求中設置適當的HTTP Accept標頭來告訴路由控制器作為JSON或JSON-P返回其本地變量,讓相同的資源同時提供完整的HTML視圖,也可以使用AJAX請求和RESTFULE API的JSON。
文章路由控制器handler()操作將返回:
{
"article" : {
"title" : " My Clever Article Title " ,
"summary" : " Am I not terribly clever? " ,
"text" : " This is my article text. "
},
"author" : {
"name" : " John Smith " ,
"email" : " [email protected] "
}
}無論您在控制器的返回語句local對像中添加的任何內容都將被返回。
對於JSONP,請在URL中使用callback :
http://www.cleverna.me/article/My-Clever-Article-Title/callback/foo
返回:
foo ( {
"article" : {
"title" : "My Clever Article Title" ,
"summary" : "Am I not terribly clever?" ,
"text" : "This is my article text."
} ,
"author" : {
"name" : "John Smith" ,
"email" : "[email protected]"
}
} ) ;要強制給定請求的特定內容類型,請在路由控制器中設置response.contentType 。
export const handler = async ( params , request , response ) => {
// Every request will receive a JSON response regardless of the Accept header
response . contentType = 'application/json'
}您可以在事件鉤中的所有請求中強制全局響應類型。
助手是可選的實用程序模塊,它們的結構完全取決於您。為了您的方便,它們存儲在app.helpers中。如果願意,您也可以手動將它們導入控制器和模型。
將以下功能放置在app/helpers/validate.js中時,將通過app.helpers.validate.email()在您的應用中訪問:
// app.helpers.validate.email()
export const email = ( address ) => {
const emailRegex = new RegExp ( / [a-z0-9!##$%&''*+/=?^_`{|}~-]+(?:.[a-z0-9!##$%&''*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])? / i )
return emailRegex . test ( address )
} Citizen在app範圍中存儲所有模塊,不僅是為了輕鬆檢索,還可以支持熱模塊更換(HMR)。當您將更改保存到開發模式下的任何模塊或視圖時,公民會實時清除現有的模塊導入和重新導出。
您將看到一個記錄的控制台記錄,並記錄了受影響的文件,並且您的應用程序將繼續運行。無需重新啟動。
公民盡力在不退出過程的情況下優雅地處理錯誤。以下控制器操作將引發錯誤,但服務器將以500響應並繼續運行:
export const handler = async ( params ) => {
// app.models.article.foo() doesn't exist, so this action will throw an error
const foo = await app . models . article . foo ( params . url . article )
return {
local : foo
}
}您還可以手動投擲錯誤並自定義錯誤消息:
export const handler = async ( params ) => {
// Get the article
const article = await app . models . article . get ( {
article : params . url . article ,
page : params . url . page
} )
// If the article exists, return it
if ( article ) {
return {
local : {
article : article
}
}
// If the article doesn't exist, throw a 404
} else {
// Error messages default to the standard HTTP Status Code response, but you can customize them.
let err = new Error ( 'The requested article does not exist.' )
// The HTTP status code defaults to 500, but you can specify your own
err . statusCode = 404
throw err
}
}請注意, params.route.controller已從請求的控制器更新為error ,因此您的應用程序中的任何引用都應考慮到所請求的控制器。
以路線要求的格式返回錯誤。如果您請求JSON,並且該路線會引發錯誤,則公民將以JSON格式返回錯誤。
腳手架實用程序創建的應用程序骨架包括用於通用客戶端和服務器錯誤的可選錯誤視圖模板,但是您可以為任何HTTP錯誤代碼創建模板。
公民的默認錯誤處理方法是capture ,它嘗試優雅地恢復。如果您希望在錯誤後退出該過程,請更改config.citizen.errors exit 。
// config file: exit the process after an error
{
"citizen" : {
"errors" : "exit"
}
}申請錯誤處理程序發射後,公民將退出該過程。
要為服務器錯誤創建自定義錯誤視圖,請創建一個名稱為/app/views/error目錄,並使用以HTTP響應代碼或節點錯誤代碼命名的模板填充它。
app/
views/
error/
500.html // Displays any 500-level error
404.html // Displays 404 errors specifically
ENOENT.html // Displays bad file read operations
error.html // Displays any error without its own template
除了查看數據外,路由控制器操作的返回語句還可以將指令傳遞到呈現替代視圖,設置cookie和會話變量,啟動重定向,呼叫和渲染包括,緩存路由控制器操作/視圖(或整個請求),並將請求移交給另一個控制器以進行進一步處理。
默認情況下,服務器呈現其名稱匹配控制器的視圖。要呈現不同的視圖,請在返回語句中使用view指令:
// article controller
export const edit = async ( params ) => {
const article = await app . models . article . get ( {
article : params . url . article ,
page : params . url . page
} )
return {
local : article ,
// This tells the server to render app/views/article/edit.html
view : 'edit'
}
}您通過在控制器操作中返回cookie對象來設置cookie。
export const handler = async ( params ) => {
return {
cookie : {
// Cookie shorthand sets a cookie called username using the default cookie properties
username : params . form . username ,
// Sets a cookie called last_active that expires in 20 minutes
last_active : {
value : new Date ( ) . toISOString ( ) ,
expires : 20
}
}
}
}這是一個完整的cookie對象的默認設置的示例:
myCookie = {
value : 'myValue' ,
// Valid expiration options are:
// 'now' - deletes an existing cookie
// 'never' - current time plus 30 years, so effectively never
// 'session' - expires at the end of the browser session (default)
// [time in minutes] - expires this many minutes from now
expires : 'session' ,
path : '/' ,
// citizen's cookies are accessible via HTTP/HTTPS only by default. To access a
// cookie via JavaScript, set this to false.
httpOnly : true ,
// Cookies are insecure when set over HTTP and secure when set over HTTPS.
// You can override that behavior globally with the https.secureCookies setting
// in your config or on a case-by-case basis with this setting.
secure : false
}一旦在客戶端上設置了cookie,它們就可以在params.cookie中提供,而在視圖中只需cookie :
<!doctype html >
< html >
< body >
< section >
Welcome, ${cookie.username}.
</ section >
</ body >
</ html >您在控制器中設置的cookie變量在params.cookie範圍內沒有立即可用。公民必須從控制器中接收上下文,並首先將響應發送給客戶端,因此,如果您在相同的請求中需要訪問該變量的本地實例。
公民設定的所有餅乾都從ctzn_前綴開始,以避免發生衝突。不要使用ctzn_啟動您的cookie名稱,您應該沒有問題。
如果您使用代理後面的公民,例如Nginx或Apache,請確保您的服務器配置中有HTTP Forwarded標頭,以便公民處理安全cookies的操作正常。
這是您如何在nginx中設置此操作的示例:
location / {
proxy_set_header Forwarded "for=$remote_addr;host=$host;proto=$scheme;";
proxy_pass http://127.0.0.1:8080;
}
如果啟用了會話,則可以通過您的控制器中的params.session訪問會話變量,也可以簡單地在視圖中訪問會話session 。這些本地範圍引用當前用戶的會話,而無需傳遞會話ID。
默認情況下,會話具有四個屬性: id , started , expires和timer 。會話ID還以稱為ctzn_session_id的cookie發送給客戶端。
設置會話變量與設置cookie變量幾乎相同:
return {
session : {
username : 'Danny' ,
nickname : 'Doc'
}
}像cookie一樣,您剛剛分配的會話變量在params.session範圍內的相同請求中不可用,因此,如果您需要立即訪問此數據,請使用本地實例。
會話基於sessions.lifespan config屬性到期,該屬性表示會話的長度。默認值為20分鐘。 timer是用戶的每個請求重置的。 timer用完時,會話將刪除。在此之後的任何客戶端請求都將生成新的會話ID,並向客戶端發送新的會話ID cookie。
強行清除並過期當前用戶的會話:
return {
session : {
expires : 'now'
}
} 公民設置的所有會話變量從ctzn_前綴開始,以避免發生衝突。不要使用ctzn_啟動會話變量名稱,您應該沒有問題。
您可以將重定向指令傳遞到處理控制器操作後將啟動的服務器。
redirect對像在其速記版本中採用一個URL字符串,或三個選項: statusCode , url和refresh 。如果您不提供狀態代碼,則公民使用302(臨時重定向)。 refresh選項確定重定向是使用位置標頭還是非標準刷新標頭。
// Initiate a temporary redirect using the Location header
return {
redirect : '/login'
}
// Initiate a permanent redirect using the Refresh header, delaying the redirect by 5 seconds
return {
redirect : {
url : '/new-url' ,
statusCode : 301 ,
refresh : 5
}
}與位置標頭不同,如果您使用refresh選項,則公民會向客戶發送渲染視圖,因為重定向是客戶端。
使用位置標頭斷路(我認為)引用器標頭,因為引用器最終不是啟動重定向的資源,而是啟動該頁面之前的資源。為了解決此問題,Citizen存儲一個稱為ctzn_referer的會話變量,其中包含啟動重定向的資源的URL,您可以使用該網址正確地重定向用戶。例如,如果未經驗證的用戶嘗試訪問安全頁面,並且您將其重定向到登錄表單,則安全頁面的地址將存儲在ctzn_referer中,因此您可以將其發送到此處而不是上頁。
如果您尚未啟用會議,則公民會改為創建一個名為ctzn_referer的cookie。
如果您使用代理後面的公民(例如Nginx或Apache),請確保您的服務器配置中具有HTTP Forwarded標頭,因此ctzn_referer可以正常工作。
這是您如何在nginx中設置此操作的示例:
location / {
proxy_set_header Forwarded "for=$remote_addr;host=$host;proto=$scheme;";
proxy_pass http://127.0.0.1:8080;
}
您可以使用header指令設置HTTP標頭:
return {
header : {
'Cache-Control' : 'max-age=86400' ,
'Date' : new Date ( ) . toISOString ( )
}
}您還可以使用Node的response.setHeader()方法,但是使用Citizen的header指令將這些標頭保存在請求緩存中,因此每當從緩存中取出該控制器操作時,它們都會被應用。
公民讓您可以使用完整的MVC模式,即公民版本的組件。每個都有自己的路線控制器,模型和視圖。包含可用於執行操作或返回完整的渲染視圖。任何路由控制器都可以包括。
假設我們的文章模板的模板具有以下內容。頭部包含動態元數據,並且標頭的內容根據用戶是否登錄而變化:
<!doctype html >
< html >
< head >
< title > ${local.metaData.title} </ title >
< meta name =" description " content =" ${local.metaData.description} " >
< meta name =" keywords " content =" ${local.metaData.keywords} " >
< link rel =" stylesheet " type =" text/css " href =" site.css " >
</ head >
< body >
< header >
${ cookie.username ? ' < p > Welcome, ' + cookie.username + ' </ p > ' : ' < a href =" /login " > Login </ a > ' }
</ header >
< main >
< h1 > ${local.article.title} — Page ${url.page} </ h1 >
< p > ${local.article.summary} </ p >
< section > ${local.article.text} </ section >
</ main >
</ body >
</ html >使用的主題部分和標題可能是有意義的,因為您可以在任何地方使用該代碼,但是您可以創建公民包含的簡單局部代碼。頭部可以使用自己的模型來填充元數據,並且由於已經過身份驗證的用戶的標頭不同,因此讓我們將該邏輯從視圖中拉出並將其放在標頭的控制器中。我喜歡遵循以下典範的慣例,但這取決於您:
app/
controllers/
routes/
_head.js
_header.js
article.js
models/
_head.js
article.js
views/
_head.html
_header/
_header.html
_header-authenticated.html // A different header for logged in users
article.html
當文章控制器被解僱時,它必須告訴包括其需要的公民。我們將其與指令include :
// article controller
export const handler = async ( params ) => {
// Get the article
const article = await app . models . article . get ( {
article : params . url . article ,
page : params . url . page
} )
return {
local : {
article : article
} ,
include : {
// Include shorthand is a string containing the pathname to the desired route controller
_head : '/_head/action/article' ,
// Long-form include notation can explicitly define a route controller, action, and view
_header : {
controller : '_header' ,
// If the username cookie exists, use the authenticated action. If not, use the default action.
action : params . cookie . username ? 'authenticated' : 'handler'
}
}
}
}公民包括模式與常規模式相同的要求,包括具有公共行動的控制者。上面的include指令告訴公民呼叫_head和_header控制器,將其傳遞給已傳遞給article控制器的參數(參數,請求,響應,上下文),呈現其各自的觀點,並將結果視圖添加到視圖上下文中。
這是我們的總部控制器的外觀:
// _head controller
export const article = async ( params ) => {
let metaData = await app . models . _head ( { article : params . url . article } )
return {
local : {
metaData : metaData
}
}
}和頭部視圖:
< head >
< title > ${local.metaData.title} </ title >
< meta name =" description " content =" ${local.metaData.description} " >
< meta name =" keywords " content =" ${local.metaData.keywords} " >
< link rel =" stylesheet " type =" text/css " href =" site.css " >
</ head >這是我們的標題控制器的樣子:
// _header controller
// No need for a return statement, and no need to specify the view
// because handler() renders the default view.
//
// Every route controller needs at least one action, even if it's empty.
export const handler = ( ) => { }
export const authenticated = ( ) => {
return {
view : '_header-authenticated'
}
}和標題視圖:
<!-- /views/_header/_header.html -->
< header >
< a href =" /login " > Login </ a >
</ header > <!-- /views/_header/_header-authenticated.html -->
< header >
< p > Welcome, ${cookie.username} </ p >
</ header >渲染包括存儲在include範圍中:
<!-- /views/article.html -->
<!doctype html >
< html >
${include._head}
< body >
${include._header}
< main >
< h1 > ${local.title} — Page ${url.page} </ h1 >
< p > ${local.summary} </ p >
< section > ${local.text} </ section >
</ main >
</ body >
</ html >公民包含的是獨立的,並將其作為完全渲染的觀點交付給呼叫控制器。當他們接收相同的數據(URL參數,表單輸入,請求上下文等)作為調用控制器,但在include a內部生成的數據並未傳遞給呼叫者。
就像其他任何路由控制器一樣,可以通過HTTP訪問旨在用作包含的模式。您可以像這樣請求_header控制器,並收到一大堆HTML或JSON作為回應:
http://cleverna.me/_header
這非常適合處理第一個請求服務器端,然後使用客戶端庫更新內容。
公民提供了豐富的功能,但是它們確實有局限性,並且在某些情況下可能會過度殺傷。
Citizen允許您使用next指令將多個路由控制器從單個請求組成串聯。請求的控制器將其數據傳遞並渲染到後續控制器,添加其自己的數據並呈現自己的視圖。
您可以按照您想要的單個請求將盡可能多的路由控制器串在一起。每個路由控制器都將具有其數據並查看輸出存儲在params.route.chain對像中。
// The index controller accepts the initial request and hands off execution to the article controller
export const handler = async ( params ) => {
let user = await app . models . user . getUser ( { userID : params . url . userID } )
return {
local : {
user : user
} ,
// Shorthand for next is a string containing the pathname to the route controller.
// URL paramaters in this route will be parsed and handed to the next controller.
next : '/article/My-Article/id/5'
// Or, you can be explicit, but without parameters
next : {
// Pass this request to app/controllers/routes/article.js
controller : 'article' ,
// Specifying the action is optional. The next controller will use its default action, handler(), unless you specify a different action here.
action : 'handler' ,
// Specifying the view is optional. The next controller will use its default view unless you tell it to use a different one.
view : 'article'
}
// You can also pass custom directives and data.
doSomething: true
}
}鏈中的每個控制器都可以訪問上一個控制器的上下文和視圖。鏈中的最後一個控制器提供了最終的渲染視圖。帶有所有網站全局元素的佈局控制器是對此的常見用途。
// The article controller does its thing, then hands off execution to the _layout controller
export const handler = async ( params , request , response , context ) => {
let article = await getArticle ( { id : params . url . id } )
// The context from the previous controller is available to you in the current controller.
if ( context . doSomething ) { // Or, params.route.chain.index.context
await doSomething ( )
}
return {
local : {
article : article
} ,
next : '/_layout'
}
}鏈中每個控制器的渲染視圖存儲在route.chain中。
<!-- index.html, which is stored in route.chain.index.output -->
< h1 > Welcome, ${local.user.username}! </ h1 >
<!-- article.html, which is stored in route.chain.article.output -->
< h1 > ${local.article.title} </ h1 >
< p > ${local.article.summary} </ p >
< section > ${local.article.text} </ section >佈局控制器處理包含並呈現自己的視圖。因為這是鏈中的最後一個控制器,所以這種渲染的視圖將發送給客戶。
// _layout controller
export const handler = async ( params ) => {
return {
include : {
_head : '/_head' ,
_header : {
controller : '_header' ,
action : params . cookie . username ? 'authenticated' : 'handler'
} ,
_footer : '/_footer
}
}
} <!-- _layout.html -->
<!doctype html >
< html >
${include._head}
< body >
${include._header}
< main >
<!-- You can render each controller's view explicitly -->
${route.chain.index.output}
${route.chain.article.output}
<!-- Or, you can loop over the route.chain object to output the view from each controller in the chain -->
${Object.keys(route.chain).map( controller = > { return route.chain[controller].output }).join('')}
</ main >
${include._footer}
</ body >
</ html >您可以通過將view指令設置為false,從而跳過鏈中的控制器的視圖:
// This controller action won't render a view
export const handler = async ( ) => {
return {
view : false ,
next : '/_layout'
}
}要在請求中繞過next ,請在URL中添加/direct/true 。
http://cleverna.me/index/direct/true
請求的路由控制器的next指令將被忽略,其視圖將直接返回給客戶端。
如本文檔開頭的配置部分所述,您可以在配置中指定一個默認佈局控制器,因此您不必在每個控制器鏈的末尾插入它:
{
"citizen" : {
"layout" : {
"controller" : " _layout " ,
"view" : " _layout "
}
}
}如果使用此方法,則無需next佈局中使用。鏈中的最後一個控制器將始終將請求交給佈局控制器以進行最終渲染。
Citizen為您提供了幾種改善應用程序性能的方法,其中大多數以系統資源(內存或CPU)為代價。
在許多情況下,請求的URL或路由控制器操作將根據相同的輸入參數生成相同的視圖,因此運行控制器鏈並為每個請求從頭開始視圖是沒有意義的。 Citizen提供靈活的緩存功能,可通過cache指令加快服務器端的渲染。
如果給定請求(URL)將在每個請求中導致完全相同的渲染視圖,則可以使用request屬性緩存該請求。這是最快的緩存選項,因為它可以從內存中汲取完全渲染的視圖並跳過所有控制器處理。
假設您像上面一樣鏈接索引,文章和佈局控制器。如果將以下緩存指令放入索引控制器中,則請求的URL響應將被緩存,然後隨後的請求將完全跳過索引,文章和佈局控制器。
return {
next : '/article' ,
cache : {
request : true
}
}對於請求緩存指令的工作,必須將其放置在鏈中的第一個控制器中;換句話說,原始請求的路由控制器(在這種情況下為索引)。在任何後續控制器中將忽略它。
URL用作緩存鍵,因此以下每個URL都會生成自己的緩存項目:
http://cleverna.me/article
http://cleverna.me/article/my-article
http://cleverna.me/article/my-article/page/2
上面的示例是用於默認緩存設置的速記。 cache.request指令也可以是一個具有選項的對象:
// Cache the requested route with some additional options
return {
cache : {
request : {
// Optional. This setting lets the server respond with a 304 Not Modified
// status if the cache content hasn't been updated since the client last
// accessed the route. Defaults to the current time if not specified.
lastModified : new Date ( ) . toISOString ( ) ,
// Optional. List of valid URL parameters that protects against accidental
// caching of malformed URLs.
urlParams : [ 'article' , 'page' ] ,
// Optional. Life of cached item in minutes. Default is 15 minutes.
// For no expiration, set to 'application'.
lifespan : 15 ,
// Optional. Reset the cached item's expiration timer whenever the item is
// accessed, keeping it in the cache until traffic subsides. Default is true.
resetOnAccess : true
}
}
} 如果給定的路由鏈在請求中會有所不同,則您仍然可以緩存單個控制器操作以使用action屬性加快渲染速度。
// Cache this controller action using the default settings
return {
cache : {
action : true
}
}
// Cache this controller with additional options
return {
cache : {
action : {
// These options function the same as request caching (see above)
urlParams : [ 'article' , 'page' ] ,
lifespan : 15 ,
resetOnAccess : true
}
}
}當您緩存控制器操作時,它們的上下文也會被緩存。在緩存的控制器操作中設置cookie或會話變量意味著該操作的所有未來請求都將設置相同的cookie或會話變量,這可能不是您想對用戶數據進行的操作。
lastModified如果請求緩存的內容沒有更改以來,則此設置使服務器可以更快地304 Not Modified響應304的響應。默認情況下,將其設置為緩存請求的時間,但是您可以以ISO格式指定自定義日期,以反映對請求內容的最後修改。
return {
next : '/_layout' ,
cache : {
request : {
// Use toISOString() to format your date appropriately
lastModified : myDate . toISOString ( ) // 2015-03-05T08:59:51.491Z
}
}
} urlParams urlParams屬性有助於防止無效的高速緩存項目(或更糟糕的是:攻擊是通過超載緩存來淹沒服務器資源的攻擊)。
return {
next : '/_layout' ,
cache : {
request : {
urlParams : [ 'article' , 'page' ]
}
}
}如果我們在文章控制器中使用了上面的示例,則將限制以下URL,因為允許“文章”和“頁面” URL參數:
http://cleverna.me/article
http://cleverna.me/article/my-article-title
http://cleverna.me/article/my-article-title/page/2
以下URL不會被緩存,這是一件好事,因為攻擊者的腳本在URL上循環並淹沒了緩存不會花很長時間:
http://cleverna.me/article/my-article-title/dosattack/1
http://cleverna.me/article/my-article-title/dosattack/2
http://cleverna.me/article/my-article-title/page/2/dosattack/3
當存在無效的URL參數時,服務器會記錄警告,但是繼續處理而無需緩存結果。
lifespan此設置確定請求或控制器操作應在數分鐘內保留多長時間。
return {
cache : {
request : {
// This cached request will expire in 10 minutes
lifespan : 10
}
}
} resetOnAccess在lifespan設置的情況下, resetOnAccess每當訪問路線或控制器緩存的計時器,將其保存在緩存中,直到流量平息為止。默認為true 。
return {
cache : {
request : {
// This cached request will expire in 10 minutes, but if a request accesses it
// before then, the cache timer will be reset to 10 minutes from now
lifespan : 10 ,
resetOnAccess : true
}
}
} 在大多數情況下,您可能需要在緩存整個請求(URL)或緩存單個控制器操作之間進行選擇,但並非兩者兼而有之。
緩存一個包含控制器操作時,指向其中的路徑路徑名用作緩存鍵。如果使用邏輯使用相同的控制器操作呈現不同的視圖,則將第一個渲染視圖被緩存。在這種情況下,您可以傳遞其他URL參數,以超越此限制,並為不同的視圖創建一個唯一的緩存項目。
export const handler = async ( context ) => {
return : {
// Two different versions of the _header include will be cached becaues the URLs are unique
include : context . authenticated ? '/_header/authenticated/true' : '/_header'
}
}公民緩存是存儲在V8堆中的RAM緩存,因此請謹慎使用緩存策略。 lifespan和resetOnAccess選項,以便在緩存中獲得大量流量的URL,而流行的URL自然會隨著時間的流逝而自然而然地掉出緩存。
通過將靜態資產緩存在內存中,您可以大大加快文件服務。要為您的應用程序的public(Web)目錄啟用靜態資產緩存, true在配置中設置cache.static.enabled 。
{
"citizen" : {
"cache" : {
"static" : {
"enabled" : true
}
}
}
}公民使用每個文件的最後一個修改日期自動處理響應標頭(ETAG,304個狀態代碼等)。請注意,如果文件被緩存後更改,則需要使用cache.clear()清除文件緩存或重新啟動應用程序。
從運行應用中清除緩存文件的文件:
app . cache . clear ( { file : '/absolute/path/to/file.jpg' } )啟用了靜態緩存,所有靜態文件都將在V8堆中緩存,因此請密切關注應用程序的內存使用情況,以確保您不會使用太多資源。
公民自動為緩存的請求和靜態資產設置ETAG標頭。您無需採取任何措施使它們工作。但是,緩存控制標頭完全是手動的。
要設置用於靜態資產的高速緩存控制標頭,請在配置中使用cache.control設置:
{
"citizen" : {
"cache" : {
"static" : true ,
"control" : {
"/css/global.css" : " max-age=86400 " ,
"/css/index.css" : " max-age=86400 " ,
"/js/global.js" : " max-age=86400 " ,
"/js/index.js" : " max-age=86400 " ,
"/images/logo.png" : " max-age=31536000 "
}
}
}
}關鍵名稱是指向Web目錄中靜態資產的路徑名。如果您的應用程序的URL路徑是/my/app ,則此值應該是/my/app/styles.css之類的。該值是要分配給該資產的高速緩存標頭值。
您可以使用像上面一樣符合確切路徑名的字符串,也可以使用通配符。混合兩者很好:
{
"citizen" : {
"cache" : {
"static" : true ,
"control" : {
"/css/*" : " max-age=86400 " ,
"/js/*" : " max-age=86400 " ,
"/images/logo.png" : " max-age=31536000 "
}
}
}
}這是有關客戶端緩存的出色教程,可幫助解釋ETAG和緩存控制標題。
在將它們發送到瀏覽器之前,都可以壓縮動態路線和靜態資產。為支持它的客戶啟用壓縮:
{
"citizen" : {
"compression" : {
"enabled" : true
}
}
}代理,防火牆和其他網絡情況可以剝離請求標頭,該標頭告訴服務器提供壓縮資產。您可以為這樣的所有客戶強加GZIP或放氣:
{
"citizen" : {
"compression" : {
"enabled" : true ,
"force" : " gzip "
}
}
}如果您啟用了請求緩存,則將緩存該請求的原始(身份)和壓縮(GZIP和DEFLATE)版本,因此您的緩存內存利用率將增加。
公民包括基本請求有效載荷解析。當用戶提交表單時,可以通過params.form在控制器中提供解析的表單數據。如果您想自己使用第三方軟件包來解析表單數據,則可以禁用在配置中解析的表單並通過request.payload訪問原始有效負載。
// login controller
export const handler = ( params ) => {
// Set some defaults for the login view
params . form . username = ''
params . form . password = ''
params . form . remember = false
}
// Using a separate action in your controller for form submissions is probably a good idea
export const submit = async ( params ) => {
let authenticate = await app . models . user . authenticate ( {
username : params . form . username ,
password : params . form . password
} ) ,
cookies = { }
if ( authenticate . success ) {
if ( params . form . remember ) {
cookies . username : authenticate . username
}
return {
cookies : cookies ,
redirect : '/'
}
} else {
return {
local : {
message : 'Login failed.'
}
}
}
}如果它是包含文件的多部分錶單,則傳遞給您的控制器的對象將看起來像這樣:
{
field1 : 'bar' ,
field2 : 'buzz' ,
fileField1 : {
filename : 'file.png' ,
contentType : 'image/png' ,
binary : < binary data >
}
}文件內容以二進制格式顯示,因此您需要使用Buffer.from(fileField1.binary, 'binary')來創建用於存儲的實際文件。
您可以通過citizen.form在配置中或通過Controller Confort在Controller Action級別傳遞全局表單設置(請參見下文)。
使用maxPayloadSize配置來限制上載。以下配置將maxFieldsSize設置為512K:
{
"citizen" : {
"forms" : {
"maxPayloadSize" : 500000 // 0.5MB
}
}
} maxPayloadSize選項在其計算中以多部分形式包含文本輸入和文件。如果表單數據超過此金額,則公民會造成錯誤。
某些事件將在您的公民申請中或每個請求中發生。您可以通過context參數對這些事件採取這些事件,執行功能,設置指令,然後將結果傳遞到下一個事件或控制器。例如,您可能會在每個新會話的開頭設置一個cookie,或在每個請求的開頭檢查cookie,並在未經認證的情況下將用戶重定向到登錄頁面。
為了利用這些事件,請在應用程序中使用一個名為“鉤子”的目錄,其中包含以下任何或所有以下模塊和出口:
app/
controllers/
hooks/
application.js // exports start() and error()
request.js // exports start() and end()
response.js // exports start() and end()
session.js // exports start() and end()
request.start() , request.end()和response.start()在啟動控制器之前調用,因此這些事件的輸出從每個事件傳遞到另一個事件,並最終通過context參數傳遞給您的控制器。他們執行了哪些行動及其輸出的操作(續),公民指令,定制指令 - 取決於您。
所有文件和導出都是可選的。公民在初創公司中解析他們,只有在它們存在的情況下才會打電話給他們。例如,您只能有一個請求start()
這是一個請求模塊的示例,該示例在每個請求的開頭檢查用戶名cookie,如果不存在,則將用戶重定向到登錄頁面。我們還通過確保請求的控制器不是登錄控制器來避免重定向循環:
// app/controllers/hooks/request.js
export const start = ( params ) => {
if ( ! params . cookie . username && params . route . controller !== 'login' ) {
return {
redirect = '/login'
}
}
} session.end在收到的參數方面略有不同,該參數僅由過期會話的副本組成(不再活動):
// app/controllers/hooks/session.js
export const end = ( expiredSession ) => {
// do something whenever a session ends
} 默認情況下,所有控制器僅響應主機的請求。公民支持跨域HTTP請求通過訪問控制標頭。
要啟用單個控制器操作的跨域訪問,請在控制器的出口中添加帶有必要標題的cors對象:
export const config = {
// Each controller action can have its own CORS headers
handler : {
cors : {
'Access-Control-Allow-Origin' : 'http://www.foreignhost.com' ,
'Access-Control-Expose-Headers' : 'X-My-Custom-Header, X-Another-Custom-Header' ,
'Access-Control-Max-Age' : 600 ,
'Access-Control-Allow-Credentials' : 'true' ,
'Access-Control-Allow-Methods' : 'OPTIONS, PUT' ,
'Access-Control-Allow-Headers' : 'Content-Type' ,
'Vary' : 'Origin'
}
}
}為什麼不只是使用HTTP標頭指令或用response.setHeader()手動設置它們?當公民收到來自主機以外的原點的請求時,它會檢查控制器中的cors導出以提供飛行前響應,而無需在控制器操作中編寫自己的邏輯。當然,您可以檢查request.method方法並在願意的情況下寫下邏輯以手動處理。
有關CORS的更多詳細信息,請查看W3C規格和Mozilla開發人員網絡。
如果您使用代理後面的公民(例如Nginx或Apache),請確保您的服務器配置中有一個Forwarded標頭,因此Citizen可以正確處理CORS請求。不同的協議(您的Load Balancer上的HTTP和您的公民應用程序中的HTTP)將導致CORS請求在沒有這些標題的情況下失敗。
這是您如何在nginx中設置此操作的示例:
location / {
proxy_set_header Forwarded "for=$remote_addr;host=$host;proto=$scheme;";
proxy_pass http://127.0.0.1:3000;
}
公民有一個內置的應用程序緩存,您可以在其中基本存儲任何東西:字符串,對象,緩衝區,靜態文件等。
您可以將任何對象存儲在公民緩存中。在您自己的全局應用程序變量中使用cache而不是存儲內容的好處是內置的緩存到期和擴展,以及用於閱讀,解析和存儲文件內容的包裝器。
公民的默認緩存時間為15分鐘,您可以在配置中進行更改(請參閱配置)。除非通過resetOnAccess: false或更改配置中的設置,否則緩存的項目壽命將延長。
// Cache a string in the default app scope for 15 minutes (default). Keys
// must be unique within a given scope.
app . cache . set ( {
key : 'welcome-message' ,
value : 'Welcome to my site.'
} )
// Cache a string under a custom scope, which is used for retrieving or clearing
// multiple cache items at once. Keys must be unique within a given scope.
// Reserved scope names are "app", "routes", and "files".
app . cache . set ( {
key : 'welcome-message' ,
scope : 'site-messages' ,
value : 'Welcome to our site.'
} )
// Cache a string for the life of the application.
app . cache . set ( {
key : 'welcome-message' ,
value : 'Welcome to my site.' ,
lifespan : 'application'
} )
// Cache a file buffer using the file path as the key. This is a wrapper for
// fs.readFile and fs.readFileSync paired with citizen's cache function.
// Optionally, tell citizen to perform a synchronous file read operation and
// use an encoding different from the default (UTF-8).
app . cache . set ( {
file : '/path/to/articles.txt' ,
synchronous : true ,
encoding : 'CP-1252'
} )
// Cache a file with a custom key. Optionally, parse the JSON and store the
// parsed object in the cache instead of the raw buffer. Expire the cache
// after 10 minutes, regardless of whether the cache is accessed or not.
app . cache . set ( {
file : '/path/to/articles.json' ,
key : 'articles' ,
parseJSON : true ,
lifespan : 10 ,
resetOnAccess : false
} ) app , routes和files是保留的示波器名稱,因此您不能將它們用於自己的自定義範圍。
這是一種檢查緩存中給定鍵或範圍的存在的方法,而無需重置該項目上的緩存計時器。如果找不到匹配,則返回false 。
// Check for the existence of the specified key
let keyExists = app . cache . exists ( { key : 'welcome-message' } ) // keyExists is true
let keyExists = app . cache . exists ( { file : '/path/to/articles.txt' } ) // keyExists is true
let keyExists = app . cache . exists ( { file : 'articles' } ) // keyExists is true
let keyExists = app . cache . exists ( { key : 'foo' } ) // keyExists is false
// Check the specified scope for the specified key
let keyExists = app . cache . exists ( {
scope : 'site-messages' ,
key : 'welcome-message'
} )
// keyExists is true
// Check if the specified scope exists and contains items
let scopeExists = app . cache . exists ( {
scope : 'site-messages'
} )
// scopeExists is true
// Check if the route cache has any instances of the specified route
let controllerExists = app . cache . exists ( {
route : '/article'
} )檢索單個鍵或整個範圍。如果請求的項目不存在,則返回false 。如果將物品緩存時resetOnAccess為真,則使用retrieve()將重置緩存時鐘並延長緩存項目的壽命。如果檢索範圍,則該範圍中的所有項目都將重置其緩存計時器。
可選地,您可以通過指定內聯指定緩存項目時覆蓋resetOnAccess屬性。
// Retrieve the specified key from the default (app) scope
let welcomeMessage = app . cache . get ( {
key : 'welcome-message'
} )
// Retrieve the specified key from the specified scope and reset its cache timer
// even if resetOnAccess was initially set to false when it was stored
let welcomeMessage = app . cache . get ( {
scope : 'site-messages' ,
key : 'welcome-message' ,
resetOnAccess : true
} )
// Retrieve all keys from the specified scope
let siteMessages = app . cache . get ( {
scope : 'site-messages'
} )
// Retrieve a cached file
let articles = app . cache . get ( {
file : '/path/to/articles.txt'
} )
// Retrieve a cached file with its custom key
let articles = app . cache . get ( {
file : 'articles'
} )使用鍵或範圍清除緩存對象。
// Store some cache items
app . cache . set ( {
key : 'welcome-message' ,
scope : 'site-messages' ,
value : 'Welcome to our site.'
} )
app . cache . set ( {
key : 'goodbye-message' ,
scope : 'site-messages' ,
value : 'Thanks for visiting!'
} )
app . cache . set ( {
file : '/path/to/articles.txt' ,
synchronous : true
} )
// Clear the welcome message from its custom scope cache
app . cache . clear ( { scope : 'site-messages' , key : 'welcome-message' } )
// Clear all messages from the cache using their custom scope
app . cache . clear ( { scope : 'site-messages' } )
// Clear the articles cache from the file scope
app . cache . clear ( { file : '/path/to/articles.txt' } ) cache.clear()也可以用於刪除緩存的請求和控制器操作。
app . cache . clear ( {
route : '/article/My-Article/page/2'
} )
// Clear the entire route scope
app . cache . clear ( { scope : 'routes' } )
// Clear the entire file scope
app . cache . clear ( { scope : 'files' } )
// Clear the entire cache
app . cache . clear ( ) 公民log()函數暴露在您的應用中通過app.log()使用。
以取決於框架模式的方式,可以輕鬆地將評論記錄到控制台或文件(或兩者)。
當公民處於生產模式時, log()默認沒有任何操作。在開發模式下, log()將記錄您傳遞的任何內容。這意味著您可以將其放置在整個應用程序的代碼中,並且只會在開發模式下寫入日誌。您可以使用配置文件中的日誌設置在全球範圍內覆蓋此行為,或者在調用log()時使用console或file選項。
app . log ( {
// Optional. Valid settings are "status" (default) or "error".
type : 'status' ,
// Optional string. Applies a label to your log item.
label : 'Log output' ,
// The content of your log. If it's anything other than a string or
// number, log() will run util.inspect on it and dump the contents.
contents : someObject ,
// Optional. Enables console logs.
console : true ,
// Optional. Enables file logging.
file : false ,
// Optional. File name you'd like to write your log to.
file : 'my-log-file.log' ,
// Optional. Disables the timestamp that normally appears in front of the log
timestamp : false
} )日誌文件顯示在您在config.citizen.directories.logs中指定的目錄中。
警告: development模式本質上是不安全的。不要在生產環境中使用它。
如果在配置文件中設置"mode": "development" ,則公民將所有主要操作都傾倒到控制台。
您還可以通過設置development.debug.view將請求上下文轉移到視圖中,請訪問config File中的true ,或以每次重新要求使用ctzn_debug url參數:
// config file: always dumps debug output in the view
{
"citizen" : {
"development" : {
"debug" : {
"view" : true
}
}
}
}默認情況下,公民將模式的完整上下文傾倒。您可以指定使用ctzn_inspect URL參數調試的確切對象:
// Dumps the server params object
http://www.cleverna.me/article/id/237/page/2/ctzn_debug/true/ctzn_inspect/params
// Dumps the user's session scope
http://www.cleverna.me/article/id/237/page/2/ctzn_debug/true/ctzn_inspect/params.session
默認情況下,調試輸出遍歷對象4級。要顯示更深的輸出,請在您的配置文件中使用development.debug.depth設置,或將ctzn_debugDepth附加到URL。調試渲染將需要更長的時間。
// config file: debug 4 levels deep
{
"citizen" : {
"development" : {
"debug" : {
"depth" : 6
}
}
}
}
// URL
// http://www.cleverna.me/article/id/237/page/2/ctzn_debug/true/ctzn_debugDepth/4在development模式下,您必須指定ctzn_debug URL參數以顯示調試輸出。調試輸出在生產模式下被禁用。
公民包裝中的UTIL目錄具有一些有用的公用事業。
創建具有功能索引模式和錯誤模板的公民應用程序的完整骨架。
$ node node_modules/citizen/util/scaffold skeleton結果文件結構:
app/
config/
citizen.json
controllers/
hooks/
application.js
request.js
response.js
session.js
routes/
index.js
models/
index.js
views/
error/
404.html
500.html
ENOENT.html
error.html
index.html
start.js
web/
運行node node_modules/citizen/util/scaffold skeleton -h以獲取選項。
創建完整的公民MVC模式。模式命令採用模式名稱和選項:
$ node node_modules/citizen/util/scaffold pattern [options] [pattern]例如, node scaffold pattern article將創建以下模式:
app/
controllers/
routes/
article.js
models/
article.js
views/
article/
article.html
使用node node_modules/citizen/util/scaffold pattern -h查看所有可定製圖案的可用選項。
(麻省理工學院許可證)
版權(C)2014-2024 Jay Sylvester
特此免費授予任何獲得此軟件副本和相關文檔文件副本(“軟件”)的人,以無限制處理該軟件,包括不限於使用,複製,修改,合併,合併,發布,分發,分發,分發,撒下,sublicense和/或允許軟件的副本,並允許對以下條件提供以下條件,以下是以下條件。
上述版權通知和此許可通知應包含在軟件的所有副本或大量部分中。
該軟件是“按原樣”提供的,沒有任何形式的明示或暗示保證,包括但不限於適銷性,適合特定目的和非侵害的保證。在任何情況下,作者或版權持有人都不應對任何索賠,損害賠償或其他責任責任,無論是在合同,侵權的訴訟中還是其他責任,是由軟件,使用或與軟件中的使用或其他交易有關的。
默認情況下,公民將模式的完整上下文傾倒。您可以指定使用ctzn_inspect URL參數調試的確切對象:
// Dumps the server params object
http://www.cleverna.me/article/id/237/page/2/ctzn_debug/true/ctzn_inspect/params
// Dumps the user's session scope
http://www.cleverna.me/article/id/237/page/2/ctzn_debug/true/ctzn_inspect/params.session
默認情況下,調試輸出遍歷對象4級。要顯示更深的輸出,請在您的配置文件中使用development.debug.depth設置,或將ctzn_debugDepth附加到URL。調試渲染將需要更長的時間。
// config file: debug 4 levels deep
{
"citizen" : {
"development" : {
"debug" : {
"depth" : 6
}
}
}
}
// URL
// http://www.cleverna.me/article/id/237/page/2/ctzn_debug/true/ctzn_debugDepth/4在development模式下,您必須指定ctzn_debug URL參數以顯示調試輸出。調試輸出在生產模式下被禁用。
公民包裝中的UTIL目錄具有一些有用的公用事業。
創建具有功能索引模式和錯誤模板的公民應用程序的完整骨架。
$ node node_modules/citizen/util/scaffold skeleton結果文件結構:
app/
config/
citizen.json
controllers/
hooks/
application.js
request.js
response.js
session.js
routes/
index.js
models/
index.js
views/
error/
404.html
500.html
ENOENT.html
error.html
index.html
start.js
web/
運行node node_modules/citizen/util/scaffold skeleton -h以獲取選項。
創建完整的公民MVC模式。模式命令採用模式名稱和選項:
$ node node_modules/citizen/util/scaffold pattern [options] [pattern]例如, node scaffold pattern article將創建以下模式:
app/
controllers/
routes/
article.js
models/
article.js
views/
article/
article.html
使用node node_modules/citizen/util/scaffold pattern -h查看所有可定製圖案的可用選項。
(麻省理工學院許可證)
版權(C)2014-2024 Jay Sylvester
特此免費授予任何獲得此軟件副本和相關文檔文件副本(“軟件”)的人,以無限制處理該軟件,包括不限於使用,複製,修改,合併,合併,發布,分發,分發,分發,撒下,sublicense和/或允許軟件的副本,並允許對以下條件提供以下條件,以下是以下條件。
上述版權通知和此許可通知應包含在軟件的所有副本或大量部分中。
該軟件是“按原樣”提供的,沒有任何形式的明示或暗示保證,包括但不限於適銷性,適合特定目的和非侵害的保證。在任何情況下,作者或版權持有人都不應對任何索賠,損害賠償或其他責任責任,無論是在合同,侵權的訴訟中還是其他責任,是由軟件,使用或與軟件中的使用或其他交易有關的。