【问题标题】:Adding response header in Servant在Servant中添加响应头
【发布时间】:2016-02-08 04:35:22
【问题描述】:

我试图弄清楚如何在 Servant 中添加CORS 响应标头(基本上,设置响应标头“Access-Control-Allow-Origin: *”)。我在下面用addHeader 函数写了一个小测试用例,但它出错了。对于找出以下错误的帮助,我将不胜感激。

代码:

{-# LANGUAGE CPP           #-}
{-# LANGUAGE DataKinds     #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeFamilies  #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Data.Aeson
import GHC.Generics
import Network.Wai
import Servant
import Network.Wai.Handler.Warp (run)
import Control.Monad.Trans.Either
import Control.Monad.IO.Class (liftIO)
import Control.Monad (when, (<$!>))
import Data.Text as T
import Data.Configurator as C
import Data.Maybe
import System.Exit (exitFailure)

data User = User
  { name              :: T.Text
  , password          :: T.Text
  } deriving (Eq, Show, Generic)

instance ToJSON User
instance FromJSON User

type Token = T.Text

type UserAPI = "grant" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[Header "Access-Control-Allow-Origin" T.Text] Token)

userAPI :: Proxy UserAPI
userAPI = Proxy

authUser :: User -> Bool
authUser u = case (password u) of
    "somepass" -> True
    _     -> False

server :: Server UserAPI
server = users  
  where users :: User -> EitherT ServantErr IO Token
        users u = case (authUser u) of
          True -> return $ addHeader "*" $ ("ok" :: Token)
          False -> return $ addHeader "*" $ ("notok" :: Token)

app ::  Application
app  = serve userAPI server

main :: IO ()
main = run 8081 app

这是我得到的错误:

src/Test.hs:43:10:
    Couldn't match type ‘Headers
                           '[Header "Access-Control-Allow-Origin" Text] Text’
                   with ‘Text’
    Expected type: Server UserAPI
      Actual type: User -> EitherT ServantErr IO Token
    In the expression: users
    In an equation for ‘server’:
        server
          = users
          where
              users :: User -> EitherT ServantErr IO Token
              users u
                = case (authUser u) of {
                    True -> return $ addHeader "*" $ ("something" :: Token)
                    False -> return $ addHeader "*" $ ("something" :: Token) }

src/Test.hs:46:28:
    Couldn't match type ‘Text’ with ‘Headers '[Header h v0] Text’
    In the expression: addHeader "*"
    In the second argument of ‘($)’, namely
      ‘addHeader "*" $ ("something" :: Token)’
    In the expression: return $ addHeader "*" $ ("something" :: Token)

src/Test.hs:47:29:
    Couldn't match type ‘Text’ with ‘Headers '[Header h1 v1] Text’
    In the expression: addHeader "*"
    In the second argument of ‘($)’, namely
      ‘addHeader "*" $ ("something" :: Token)’
    In the expression: return $ addHeader "*" $ ("something" :: Token)

我有一个工作版本,它的 API 更简单(简单的GET)。但是,对于上述类型的UserAPI,它会出错。 addHeader 函数类型似乎与我认为的类型签名一致。我肯定在这里遗漏了一些东西,否则它不会像这样出错。

【问题讨论】:

    标签: haskell servant


    【解决方案1】:

    我认为将 CORS 标头添加到响应中的最简单方法是在服务端之上使用中间件。 wai-cors 很容易:

    import Network.Wai.Middleware.Cors
    
    [...]
    
    app ::  Application
    app  = simpleCors (serve userAPI server)
    

    对于您的实际响应,我想您需要使用addHeaderText 类型的值转换为Headers '[Header "Access-Control-Allow-Origin" T.Text 类型的值。

    【讨论】:

    • @majdar,非常有用的指针。这很可能是我将采取的路线。在你指出之前我不知道那个有用的库
    • 感谢您的回答发现 wai-corssimpleCors 正是我现在需要的!
    【解决方案2】:

    madjar 已经提出了这个建议,但要对其进行扩展:addHeader 更改了返回类型:

    x :: Int
    x = 5
    
    y :: Headers '[Header "SomeHeader" String] Int
    y = addHeader "headerVal" y
    

    在您的情况下,这意味着您必须更新 users where binding 的类型以返回 Either ServantErr IO (Headers '[Header "Access-Control-Allow-Origin" T.Text] Token

    更一般地说,您可以在 ghci 中使用 :kind! Server UserAPI 来查看类型同义词扩展为什么 - 这通常对仆人有帮助!

    【讨论】:

    • 啊哈,很有启发性。谢谢!我很困惑为什么添加标题时类型不会改变。正如您所指出的,它们确实发生了变化。
    • 在使用servant-client时可以用来从GET请求中检索“Location”标头吗?
    • @user239558 是的 - 只需在响应中使用getHeadersgetHeadersHList(来自here
    • 请注意,上面应该是y = addHeader "headerVal" x,但由于编辑必须有六个字符,因此无法提交此更改。
    猜你喜欢
    • 2017-04-30
    • 2020-01-10
    • 2014-07-30
    • 2016-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    相关资源
    最近更新 更多