仆人客户端如何处理收到的 cookie?
How does a Servant client handle received cookies?
我想使用 Servant 客户端首先调用登录端点以获取 session cookie,然后向需要 cookie 身份验证的端点发出请求。
API 是(简化)
import qualified Servant as SV
import qualified Servant.Auth.Server as AS
import qualified Servant.Client as SC
-- Authentication and X-CSRF cookies
type CookieHeader = ( SV.Headers '[SV.Header "Set-Cookie" AS.SetCookie
, SV.Header "Set-Cookie" AS.SetCookie]
SV.NoContent )
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
type ProtectedEndpoint = "protected" :> SV.Get '[SV.JSON]
-- The overall API
type Api = LoginEndpoint :<|> (AS.Auth '[AS.Cookie, AS.JWT] User :> ProtectedEndpoint)
apiProxy :: Proxy Api
apiProxy = Proxy
我定义客户端如下:
loginClient :: Api.Login -> SC.ClientM Api.CookieHeader
protectedClient :: AC.Token -> SC.ClientM Text :<|> SC.ClientM SV.NoContent
loginClient :<|> protectedClient = SC.client Api.apiProxy
客户端如何处理身份验证cookie?我可以想到两种方法。在 ClientM
monad 中执行请求时,例如
do
result <- SC.runClientM (loginClient (Login "user" "password")) clientEnv
[..]
其中 Login
是登录请求 body 并且类型 Servant.Client.ClientEnv
, the cookie could part of the result
, it could be updated in the cookieJar
TVar inside clientEnv
, or both. I would assume the TVar to be updated, so that a subsequent request with the same clientEnv
would send the received cookies along. However, my attempt to read the TVar and inspect its contents using Network.HTTP.Client.destroyCookieJar
的 clientEnv
显示了一个空数组。这是故意的吗?我在文档中找不到任何内容。
因此,要进行经过身份验证的调用,我需要从 result
中的 header 中提取 cookie(如何?),更新 TVar,创建一个新的 clientEnv
引用此 TVar,并使用此新环境进行经过身份验证的调用。这确实是建议的程序吗?我问是因为我认为用例非常标准,应该有一个更简化的解决方案。有没有?我错过了什么吗?
经过一些实验,我发现 Servant 客户端确实在属于 clientEnv
的 cookieJar
中维护 cookie。更准确地说,clientEnv
包含字段 cookieJar
,其类型为 Maybe (TVar CookieJar)
。是client根据后续请求的Set-Cookie
指令更新的TVar。由开发人员在发出第一个请求之前创建和初始化该 TVar;否则,Servant 客户端将在请求之间丢弃 cookie。
另外,可以像请求一样获取cookies body。为此,必须将要检索的 cookie 定义为 API 类型的一部分,就像我最初问题的示例:
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
起初拆卸 returned 有点棘手,因为我需要弄清楚 Servant 的 type-level 机器产生的最终类型。最终,我做了以下事情:
SV.Headers resp h <- tryRequest clientEnv (loginClient (Api.Login "user" "pwd"))
let headers = SV.getHeaders h
其中 tryRequest
是执行 runClientM
和提取 Right
部分的助手。模式匹配 resp
包含 return 值(此处为 NoContent
),而 h
是不同 header 的 HList
。可以用Servant的getHeaders
函数转换成Network.HTTP.Types.Header
的正则列表
然后可以更改或生成新的 header 并通过向 cookieJar
TVar 添加新的 header 来提交新的请求(参见 cookie-manipulating functions 在 Network.HTTP.Client).
我想使用 Servant 客户端首先调用登录端点以获取 session cookie,然后向需要 cookie 身份验证的端点发出请求。
API 是(简化)
import qualified Servant as SV
import qualified Servant.Auth.Server as AS
import qualified Servant.Client as SC
-- Authentication and X-CSRF cookies
type CookieHeader = ( SV.Headers '[SV.Header "Set-Cookie" AS.SetCookie
, SV.Header "Set-Cookie" AS.SetCookie]
SV.NoContent )
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
type ProtectedEndpoint = "protected" :> SV.Get '[SV.JSON]
-- The overall API
type Api = LoginEndpoint :<|> (AS.Auth '[AS.Cookie, AS.JWT] User :> ProtectedEndpoint)
apiProxy :: Proxy Api
apiProxy = Proxy
我定义客户端如下:
loginClient :: Api.Login -> SC.ClientM Api.CookieHeader
protectedClient :: AC.Token -> SC.ClientM Text :<|> SC.ClientM SV.NoContent
loginClient :<|> protectedClient = SC.client Api.apiProxy
客户端如何处理身份验证cookie?我可以想到两种方法。在 ClientM
monad 中执行请求时,例如
do
result <- SC.runClientM (loginClient (Login "user" "password")) clientEnv
[..]
其中 Login
是登录请求 body 并且类型 Servant.Client.ClientEnv
, the cookie could part of the result
, it could be updated in the cookieJar
TVar inside clientEnv
, or both. I would assume the TVar to be updated, so that a subsequent request with the same clientEnv
would send the received cookies along. However, my attempt to read the TVar and inspect its contents using Network.HTTP.Client.destroyCookieJar
的 clientEnv
显示了一个空数组。这是故意的吗?我在文档中找不到任何内容。
因此,要进行经过身份验证的调用,我需要从 result
中的 header 中提取 cookie(如何?),更新 TVar,创建一个新的 clientEnv
引用此 TVar,并使用此新环境进行经过身份验证的调用。这确实是建议的程序吗?我问是因为我认为用例非常标准,应该有一个更简化的解决方案。有没有?我错过了什么吗?
经过一些实验,我发现 Servant 客户端确实在属于 clientEnv
的 cookieJar
中维护 cookie。更准确地说,clientEnv
包含字段 cookieJar
,其类型为 Maybe (TVar CookieJar)
。是client根据后续请求的Set-Cookie
指令更新的TVar。由开发人员在发出第一个请求之前创建和初始化该 TVar;否则,Servant 客户端将在请求之间丢弃 cookie。
另外,可以像请求一样获取cookies body。为此,必须将要检索的 cookie 定义为 API 类型的一部分,就像我最初问题的示例:
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
起初拆卸 returned 有点棘手,因为我需要弄清楚 Servant 的 type-level 机器产生的最终类型。最终,我做了以下事情:
SV.Headers resp h <- tryRequest clientEnv (loginClient (Api.Login "user" "pwd"))
let headers = SV.getHeaders h
其中 tryRequest
是执行 runClientM
和提取 Right
部分的助手。模式匹配 resp
包含 return 值(此处为 NoContent
),而 h
是不同 header 的 HList
。可以用Servant的getHeaders
函数转换成Network.HTTP.Types.Header
的正则列表
然后可以更改或生成新的 header 并通过向 cookieJar
TVar 添加新的 header 来提交新的请求(参见 cookie-manipulating functions 在 Network.HTTP.Client).