1
1
{-# LANGUAGE CPP #-}
2
+ {-# LANGUAGE AllowAmbiguousTypes #-}
2
3
{-# LANGUAGE DataKinds #-}
3
4
{-# LANGUAGE TypeFamilies #-}
4
5
{-# LANGUAGE InstanceSigs #-}
19
20
-- an API. See haddocks of 'MultipartForm' for an introduction.
20
21
module Servant.Multipart
21
22
( MultipartForm
23
+ , MultipartForm'
22
24
, MultipartData (.. )
23
25
, FromMultipart (.. )
24
26
, lookupInput
@@ -57,6 +59,7 @@ import Network.HTTP.Media.MediaType ((//), (/:))
57
59
import Network.Wai
58
60
import Network.Wai.Parse
59
61
import Servant
62
+ import Servant.API.Modifiers (FoldLenient )
60
63
import Servant.Client.Core (HasClient (.. ), RequestBody (RequestBodySource ), setRequestBody )
61
64
import Servant.Docs
62
65
import Servant.Foreign
@@ -151,7 +154,10 @@ import qualified Data.ByteString.Lazy as LBS
151
154
-- after your handler has run, if they are still there. It is
152
155
-- therefore recommended to move or copy them somewhere in your
153
156
-- handler code if you need to keep the content around.
154
- data MultipartForm tag a
157
+ type MultipartForm tag a = MultipartForm' '[] tag a
158
+
159
+ -- | 'MultipartForm' which can be modified with 'Servant.API.Modifiers.Lenient'.
160
+ data MultipartForm' (mods :: [* ]) tag a
155
161
156
162
-- | What servant gets out of a @multipart/form-data@ form submission.
157
163
--
@@ -288,11 +294,12 @@ instance ToMultipart tag (MultipartData tag) where
288
294
instance ( FromMultipart tag a
289
295
, MultipartBackend tag
290
296
, LookupContext config (MultipartOptions tag )
297
+ , SBoolI (FoldLenient mods )
291
298
, HasServer sublayout config )
292
- => HasServer (MultipartForm tag a :> sublayout ) config where
299
+ => HasServer (MultipartForm' mods tag a :> sublayout ) config where
293
300
294
- type ServerT (MultipartForm tag a :> sublayout ) m =
295
- a -> ServerT sublayout m
301
+ type ServerT (MultipartForm' mods tag a :> sublayout ) m =
302
+ If ( FoldLenient mods ) ( Either String a ) a -> ServerT sublayout m
296
303
297
304
#if MIN_VERSION_servant_server(0,12,0)
298
305
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy sublayout ) pc nt . s
@@ -306,16 +313,16 @@ instance ( FromMultipart tag a
306
313
popts = Proxy :: Proxy (MultipartOptions tag )
307
314
multipartOpts = fromMaybe (defaultMultipartOptions pbak)
308
315
$ lookupContext popts config
309
- subserver' = addMultipartHandling pbak multipartOpts subserver
316
+ subserver' = addMultipartHandling @ tag @ a @ mods pbak multipartOpts subserver
310
317
311
318
-- | Upon seeing @MultipartForm a :> ...@ in an API type,
312
319
-- servant-client will take a parameter of type @(LBS.ByteString, a)@,
313
320
-- where the bytestring is the boundary to use (see 'genBoundary'), and
314
321
-- replace the request body with the contents of the form.
315
322
instance (ToMultipart tag a , HasClient m api , MultipartBackend tag )
316
- => HasClient m (MultipartForm tag a :> api ) where
323
+ => HasClient m (MultipartForm' mods tag a :> api ) where
317
324
318
- type Client m (MultipartForm tag a :> api ) =
325
+ type Client m (MultipartForm' mods tag a :> api ) =
319
326
(LBS. ByteString , a ) -> Client m api
320
327
321
328
clientWithRoute pm _ req (boundary, param) =
@@ -427,10 +434,11 @@ check pTag tag = withRequest $ \request -> do
427
434
where parseOpts = generalOptions tag
428
435
429
436
-- Add multipart extraction support to a Delayed.
430
- addMultipartHandling :: forall tag multipart env a . (FromMultipart tag multipart , MultipartBackend tag )
437
+ addMultipartHandling :: forall tag multipart (mods :: [* ]) env a . (FromMultipart tag multipart , MultipartBackend tag )
438
+ => SBoolI (FoldLenient mods )
431
439
=> Proxy tag
432
440
-> MultipartOptions tag
433
- -> Delayed env (multipart -> a )
441
+ -> Delayed env (If ( FoldLenient mods ) ( Either String multipart ) multipart -> a )
434
442
-> Delayed env a
435
443
addMultipartHandling pTag opts subserver =
436
444
addBodyCheck subserver contentCheck bodyCheck
@@ -440,10 +448,11 @@ addMultipartHandling pTag opts subserver =
440
448
441
449
bodyCheck () = do
442
450
mpd <- check pTag opts :: DelayedIO (MultipartData tag )
443
- case fromMultipart mpd of
444
- Left msg -> liftRouteResult $ FailFatal
451
+ case (sbool :: SBool ( FoldLenient mods ), fromMultipart @ tag @ multipart mpd ) of
452
+ ( SFalse , Left msg) -> liftRouteResult $ FailFatal
445
453
err400 { errBody = " Could not decode multipart mime body: " <> cs msg }
446
- Right x -> return x
454
+ (SFalse , Right x) -> return x
455
+ (STrue , res) -> return $ either (Left . cs) Right res
447
456
448
457
contentTypeH req = fromMaybe " application/octet-stream" $
449
458
lookup " Content-Type" (requestHeaders req)
0 commit comments