假設我正在運行一個具有兩個端點的 Servant 網路服務器,其型別如下所示:
type BookAPI =
"books" :> Get '[JSON] (Map Text Text)
:<|> "book" :> Capture "Name" Text :> ReqBody '[JSON] (Text) :> Post '[JSON] (Text)
λ:T.putStrLn $ layout (Proxy :: Proxy BookAPI)
/
├─ book/
│ └─ <capture>/
│ └─?
└─ books/
└─?
我可能想使用Network.Wai.Middleware.Prometheus 的 instrumentHandlerValue 之類的東西來生成 Prometheus 指標,每次呼叫此 API 時都會觸發該指標,并將處理程式值設定為請求的路徑。
但是,如果我執行以下操作:
prometheusMiddlware = instrumentHandlerValue (T.intercalate "\\" . pathInfo)
這很糟糕,因為對book/<Name>端點的不同請求,例如book/great-expectations和book/vanity-fair導致不同的標簽,如果書籍數量很少,這很好,但是如果它非常大,那么這些指標使用的資料量非常大,并且要么我的服務失敗了,或者我的監控費用變得非常大。
我非常想要一個函式,它接受一個仆人 API 和一個 Wai 請求,如果匹配,則以每個端點相同的形式回傳一個段串列。
即請求/books將回傳Just ["books"],請求/book/little-dorrit將回傳Just ["book", "Name"],請求/films將回傳Nothing。
我有種看你會如何通過模式匹配在寫這Router'從Servant.Server.Internal.Router,但目前還不清楚,我認為,為了做到這一點靠的內包裝是一個好主意。
有沒有更好的辦法?
uj5u.com熱心網友回復:
該pathInfo函式回傳 a 的所有路徑段Request。也許我們可以定義一個型別類,給定一個仆人 API,為段串列生成一個“決議器”,其結果將是串列的格式化版本。
決議器型別可能類似于:
import Data.Text
import Control.Monad.State.Strict
import Control.Applicative
type PathParser = StateT ([Text],[Text]) Maybe ()
其中[Text]狀態中的第一個是尚未決議的路徑段,第二個是我們迄今為止積累的格式化路徑段。
這種型別有一個Alternative實體,其中失敗丟棄狀態(基本上是回溯)和一個在-blocks內的模式匹配失敗時MonadFail回傳的實體。mzerodo
型別類:
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Data ( Proxy )
import GHC.TypeLits
class HasPathParser (x :: k) where
pathParser :: Proxy x -> PathParser
將Symbol路徑片段從待處理串列移動到已處理串列的實體:
instance KnownSymbol piece => HasPathParser (piece :: Symbol) where
pathParser _ = do
(piece : rest, found) <- get -- we are using MonadFail here
guard (piece == Data.Text.pack (symbolVal (Proxy @piece)))
put (rest, piece : found)
for 的實體Capture將路徑變數的名稱——而不是值——放在已處理的串列中:
instance KnownSymbol name => HasPathParser (Capture name x) where
pathParser _ = do
(_ : rest, found) <- get -- we are using MonadFail here
put (rest, Data.Text.pack (symbolVal (Proxy @name)) : found)
當我們到達Verb( GET, POST...) 時,我們要求不應保留任何掛起的路徑片段:
instance HasPathParser (Verb method statusCode contextTypes a) where
pathParser _ = do
([], found) <- get -- we are using MonadFail here
put ([], found)
其他一些情況:
instance HasPathParser (ReqBody x y) where
pathParser _ = pure ()
instance (HasPathParser a, HasPathParser b) => HasPathParser (a :> b) where
pathParser _ = pathParser (Proxy @a) *> pathParser (Proxy @b)
instance (HasPathParser a, HasPathParser b) => HasPathParser (a :<|> b) where
pathParser _ = pathParser (Proxy @a) <|> pathParser (Proxy @b)
讓它作業:
main :: IO ()
main = do
do let Just ([], result) = execStateT (pathParser (Proxy @BookAPI)) (["books"],[])
print result
-- ["books"]
do let Just ([], result) = execStateT (pathParser (Proxy @BookAPI)) (["book", "somebookid"],[])
print result
-- ["Name","book"]
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/404606.html
標籤:
