摘要:處理函數的返回值用于渲染輸出。例如,如果不滿足某些條件,我們可以取消操作并重定向到另一個頁面是處理一個常規動作的返回值,所以同樣的事情你可以返回一個動作,可以傳遞給請求對象每個操作在調用時會請求對象作為其第一個參數傳遞。
lapis請求處理
每個被Lapis處理的HTTP請求在被Nginx處理后都遵循相同的基本流程。第一步是路由。路由是 url 必須匹配的模式。當你定義一個路由時,你也得包括一個處理函數。這個處理函數是一個常規的Lua/MoonScript函數,如果相關聯的路由匹配,則將調用該函數。
所有被調用的處理函數都具有一個參數(一個請求對象)。請求對象將存儲您希望在處理函數和視圖之間共享的所有數據。此外,請求對象是您向Web服務器了解如何將結果發送到客戶端的接口。
處理函數的返回值用于渲染輸出。字符串返回值將直接呈現給瀏覽器。table 的返回值將用作[渲染選項]()。如果有多個返回值,則所有這些返回值都合并到最終結果中。您可以返回字符串和table以控制輸出。
如果沒有匹配請求的路由,則執行默認路由處理程序,在[application callbacks]()了解更多。
Routes 和 URL 模式路由模式 使用特殊語法來定義URL的動態參數 并為其分配一個名字。最簡單的路由沒有參數:
local lapis = require("lapis") local app = lapis.Application() app:match("/", function(self) end) app:match("/hello", function(self) end) app:match("/users/all", function(self) end)
這些路由與URL逐字匹配。 / 路由是必需的。路由必須匹配請求的整個路徑。這意味著對 /hello/world 的請求將不匹配 /hello。
您可以在:后面理解跟上一個名稱來指定一個命名參數。該參數將匹配除/的所有字符(在一般情況下):
app:match("/page/:page", function(self) print(self.params.page) end) app:match("/post/:post_id/:post_name", function(self) end)
在上面的例子中,我們調用 print 函數來調試,當在openresty中運行時,print的輸出是被發送到nginx的notice級別的日志中去的
捕獲的路由參數的值按其名稱保存在請求對象的 params 字段中。命名參數必須至少包含1個字符,否則將無法匹配。
splat是另一種類型的模式,將盡可能匹配,包括任何/字符。 splat存儲在請求對象的 params 表中的 splat 命名參數中。它只是一個單一 *
app:match("/browse/*", function(self) print(self.params.splat) end) app:match("/user/:name/file/*", function(self) print(self.params.name, self.params.splat) end)
如果將任何文本直接放在splat或命名參數之后,它將不會包含在命名參數中。例如,您可以將以.zip結尾的網址與/files/:filename.zip進行匹配(那么.zip就不會包含在命名參數 filename 中)
可選路由組件圓括號可用于使路由的一部分可選:
/projects/:username(/:project)
以上將匹配 /projects/leafo 或 /projects/leafo/lapis 。可選組件中不匹配的任何參數在處理函數中的值將為nil。
這些可選組件可以根據需要嵌套和鏈接:
/settings(/:username(/:page))(.:format)參數字符類
字符類可以應用于命名參數,以限制可以匹配的字符。語法建模在 Lua 的模式字符類之后。此路由將確保該 user_id 命名參數只包含數字:
/color/:hex[a-fA-F%d]
這個路由只匹配十六進制參數的十六進制字符串。
/color/:hex[a-fA-F%d]路由優先級
首先按優先順序搜索路由,然后按它們定義的順序搜索。從最高到最低的路由優先級為:
精確匹配的路由 /hello/world 變化參數的路由 /hello/:variable 貪婪匹配的路由 /hello/*命名路由
為您的路由命名是有用的,所以只要知道網頁的名稱就可以生成到其他網頁的鏈接,而不是硬編碼 URL 的結構。
應用程序上定義新路由的每個方法都有第二個形式,它將路由的名稱作為第一個參數:
local lapis = require("lapis") local app = lapis.Application() app:match("index", "/", function(self) return self:url_for("user_profile", { name = "leaf" }) end) app:match("user_profile", "/user/:name", function(self) return "Hello " .. self.params.name .. ", go home: " .. self:url_for("index") end)
我們可以使用self:url_for()生成各種操作的路徑。第一個參數是要調用的路由的名稱,第二個可選參數是用于填充 參數化路由 的值的表。
點擊[url_for]() 去查看不同方式去生成 URL 的方法。
處理HTTP動詞根據請求的 HTTP 動詞,進行不同的處理操作是很常見的。 Lapis 有一些小幫手,讓寫這些處理操作很簡單。 respond_to 接收由 HTTP 動詞索引的表,當匹配對應的動詞執行相應的函數處理
local lapis = require("lapis") local respond_to = require("lapis.application").respond_to local app = lapis.Application() app:match("create_account", "/create-account", respond_to({ GET = function(self) return { render = true } end, POST = function(self) do_something(self.params) return { redirect_to = self:url_for("index") } end }))
respond_to 也可以采用自己的 before 過濾器,它將在相應的 HTTP 動詞操作之前運行。我們通過指定一個 before 函數來做到這一點。與過濾器相同的語義適用,所以如果你調用 self:write(),那么其余的動作將不會運行.
local lapis = require("lapis") local respond_to = require("lapis.application").respond_to local app = lapis.Application() app:match("edit_user", "/edit-user/:id", respond_to({ before = function(self) self.user = Users:find(self.params.id) if not self.user then self:write({"Not Found", status = 404}) end end, GET = function(self) return "Edit account " .. self.user.name end, POST = function(self) self.user:update(self.params.user) return { redirect_to = self:url_for("index") } end }))
在任何 POST 請求,無論是否使用 respond_to,如果 Content-type 頭設置為 application/x-www-form-urlencoded,那么請求的主體將被解析,所有參數將被放入 self.params。
您可能還看到了 app:get() 和 app:post() 方法在前面的示例中被調用。這些都是封裝了 respond_to 方法,可讓您快速為特定 HTTP 動詞定義操作。你會發現這些包裝器最常見的動詞:get,post,delete,put。對于任何其他動詞,你需要使用respond_to。
app:get("/test", function(self) return "I only render for GET requests" end) app:delete("/delete-account", function(self) -- do something destructive end)Before Filters
有時你想要一段代碼在每個操作之前運行。一個很好的例子是設置用戶會話。我們可以聲明一個 before 過濾器,或者一個在每個操作之前運行的函數,像這樣:
local app = lapis.Application() app:before_filter(function(self) if self.session.user then self.current_user = load_user(self.session.user) end end) app:match("/", function(self) return "current user is: " .. tostring(self.current_user) end)
你可以通過多次調用 app:before_filter 來隨意添加。它們將按照注冊的順序運行。
如果一個 before_filter 調用 self:write()方法,那么操作將被取消。例如,如果不滿足某些條件,我們可以取消操作并重定向到另一個頁面:
local app = lapis.Application() app:before_filter(function(self) if not user_meets_requirements() then self:write({redirect_to = self:url_for("login")}) end end) app:match("login", "/login", function(self) -- ... end)
self:write() 是處理一個常規動作的返回值,所以同樣的事情你可以返回一個動作,可以傳遞給 self:write()
請求對象每個操作在調用時會請求對象作為其第一個參數傳遞。由于調用第一個參數 self 的約定,我們在一個操作的上下文中將請求對象稱為 self。
請求對象具有以下參數:
self.params 一個包含所有GET,POST 和 URL 參數的表
self.req 原始請求表(從ngx狀態生成)
self.res 原始響應表(從ngx狀態生成)
self.app 應用程序的實例
self.cookies cookie 表,可以分配設置新的cookie。 只支持字符串作為值
self.session session表, 可以存儲任何能夠 被JSON encode 的值。 由Cookie支持
self.route_name 匹配請求的路由的名稱(如果有)
self.options 控制請求如何呈現的選項集,通過write設置
self.buffer 輸出緩沖區,通常你不需要手動設置,通過write設置
此外,請求對象具有以下方法:
write(options, ...) 指示請求如何呈現結果
url_for(route, params, ...) 根據命名路由或對象來獲取 URL
build_url(path, params) 根據 path 和 params 構建一個完整的URL
html(fn) 使用HTML構建語法生成字符串
@req原始請求表 self.req 封裝了 ngx 提供的一些數據。 以下是可用屬性的列表。
self.req.headers 請求頭的表
self.req.parsed_url 解析請求的url,這是一個包含scheme, path, host, port 和 query 屬性的表
self.req.params_post POST請求的參數表
self.req.params_get GET請求的參數表
Cookies請求中的 self.cookies 表允許您讀取和寫入Cookie。 如果您嘗試遍歷表以打印 Cookie,您可能會注意到它是空的:
app:match("/my-cookies", function(self) for k,v in pairs(self.cookies) do print(k, v) end end)
現有的 Cookie 存儲在元表的 __index 中。 之這樣做,是因為我們可以知道在操作期間分配了哪些 Cookie,因為它們將直接在 self.cookies 表中。
因此,要設置一個 cookie,我們只需要分配到 self.cookies 表:
app:match("/sets-cookie", function(self) self.cookies.foo = "bar" end)
默認情況下,所有 Cookie 都有額外的屬性 Path = /; HttpOnly (創建一個session cookie )。 您可以通過重寫 app.cookie_attributes 函數來配置 cookie 的設置。 以下是一個向 cookies 添加過期時間以使其持久化的示例:
local date = require("date") local app = lapis.Application() app.cookie_attributes = function(self) local expires = date(true):affffdays(365):fmt("${http}") return "Expires=" .. expires .. "; Path=/; HttpOnly" end
cookie_attributes 方法將請求對象作為第一個參數(self),然后是要處理的 cookie 的名稱和值。
Sessionself.session 是一種更先進的方法,通過請求來持久化數據。 會話的內容被序列化為 JSON 并存儲在特定名稱的 cookie 中。 序列化的 Cookie 使用您的應用程序密鑰簽名,因此不會被篡改。 因為它是用 JSON 序列化的,你可以存儲嵌套表和其他原始值。
session 可以像 Cookie 一樣設置和讀取:
app.match("/", function(self) if not self.session.current_user then self.session.current_user = "Adam" end end)
默認情況下,session 存儲在名為 lapis_session 的 cookie 中。 您可以使用配置變量session_name 覆蓋 session 的名稱。 session 使用您的應用程序密鑰(存儲在配置的secret 中)進行簽名。 強烈建議更改它的默認值。
-- config.lua local config = require("lapis.config").config config("development", { session_name = "my_app_session", secret = "this is my secret string 123456" })請求對象的方法 write(things...)
一下列出它的所有參數。 根據每個參數的類型執行不同的操作。
string 字符串追加到輸出緩沖區
function (或者是可調用表) 函數被輸出緩沖區調用,結果遞歸傳遞給write
table 鍵/值對將會被分配到 self.options中 ,所有其他值遞歸傳遞給write
在大多數情況下,沒有必要調用 write ,因為處理函數的返回值會自動傳遞給 write 。 在before filter中 ,write具有寫入輸出和取消任何進一步操作的雙重目的。
url_for(name_or_obj, params, query_params=nil, ...)依據 路由的name或一個對象生成 url
url_for 有點用詞不當,因為它通常生成到請求的頁面的路徑。 如果你想得到整個 URL,你可以與build_url函數和一起使用。
如果 name_or_obj 是一個字符串,那么使用 params中的值來查找和填充該名稱的路由。 如果路由不存在,則拋出錯誤。
給定以下路由:
app:match("index", "/", function() -- ... end) app:match("user_data", "/data/:user_id/:data_field", function() -- ... end)
到頁面的 URL 可以這樣生成:
-- returns: / self:url_for("index") -- returns: /data/123/height self:url_for("user_data", { user_id = 123, data_field = "height"})
如果提供了第三個參數 query_params ,它將被轉換為查詢參數并附加到生成的 URL 的末尾。 如果路由不接受任何參數,則第二個參數必須被設置為 nil 或 空對象 :
-- returns: /data/123/height?sort=asc self:url_for("user_data", { user_id = 123, data_field = "height"}, { sort = "asc" }) -- returns: /?layout=new self:url_for("index", nil, {layout = "new"})
如果提供了所有封閉的參數,則只包括路由的任何可選組件。 如果 optinal 組件沒有任何參數,那么它將永遠不會被包括。
給定以下路由:
app:match("user_page", "/user/:username(/:page)(.:format)", function(self) -- ... end)
可以生成以下 URL:
-- returns: /user/leafo self:url_for("user_page", { username = "leafo" }) -- returns: /user/leafo/projects self:url_for("user_page", { username = "leafo", page = "projects" }) -- returns: /user/leafo.json self:url_for("user_page", { username = "leafo", format = "json" }) -- returns: /user/leafo/code.json self:url_for("user_page", { username = "leafo", page = "code", format = "json" })
如果路由包含了 splat,則可以通過名為 splat 的參數提供該值:
app:match("browse", "/browse(/*)", function(self) -- ... end)
-- returns: /browse self:url_for("browse") -- returns: /browse/games/recent self:url_for("browse", { splat = "games/recent" })將對象傳遞給 url_for
如果 name_or_obj 是一個 table ,那么在該 table 上調用 此table的url_params 方法,并將返回值傳遞給 url_for 。
url_params 方法接受請求對象作為參數,其次是任何傳遞給 url_for 的東西。
通常在 model 上實現 url_params,讓他們能夠定義它們代表的頁面。 例如,為User model定義了一個 url_params 方法,該方法轉到用戶的配置文件頁面:
local Users = Model:extend("users", { url_params = function(self, req, ...) return "user_profile", { id = self.id }, ... end })
我們現在可以將User實例直接傳遞給 url_for,并返回 user_profile 路徑的l路由:
local user = Users:find(100) self:url_for(user) -- could return: /user-profile/100
你可能會注意到我們將 ... 傳遞給 url_params方法返回值。 這允許第三個 query_params 參數仍然起作用:
local user = Users:find(1) self:url_for(user, { page = "likes" }) -- could return: /user-profile/100?page=likes使用 url_key 方法
如果 params 中參數的值是一個字符串,那么它會被直接插入到生成的路徑中。 如果它的值是一個 table,那么將在此 table 上面調用url_key 方法,并將此方法的返回值插入到路徑中。
例如,我們為 User 模型定義一個我們的 url_key 方法:
local Users = Model:extend("users", { url_key = function(self, route_name) return self.id end })
如果我們想生成一個user_profile文件的路徑,我們通常可以這樣寫:
local user = Users:find(1) self:url_for("user_profile", {id = user.id})
我們定義的 url_key 方法讓我們直接傳遞 User 對象作為 id 參數,它將被轉換為 id :
local user = Users:find(1) self:url_for("user_profile", {id = user})
url_key 方法將路由的名稱作為第一個參數,因此我們可以根據正在處理的路由更改我們返回的內容。
build_url(path,[options])依據 path 構建一個絕對 URL 。 當前請求的URIb被用于構建URL。
例如,如果我們在 localhost:8080 上運行我們的服務器:
self:build_url() --> http://localhost:8080 self:build_url("hello") --> http://localhost:8080/hello渲染選項
每當寫一個表時,鍵/值對(對于是字符串的鍵)被復制到 self.options。 例如,在以下操作中,將復制render和 status 屬性。 在請求處理的生命周期結束時使用options表來創建適當的響應。
app:match("/", function(self) return { render = "error", status = 404} end)
以下是可以寫入的 options的字段列表
status 設置 http 狀態碼 (eg. 200,404,500)
render 導致一個視圖被請求渲染。 如果值為 true,則使用路由的名稱作為視圖名稱。 否則,該值必須是字符串或視圖類。
content_type 設置Content-type頭
header 要添加到響應的響應頭
json 導致此請求返回 JSON encode的值。 content-type被設置為 application / json 。
layout 更改app默認定義layout
redirect_to 將狀態碼設置為 302,并設置Location頭。 支持相對和絕對URL。 (結合status執行 301 重定向)
當渲染 JSON 時,確保使用 json 渲染選項。 它將自動設置正確的content-type并禁用 layout:
app:match("/hello", function(self) return { json = { hello = "world" } } end)應用程序回調
應用程序回調是一種特殊方法,它可以在需要處理某些類型的請求時調用。可以被應用程序覆蓋, 雖然它們是存儲在應用程序上的函數,但它們被稱為是常規操作,這意味著函數的第一個參數是請求對象的實例。
默認操作當請求與您定義的任何路由不匹配時,它將運行默認處理函數。 Lapis附帶了一個默認操作,預定義如下:
app.default_route = function(self) -- strip trailing / if self.req.parsed_url.path:match("./$") then local stripped = self.req.parsed_url:match("^(.+)/+$") return { redirect_to = self:build_url(stripped, { status = 301, query = self.req.parsed_url.query, }) } else self.app.handle_404(self) end end
如果它注意到URL尾部跟隨 一個/,它將嘗試重定向到尾部沒有/的版本。 否則它將調用app上的handle_404方法。
這個方法default_route只是 app 的一個普通方法。 你可以覆蓋它來做任何你喜歡的。 例如,添加個日志記錄:
app.default_route = function(self) ngx.log(ngx.NOTICE, "User hit unknown path " .. self.req.parsed_url.path) -- call the original implementaiton to preserve the functionality it provides return lapis.Application.default_route(self) end
你會注意到在default_route的預定義版本中,另一個方法handle_404被引用。 這也是預定義的,如下所示:
app.handle_404 = function(self) error("Failed to find route: " .. self.req.cmd_url) end
這將在每個無效請求上觸發 500 錯誤和 stack trance。 如果你想做一個 404 頁面,這b便是你能實現的地方。
覆蓋handle_404方法而不是default_route允許我們創建一個自定義的404頁面,同時仍然保留上面的尾部/刪除代碼。
這里有一個簡單的404處理程序,只打印文本Not Found!
app.handle_404 = function(self) return { status = 404, layout = false, "Not Found!" } end錯誤處理
Lapis 執行的每個處理函數都被 xpcall 包裝。 這確保可以捕獲到致命錯誤,并且可以生成有意義的錯誤頁面,而不是 Nginx默認錯誤信息。
錯誤處理程序應該僅用于捕獲致命和意外錯誤,預期錯誤在[異常處理指南]()中討論
Lapis 自帶一個預定義的錯誤處理程序,提取錯誤信息并渲染模板 lapis.views.error。 此錯誤頁面包含報錯的堆棧和錯誤消息。
如果你想有自己的錯誤處理邏輯,你可以重寫方法handle_error:
-- config.custom_error_page is made up for this example app.handle_error = function(self, err, trace) if config.custom_error_page then return { render = "my_custom_error_page" } else return lapis.Application.handle_error(self, err, trace) end end
傳遞給錯誤處理程序的請求對象或 self 不是失敗了的請求創建的請求對象。 Lapis 提供了一個新的,因為之前的可能已經寫入失敗了。
您可以使用self.original_request訪問原始請求對象
Lapis 的默認錯誤頁面顯示整個錯誤堆棧,因此在生產環境中建議將其替換自定義堆棧跟蹤,并在后臺記錄異常。
lapis-exceptions 模塊增加了錯誤處理程序以在數據庫中記錄錯誤。 它也可以當有異常時向您發送電子郵件。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/39495.html
摘要:入門是為和編寫的框架。使用來安裝創建一個應用命令行工具附帶了一個命令行工具,可幫助您創建新項目和啟動服務器。在生產環境中,應當啟用緩存以獲得最佳性能。指令指定一個代碼塊,它將處理與其他不匹配的任何請求。將忽略常規的二進制文件。 lapis入門 Lapis 是為 Lua 和 MoonScript 編寫的 Web 框架。 Lapis 很有趣,因為它建立在Nginx 發行的 OpenRest...
摘要:配置及環境被設計于依據不同環境載入不同的配置來運行服務器。環境名稱僅影響加載的配置。例如,這里有一個的配置塊編譯時,首先檢查環境變量。默認日志記錄位置設置為,在默認的配置中指定。 配置及環境 Lapis 被設計于依據不同環境載入不同的配置來運行服務器。例如,可能您開發環境的配置設置為本地數據庫的URL,禁用代碼緩存和單個worker。然后,您生產環境的配置可能設定為遠程數據庫的 URL...
摘要:使用創建應用程序生成一個新項目如果您尚未閱讀,請閱讀入門指南,了解有關創建新項目骨架的信息以及,配置和命令的詳細信息。是包含應用程序的常規模塊。 使用Lua創建Lapis應用程序 生成一個新項目 如果您尚未閱讀,請閱讀入門指南,了解有關創建新項目骨架的信息以及OpenResty,Nginx配置和lapis命令的詳細信息。 您可以在當前目錄中通過運行以下命令啟動一個新的Lua項目: la...
摘要:的異常處理錯誤的種類區分兩種錯誤可恢復和不可恢復錯誤。捕獲可恢復的錯誤幫助程序用于包裝一個操作,以便它可以捕獲錯誤并運行錯誤處理程序。相反,使用協同程序創建一個異常處理系統。 lapis的異常處理 錯誤的種類 Lapis 區分兩種錯誤:可恢復和不可恢復錯誤。 Lua 的運行時在執行期間拋出的錯誤或調用錯誤被認為是不可恢復的。 (這也包括 Lua 內置函數 assert ) 因為不可恢復...
摘要:配置語法配置示例的配置模塊提供了對遞歸合并的支持。例如,我們可以定義一個基本配置,然后覆蓋更多具體的配置聲明中的一些值這將產生以下兩個配置結果默認值省略您可以在相同的配置名稱上調用函數多次,每次將傳入的表合并到配置中。 Lua 配置語法 配置示例 Lapis 的配置模塊提供了對遞歸合并 table 的支持。 例如,我們可以定義一個基本配置,然后覆蓋更多具體的配置聲明中的一些值: --...
閱讀 2034·2023-04-26 00:16
閱讀 3486·2021-11-15 11:38
閱讀 3177·2019-08-30 12:50
閱讀 3188·2019-08-29 13:59
閱讀 759·2019-08-29 13:54
閱讀 2509·2019-08-29 13:42
閱讀 3313·2019-08-26 11:45
閱讀 2195·2019-08-26 11:36