使用 Servant / Wai 提供静态文件

Serving Static Files With Servant / Wai

我正在按照本教程 http://www.parsonsmatt.org/programming/2015/06/07/servant-persistent.html 通过 servant 创建 API。我也想自定义服务器以提供静态文件,但找不到方法。

我正在使用 stack 构建工具。

我修改了 Main.hs 文件的 运行 包括 static (run port $ static $ logger $ app cfg) 并导入了 Network.Wai.Middleware.Static (static)。我还将 wai-middleware-static >=0.7.0 && < 0.71 添加到我的 cabal 文件中。

当我 运行 stack build 我得到: 更新:这部分完全是我的错误。我将包添加到错误的 cabal 文件中.. 蹩脚。导入 Network.Wai.Middleware.Static 工作并提供静态文件。留下下面的错误以防有人搜索它并发现它有用。)

Could not find module ‘Network.Wai.Middleware.Static’
Perhaps you meant
  Network.Wai.Middleware.Gzip (from wai-extra-3.0.7.1@waiex_GpotceEdscHD6hq9p0wPOJ)
  Network.Wai.Middleware.Jsonp (from wai-extra-3.0.7.1@waiex_GpotceEdscHD6hq9p0wPOJ)
  Network.Wai.Middleware.Local (from wai-extra-3.0.7.1@waiex_GpotceEdscHD6hq9p0wPOJ)

接下来我尝试使用 servant 的 serveDirectory 如下(简化):

type  API = "users" :> Get   '[JSON]   [Person]
            :<|> "static" :> Raw
server = createPerson :<|> serveDirectory "/static" 

我收到这个错误:

Couldn't match type ‘IO’ with ‘EitherT ServantErr IO’
arising from a functional dependency between:
  constraint ‘Servant.Server.Internal.Enter.Enter
                (IO Network.Wai.Internal.ResponseReceived)
                (AppM :~> EitherT ServantErr IO)
                (IO Network.Wai.Internal.ResponseReceived)’
    arising from a use of ‘enter’
  instance ‘Servant.Server.Internal.Enter.Enter
              (m a) (m :~> n) (n a)’
    at <no location info>
In the expression: enter (readerToEither cfg) server
In an equation for ‘readerServer’:
    readerServer cfg = enter (readerToEither cfg) server

我是 Haskell 初学者,我不熟悉 Wai,所以不确定从哪里开始。我需要做哪些更改才能使博客中的示例代码 post 提供静态文件?

编辑:由于默认视图中隐藏了评论,因此我将最后一条评论粘贴到此处:

这里是马特 code 博客中的精简版。我将他的所有模块合并到一个文件中,删除了所有数据库内容,但没有清理 extensions/imports。当我 运行 此代码时,出现上述类型不匹配错误。请注意,此代码不使用 Network.Wai.Middleware.Static,我正在使用合格的 Servant StaticFiles 导入。

in the relevant section of servant's tutorial 所述,enter 的整个处理是让您的请求处理程序使用一些 monad m(在您的情况下是一些 ReaderT monad)并提供一种方法将 m 中的计算转换为 servant 的标准 EitherT ServantErr IO monad.

中的计算

不过这里的问题是您在 ReaderT 中定义了一堆请求处理程序,另外一个用于提供静态文件,并调用 enter在所有这些。 ReaderT 处理程序转换为 EitherT ... 处理程序就好了,但是 enter 尝试将 serveDirectory 调用从 ReaderT ... 转换为 EitherT ...。这当然不会很快发生,因为 serveDirectory 不是 ReaderT ... 开始的计算!

servant 可以说让 serveDirectory 独自一人 -- 在这一点上我对我们是否应该这样做没有明确的意见,或者如果最好将文件服务处理程序单独粘贴到在所有其他端点上调用 enter 的结果。这是它的样子(查找 -- NEW 以查看更改):

type PersonAPI = 
    "users" :> Capture "name" String :> Get '[JSON] Person
   -- NEW: removed Raw from here

-- NEW
type WholeAPI = PersonAPI :<|> Raw

type AppM = ReaderT Config (EitherT ServantErr IO)

userAPI :: Proxy PersonAPI
userAPI = Proxy

-- NEW
wholeAPI :: Proxy WholeAPI
wholeAPI = Proxy

-- NEW: changed 'userAPI' to 'wholeAPI'
app :: Config -> Application
app cfg = serve wholeAPI (readerServer cfg)

readerServer :: Config -> Server WholeAPI
readerServer cfg = enter (readerToEither cfg) server
              :<|> S.serveDirectory "/static" -- NEW

readerToEither :: Config -> AppM :~> EitherT ServantErr IO
readerToEither cfg = Nat $ \x -> runReaderT x cfg

server :: ServerT PersonAPI AppM
server = singlePerson

singlePerson :: String -> AppM Person
singlePerson str = do
    let person = Person { name = "Joe", email = "joe@example.com" }
    return person

无论如何我已经把这个话题提请其他仆人开发者注意了,谢谢!到目前为止,我们还没有真正考虑过 enterserveDirectory 之间的交互(好吧,我没有)。