来自https://github.com/scotty-web/scotty/issues/203
作为一种解决方法,我通过收起 Content-Type 标头来防止 Scotty 解析正文:
{-# LANGUAGE OverloadedStrings #-}
module Main
( main
) where
import Control.Exception (bracket)
import Control.Exception.Base (catch, throwIO)
import Control.Monad.Trans (liftIO)
import qualified Data.ByteString as BS
import Data.CaseInsensitive (CI)
import Network.HTTP.Types.Header (hContentType)
import Network.Wai (Middleware, Request, requestHeaders)
import Network.Wai.Parse
(BackEnd, FileInfo(..), getRequestBodyType, parseRequestBody)
import System.FilePath ((</>))
import System.IO (hClose)
import System.IO.Error (isDoesNotExistError)
import System.Posix.Files (removeLink)
import System.Posix.Temp (mkstemp)
import Web.Scotty
data UploadState = UploadState
{ size :: !Int
}
removeIfExists :: FilePath -> IO ()
removeIfExists path = removeLink path `catch` handleExists
where
handleExists e
| isDoesNotExistError e = return ()
| otherwise = throwIO e
fileBackend :: BackEnd UploadState
fileBackend _ (FileInfo _fname _cntType ()) reader = bracket start stop work
where
st0 = UploadState {size = 0}
start = mkstemp ("uploads" </> "tmp-")
stop (p, h) = do
hClose h
removeIfExists p
work (_p, h) = do
st <- loop h st0
return st
loop h st = do
bs <- reader
if BS.null bs
then return st
else do
BS.hPut h bs
loop h st {size = size st + BS.length bs}
scottyHack :: Middleware
scottyHack app req resp =
case getRequestBodyType req of
Nothing -> app req resp
Just _ -> app (fixRequest req) resp
xContentType :: CI BS.ByteString
xContentType = "X-Content-Type"
fixRequest :: Request -> Request
fixRequest req = req {requestHeaders = map putaway $ requestHeaders req}
where
putaway (h, v) =
if h == hContentType
then (xContentType, v)
else (h, v)
unFixRequest :: Request -> Request
unFixRequest req = req {requestHeaders = map putback $ requestHeaders req}
where
putback (h, v) =
if h == xContentType
then (hContentType, v)
else (h, v)
main :: IO ()
main =
scotty 3000 $ do
middleware scottyHack
post "/upload" $ do
req <- request
(_, docs) <- liftIO $ parseRequestBody fileBackend (unFixRequest req)
json $ map (size . fileContent . snd) docs