Haskell Servant从处理程序获取当前路由/URL

Haskell Servant从处理程序获取当前路由/URL,haskell,request,servant,Haskell,Request,Servant,我想获取与我的处理程序对应的当前路径。以下是我的服务器模型,仅供参考: type ServerAPI = "route01" :> Get '[HTML] Text :<|> "route02" :> "subroute" :> Get '[HTML] Text :<|> "route03" :> Get '[HTML] Text [编辑]我刚刚注意到我正在查看旧版本servant的api,这不再有效。新的r

我想获取与我的处理程序对应的当前路径。以下是我的服务器模型,仅供参考:

type ServerAPI = 
         "route01" :> Get '[HTML] Text
    :<|> "route02" :> "subroute" :> Get '[HTML] Text
    :<|> "route03" :> Get '[HTML] Text

[编辑]我刚刚注意到我正在查看旧版本servant的api,这不再有效。新的
route
具有类型签名
route::Proxy-api->Context-Context->Delayed-env(Server-api)->Router-env
,我真的看不到从这里获取
请求的方法


而不是将
route1和2handler
类型签名设置为
Request->Handler Text
,但我在尝试创建
HasServer
实例时遇到此错误:

`Server' is not a (visible) associated type of class `HasServer'

最后,我要指出的是,我的最终目标是从
处理程序中获取当前路由,在数据库中为路由添加访问计数只是为了举例。我不想用更好的方法来计算访问次数或类似的事情。

我不知道如何自动完成,但可以使用该功能“手动”完成

这个想法是,如果你有一个API

type ServerAPI = 
        "route01" :> Get '[HTML] Text
   :<|> "route02" :> "subroute" :> Get '[HTML] Text
   :<|> Route3

type Route3 = "route03" :> Get '[HTML] Text
如果路由具有参数,则还必须传递处理程序获取的参数。例如:

type ServerAPI =
       ...
   :<|> Route4

type Route4 = "route04" :> Capture "cap" Int :> Get '[JSON] Text
type ServerAPI = 
         "route01" :> Get '[JSON] Text
    :<|> "route02" :> "subroute" :> Get '[JSON] Text
    :<|> "route03" :> Get '[JSON] Text

route1and2Handler :: String -> Handler Text
route1and2Handler route = do
    -- addVisitCountForRouteToDatabaseOrSomethingOfThatSort...
    return (pack route)

route3Handler :: Handler Text
route3Handler = return "Hello, I'm route 03"

server :: Server ServerAPI
server = route1and2Handler "route01" :<|> route1and2Handler "route02" :<|> route3Handler


您必须对每个路由执行此操作。

当您向处理程序添加“路由”参数时,您可以在组合服务器的处理程序时通过函数应用程序设置参数值

根据您的示例:

type ServerAPI =
       ...
   :<|> Route4

type Route4 = "route04" :> Capture "cap" Int :> Get '[JSON] Text
type ServerAPI = 
         "route01" :> Get '[JSON] Text
    :<|> "route02" :> "subroute" :> Get '[JSON] Text
    :<|> "route03" :> Get '[JSON] Text

route1and2Handler :: String -> Handler Text
route1and2Handler route = do
    -- addVisitCountForRouteToDatabaseOrSomethingOfThatSort...
    return (pack route)

route3Handler :: Handler Text
route3Handler = return "Hello, I'm route 03"

server :: Server ServerAPI
server = route1and2Handler "route01" :<|> route1and2Handler "route02" :<|> route3Handler

其中有两个问题:

  • 如何获取当前请求或URL
  • 如何获得当前的“路线”
  • 请注意,URL(例如
    /route12/42
    )与路由不同 (例如,`route12:>Capture“id”Int:>Get'[JSON]Int)。 让我们看看如何解决这两个问题 简短语言pragma和导入部分

    {-#语言约束种类}
    {-#语言数据类型}
    {-#派生通用语言}
    {-#语言灵活语境#-}
    {-#语言灵活实例}
    {-#语言MultiParamTypeClasses}
    {-#语言重载字符串}
    {-#语言等级}
    {-#语言范围类型变量#-}
    {-#语言类型族{-}
    {-#语言类型运算符{-}
    {-#语言不可判定实例}
    {-#语言不可判定超类}
    {-#选项{GHC-Wno孤儿}
    模块主要在哪里
    导入数据。可能(来自可能)
    导入控制.Monad.IO.Class(liftIO)
    导入System.Environment(getArgs)
    进口GHC.仿制药(到,从,M1(..),K1(..,(:*:)(..)
    --用于“不安全”vault密钥创建
    导入System.IO.Unsafe(unsafePerformIO)
    将符合条件的Data.ByteString.Char8导入为BS8
    将限定的Data.Vault.Lazy导入为V
    导入合格网络。Wai为Wai
    导入合格的Network.Wai.Handler.Warp作为Warp
    进口佣人
    导入Servant.API.Generic
    导入Servant.Server.Generic
    导入Servant.Server.Internal.RoutingApplication(passToServer)
    
    如何获取当前的
    请求
    对象或URL 将当前的
    WAI
    请求
    传递给处理程序实际上相当简单。 这是“懒惰”的方法,我们在请求中要求“一切”, 我们必须小心处理程序(例如,我们不能触摸
    requestBody
    )。 这个“组合器”还将实现与
    wai
    服务器实现联系起来, 这是一个实现细节 (除了
    Raw
    之外,
    servant服务器中没有任何其他内容公开
    wai
    内部结构)

    其思想是使
    服务器(Wai.Request:>api)=Wai.Request->Server-api
    。 如果我们想象一下,我们有这样的功能, 我们可以使用
    Servant.API.Generic
    (参见“使用泛型”食谱)编写:

    数据路由1路由=路由1
    {route11::route:-Wai.Request:>“route1”:>获取'[JSON]Int
    ,route12::route:-Wai.Request:>route2:>Capture“id”Int:>Get'[JSON]Int
    }
    派生(通用)
    路由1::路由1服务器
    路由1=路由1
    {route11=\req->liftIO$do
    设p=Wai.rawPathInfo请求
    BS8.putStrLn p
    返回(BS8.p)
    ,route12=\req i->liftIO$do
    设p=Wai.rawPathInfo请求
    BS8.putStrLn p
    返回(成功i)
    }
    app1::应用程序
    app1=通用服务路由1
    
    我们定义一个
    Routes1
    数据类型,实现
    routes1asserver
    值并将其转换为 进入
    wai
    应用程序
    。然而,要编译这个示例,我们需要一个 附加实例。我们在
    路由的实现

    instance HasServer api ctx=>HasServer(Wai.Request:>api)ctx其中
    键入ServerT(Wai.Request:>api)m=Wai.Request->servertapi m
    带上下文的提升服务器\uPC nt s=
    提升服务器WithContext(代理::代理api)pc nt。s
    路由u-ctx d=路由(代理::代理api)ctx$
    passToServer d id
    
    这个解决方案是一个很好的快速修复方案,但可以说还有更好的方法

    特定组合子 我们可能会注意到,我们的两个处理程序都使用
    Wai.rawPathInto-req
    调用。 这应该提醒我们。特定的组合器更优雅。 能够在核心框架之外创建新的组合器, 是
    仆人
    的设计原则之一

    数据RawPathInfo
    实例HasServer api ctx=>HasServer(RawPathInfo:>api)ctx,其中
    键入ServerT(RawPathInfo:>api)m=BS8.ByteString->servertapi m
    带上下文的提升服务器\uPC nt s=
    提升服务器WithContext(代理::代理api)pc nt。s
    路由u-ctx d=路由(代理::代理api)ctx$
    passToServer d Wai.rawPathInfo
    
    使用新的
    RawPathInfo
    combinator,我们可以重新实现我们的应用程序:

    数据路由2路由=路由2
    {route21::rout
    
    type ServerAPI =
           ...
       :<|> Route4
    
    type Route4 = "route04" :> Capture "cap" Int :> Get '[JSON] Text
    
    ghci> :set -XKindSignatures -XDataKinds -XTypeOperators -XTypeFamilies
    ghci> :type safeLink (Proxy::Proxy ServerAPI) (Proxy::Proxy Route4)
    Int -> URI
    
    type ServerAPI = 
             "route01" :> Get '[JSON] Text
        :<|> "route02" :> "subroute" :> Get '[JSON] Text
        :<|> "route03" :> Get '[JSON] Text
    
    route1and2Handler :: String -> Handler Text
    route1and2Handler route = do
        -- addVisitCountForRouteToDatabaseOrSomethingOfThatSort...
        return (pack route)
    
    route3Handler :: Handler Text
    route3Handler = return "Hello, I'm route 03"
    
    server :: Server ServerAPI
    server = route1and2Handler "route01" :<|> route1and2Handler "route02" :<|> route3Handler
    
    import Network.Wai.Middleware.RequestLogger (logStdoutDev)
    
    ...
    
    app :: Application
    app = serve serverAPI server
    
    main :: IO ()
    main = run 8081 $ logStdoutDev app