在Servant中添加响应头

时间:2016-02-08 04:35:23

标签: haskell servant

我试图弄清楚如何在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函数类型似乎与我想到的类型签名一致。我肯定在这里遗漏了一些东西,或者不会像这样错误。

2 个答案:

答案 0 :(得分:11)

我认为将CORS标头添加到响应的最简单方法是在servant之上使用中间件。 wai-cors让它变得非常简单:

import Network.Wai.Middleware.Cors

[...]

app ::  Application
app  = simpleCors (serve userAPI server)

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

答案 1 :(得分:5)

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

x :: Int
x = 5

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

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

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