8

我试图弄清楚如何CORS在 Servant 中添加响应标头(基本上,设置响应标头“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)

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

4

2 回答 2

14

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

import Network.Wai.Middleware.Cors

[...]

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

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

于 2016-02-08T08:24:31.287 回答
5

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

x :: Int
x = 5

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

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

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

于 2016-02-08T11:30:21.363 回答