我使用servant定義了以下端點:
type ServiceAPI = "maintenance" :> Get '[PlainText] Text
myServer ::
MonadIO m
=> MonadLog m
=> MonadMetrics m
=> MonadRandom m
=> Config
-> Client
-> ServerT ServiceAPI m
myServer cfg client = ...
有時內容太大無法立即回傳,HTTP 請求超時。我想將此服務轉換為某種基于流的回應。類似于:
type ServiceAPI = "maintenance" :> StreamGet NewlineFraming PlainText (SourceIO Text) -- or SourceT m Text
但是,我不明白/弄清楚如何更新myServer以很好地與流媒體SourceIO( SourceT m) 配合使用。我相信它不能與SourceIO因為type SourceIO = SourceT IO這里我們有一些其他的 monad 堆疊。
- 如何流式傳輸此回應的內容?(如果
Text讓事情變得困難,我也可以使用[Text],但MonadIO可能會抱怨MonadIO does not have an instance for (MonadIO [])或類似)。(謝謝!)
uj5u.com熱心網友回復:
看來你需要一個SourceT m Text. 往里看Servant.Types.SourceT,定義是
newtype SourceT m a = SourceT
{ unSourceT :: forall b. (StepT m a -> m b) -> m b
}
所以,我們得到了一個消費者函式StepT m Text -> m b,我們需要將 a 傳遞StepT m Text給它。
我們可能會問,為什么 Servant 不StepT m Text直接require ,而不是這個 continuation-passing 定義?答案是 continuation-passing 定義允許您插入類似bracket操作,例如,在開始時打開一個檔案并確保在流式傳輸完成后關閉該檔案。
我在您的簽名中看到的一個可能問題是約束不支持類似括號的操作。你需要這樣的東西MonadUnliftIO或MonadMask為。MonadIO是不足夠的。
假設您的 monad 有一個 實體MonadUnliftIO,那么您可以Text像這樣流式傳輸檔案:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.IO.Unlift -- from "unliftio-core"
import Data.Text
import Data.Text.IO
import Servant.API
import Servant.Server
import Servant.Types.SourceT
import System.FilePath
import System.IO
serveText :: forall m. MonadUnliftIO m => FilePath -> SourceT m Text
serveText filePath = SourceT $ \consumer ->
withRunInIO $ \unlift ->
withFile filePath ReadMode $ \handle -> do
let steps :: StepT m Text
steps =
Effect
( do
eof <- liftIO $ hIsEOF handle
if eof
then pure Stop
else do
line <- liftIO $ Data.Text.IO.hGetLine handle
pure (Yield line steps) -- recurse for more lines
)
-- we get down to IO to satisfy the signature of withFile,
-- the withRunInIO brings us back to m
unlift (consumer steps)
(小心天真的使用,hGetLine因為它使用系統的默認編碼。streamDecodeUtf8實踐中可能會更好。)
一些注意事項:
- 只有同構 to 的 monad
ReaderT可以是 的實體MonadUnliftIO,而您的可能不適合該模式。也許它是一個MonadMask. - 也許,您可以嘗試使用resourcet包,而不是使用 classic
bracket, or 。withField - 有適用于流行流媒體庫的配接器,讓您無需
StepT直接定義。
uj5u.com熱心網友回復:
與往常一樣,答案在于您嘗試輸入的方式。我們想要回傳一個流,我們更新它的型別ServiceAPI如下:
type ServiceAPI = "maintenance" :> StreamGet NewlineFraming PlainText (SourceIO Text)
型別myServer保持完全相同。由于我是新來的servant,我不知道我們想要什么樣的回報是SourceT IO Text在身體的任何myServer單位計算。為了更好地查看型別,讓我們假設myServer主體呼叫函式myFunc如下,myFunc在回傳之前的位置Text或[Text](此解決方案適用于兩者,重構作業最少)。
myServer ::
MonadIO m
=> MonadLog m
=> MonadMetrics m
=> MonadRandom m
=> Config
-> ServerT ServiceAPI m
myServer cfg = myFunc cfg
myFunc ::
MonadIO m
=> MonadLog m
=> Config
-> m (SourceT IO Text)
myFunc cfg client = do
...
let ls = _ :: [Text] -- some [Text]
pure $ source ls
實作是我們不想myFunc回傳SourceT m Text但是m (SourceT IO Text)。缺失的部分之一是source :: [a] -> SourceT n a我們想要的n = IO,以便我們可以回傳SourceIO = SourceT IO。
(我的錯誤是在理解myFuncas的回傳型別SourceT m Text時,它應該是m (SourceT IO Text),并且非常正確,型別檢查器不允許這樣做)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/365069.html
下一篇:如何列出配對串列的所有可能方法?
