Haskell 使用Servant/Wai服务静态文件

Haskell 使用Servant/Wai服务静态文件,haskell,haskell-wai,servant,Haskell,Haskell Wai,Servant,我遵循本教程通过servant创建API。我想自定义服务器以提供静态文件,但找不到方法 我正在使用堆栈构建工具 我修改了Main.hs文件的运行以包括static(run port$static$logger$app cfg),并导入了Network.Wai.Middleware.static(static)。我还将wai中间件static>=0.7.0&Get'[JSON][Person] :“静态”:>原始 server=createPerson:serveDirectory/“静态” 我

我遵循本教程通过servant创建API。我想自定义服务器以提供静态文件,但找不到方法

我正在使用
堆栈
构建工具

我修改了
Main.hs
文件的运行以包括
static
run port$static$logger$app cfg
),并导入了
Network.Wai.Middleware.static(static)
。我还将
wai中间件static>=0.7.0&<0.71
添加到我的阴谋集团文件中

当我运行
stack build
时,我得到:(更新:这部分完全是我的错误。我将包添加到错误的cabal文件中..lame.Importing Network.Wai.Middleware.Static工作并服务于静态文件。将错误保留在下面,以防任何人搜索它并发现它有用。)

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

typeapi=“users”:>Get'[JSON][Person]
:“静态”:>原始
server=createPerson:serveDirectory/“静态”
我得到这个错误:

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
无法将类型“IO”与“EitherT ServantErr IO”匹配
由以下两者之间的功能依赖性引起:
约束“Servant.Server.Internal.Enter.Enter”
(IO网络、Wai、内部、响应接收)
(附件:~>EitherT ServantErr IO)
(IO网络.外部.内部.响应已接收)'
因使用“回车”而产生
实例“Servant.Server.Internal.Enter.Enter”
(ma)(m:~>n)(na)"
在
在表达式中:输入(readerToEither cfg)server
在“readerServer”的方程式中:
readerServer cfg=输入(readerToEither cfg)服务器
我是Haskell初学者,我对Wai不太熟悉,所以不确定从哪里开始。我需要做哪些更改才能使博客文章中的示例代码服务于静态文件

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

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

谢谢

如前所述,
enter
的整个过程就是让您的请求处理程序使用一些monad
m
(在您的例子中是一些
ReaderT
monad),并提供一种将
m
中的计算转换为服务标准
EitherT ServantErr IO
monad中的计算的方法

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

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

type PersonAPI=
“用户”:>捕获“名称”字符串:>获取“[JSON]Person
--新的:从这里原始删除
--新的
类型WholeAPI=PersonAPI:Raw
类型AppM=ReaderT Config(EitherT ServantErr IO)
userAPI::代理角色pi
userAPI=Proxy
--新的
wholeAPI::代理wholeAPI
wholeAPI=代理
--新增:将“userAPI”更改为“wholeAPI”
app::Config->Application
app cfg=SERVICE wholeAPI(读服务器cfg)
readerServer::Config->Server WholeAPI
readerServer cfg=输入(readerToEither cfg)服务器
:S.serveDirectory“/静态”--新
readerToEither::Config->AppM:~>EitherT ServantErr IO
readerToEither cfg=Nat$\x->runReaderT x cfg
服务器::ServerT PersonAPI AppM
服务器=单人
singlePerson::String->AppM Person
单人str=do
让person=person{name=“Joe”,email=”joe@example.com" }
返回人

不管怎样,我已经把这个话题引起了其他服务开发人员的注意,谢谢!到目前为止,我们还没有真正考虑到
enter
serveDirectory
之间的交互(我没有考虑)。

对于第一个问题,我认为您需要将wai应用程序静态添加到.cabal文件中的构建中。谢谢,Michael。事实上,我把包裹放错了阴谋集团的档案里。所以,静态工作很好。我正在玩链接博客文章中的代码,我注意到它链接了中间件,所以决定使用静态中间件。我可能要花更长的时间才能弄清楚如何在这种情况下使用
wai-app-static
。@Ecognium你看过仆人教程吗?@Ecognium
enter
机器将你的处理程序从一个单子转换成另一个单子。基于
Reader
的服务器中有一个
Raw
用于文件服务,因此
enter
尝试将文件服务内容从
ReaderT…
转换为
EitherT…
,这将无法工作,因为
serveDirectory
不存在于
ReaderT
中。我不确定这是否是servant中的一个bug,或者除了所有的
ReaderT
之外,单独定义文件服务处理程序是否真的更有意义。我已经通知了其他服务开发者@当然,我会试着写一个最小的完整答案。有关更多问题,请访问fe
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
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