7

我正在按照本教程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 不熟悉,所以甚至不确定从哪里开始。我需要对博客文章中的示例代码进行哪些更改才能提供静态文件?

编辑:由于评论从默认视图中隐藏,我将我的最后一条评论粘贴在这里:

这是他博客中马特代码的低调版本。我将他的所有模块合并到一个文件中,删除了所有数据库内容,但没有清理扩展/导入。当我运行此代码时,我收到上述类型不匹配错误。请注意,此代码不使用 Network.Wai.Middleware.Static,而我使用的是符合条件的 Servant StaticFiles 导入。

4

1 回答 1

8

仆人教程的相关部分所述,整个处理enter是让您的请求处理程序使用一些单子m(在您的情况下是一些ReaderT单子),并提供一种将计算转换为服务人标准单子m中的计算的方法。EitherT ServantErr IO

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

可以说,仆人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

无论如何,我已经将这个话题引起了其他仆人开发人员的注意,谢谢!enter到目前为止,我们还没有真正考虑过和之间的相互作用serveDirectory(嗯,我没有)。

于 2015-06-28T10:50:02.730 回答