Обслуживание статических файлов с помощью Servant / Wai
Я следую этому руководству http://www.parsonsmatt.org/programming/2015/06/07/servant-persistent.html чтобы создавать API через слугу. Я хочу настроить сервер для обслуживания статических файлов, но не смог найти способ сделать это.
Я использую stack
инструмент для сборки.
Я модифицировал Main.hs
запуск файла для включения static
(run port $ static $ logger $ app cfg
) и я импортировала Network.Wai.Middleware.Static (static)
, Я также добавил wai-middleware-static >=0.7.0 && < 0.71
в мой файл клики.
Когда я бегу 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)
Затем я попытался использовать слуги 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, и я не знаком с Ваем, поэтому не знаю, с чего начать. Какие изменения мне нужно сделать, чтобы пример кода в посте блога служил статическим файлам?
Изменить: Поскольку комментарии скрыты от представления по умолчанию, я добавляю свой последний комментарий здесь:
Вот уменьшенная версия кода Мэтта из его блога. Я объединил все его модули в один файл, удалил все содержимое базы данных, но не очистил расширения / импорт. Когда я запускаю этот код, я получаю вышеуказанную ошибку несоответствия типов. Обратите внимание, что этот код не использует Network.Wai.Middleware.Static, и я использую квалифицированный импорт Servant StaticFiles.
Спасибо!
1 ответ
Как описано в соответствующем разделе учебника слуги, вся сделка с enter
чтобы ваши обработчики запросов использовали монаду m
(в вашем случае некоторые ReaderT
монада) и предоставить способ для преобразования вычислений в m
к вычислению в стандарте слугиEitherT ServantErr IO
монада.
Проблема здесь в том, что вы определяете кучу обработчиков запросов в ReaderT
и дополнительный для обслуживания статических файлов и вызова enter
на все это. ReaderT
обработчики преобразуются в EitherT ...
обработчики просто отлично, но enter
пытается преобразовать serveDirectory
позвонить с ReaderT ...
в EitherT ...
, Это, конечно, не произойдет в ближайшее время, так как serveDirectory
не вычисление в ReaderT ...
начать с!
слуга может просто уйти 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
до сих пор (ну, я не сделал).