2015-06-24 4 views
7

Sto seguendo questo tutorial http://www.parsonsmatt.org/programming/2015/06/07/servant-persistent.html per creare API tramite servant. Voglio personalizzare il server per servire anche i file statici, ma non sono riuscito a trovare un modo per farlo.Servire file statici con Servant/Wai

Sto usando lo strumento di creazione stack.

Ho modificato il file Main.hs per includere static (run port $ static $ logger $ app cfg) e ho importato Network.Wai.Middleware.Static (static). Ho anche aggiunto wai-middleware-static >=0.7.0 && < 0.71 al mio file cabal.

Quando eseguo stack build ottengo: (Aggiornamento:. Questa parte è assolutamente il mio errore ho aggiunto il pacchetto al file cabala sbagliato .. zoppo Importazione di opere Network.Wai.Middleware.Static e serve. . file statici Lasciando l'errore sotto nel caso qualcuno cerca e lo trova utile)

Could not find module ‘Network.Wai.Middleware.Static’ 
Perhaps you meant 
    Network.Wai.Middleware.Gzip (from [email protected]_GpotceEdscHD6hq9p0wPOJ) 
    Network.Wai.Middleware.Jsonp (from [email protected]_GpotceEdscHD6hq9p0wPOJ) 
    Network.Wai.Middleware.Local (from [email protected]_GpotceEdscHD6hq9p0wPOJ) 

Poi ho provato ad utilizzare di serveDirectory servo come segue (semplificato):.

type API = "users" :> Get '[JSON] [Person] 
      :<|> "static" :> Raw 
server = createPerson :<|> serveDirectory "/static" 

ottengo questo errore:

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 sono un principiante Haskell e non ho familiarità con Wai così sicuri da dove cominciare anche. Quali cambiamenti devo fare per rendere il codice di esempio nel post del blog per servire file statici?

Edit: Dal momento che i commenti vengono nascosti alla visualizzazione predefinita, sto incollando il mio ultimo commento qui:

Qui è attenuato versione di Matt code dal suo blog. Ho consolidato tutti i suoi moduli in un unico file, rimosso tutto il materiale del database ma non ripulendo le estensioni/importazioni. Quando eseguo questo codice ottengo l'errore di disallineamento di tipo sopra riportato. Si noti che questo codice non utilizza Network.Wai.Middleware.Static e sto utilizzando un'importazione qualificata di Servant StaticFiles.

Grazie!

+1

Per il primo, penso che sia necessario aggiungere wai-app-static alla build, a seconda del file .cabal. –

+0

Grazie, Michael. In realtà avevo messo il pacchetto nel file cabal sbagliato. Quindi, wai-middleware-static funziona bene. Sto giocando con il codice nel post del blog collegato e ho notato che catene di middleware hanno deciso di utilizzare il middleware statico. Probabilmente mi ci vorrà più tempo per capire come usare 'wai-app-static' in quel contesto. – Ecognium

+0

@Ecognium Hai visto [questa parte] (https://haskell-servant.github.io/tutorial/server.html#serving-static-files) del tutorial del servo? –

risposta

8

Come descritto in the relevant section of servant's tutorial, l'intera faccenda con enter è quello di avere i vostri gestori delle richieste utilizzare alcuni monade m (nel tuo caso qualche ReaderT Monade) e di fornire un modo per convertire un calcolo in m ad un calcolo in servo ' s standard EitherT ServantErr IO monade.

Il problema qui però è che si definisce un gruppo di gestori delle richieste in ReaderTe uno supplementare per servire i file statici, e chiamare enter su tutti questi. I gestori ReaderT sono convertiti in gestori EitherT ... correttamente, ma enter tenta di convertire la chiamata serveDirectory da ReaderT ... a EitherT .... Questo ovviamente non accadrà presto, dal momento che lo serveDirectory non è un calcolo in ReaderT ... per cominciare!

servo potrebbe forse lasciare serveDirectory da solo - a questo punto non ho un parere definitivo sulla questione se dovremmo farlo o no, o se è meglio avere solo il gestore di file-serving essere incollato a parte , al risultato di chiamare enter su tutti gli altri endpoint. Ecco come questo sarebbe simile (look per - NEW per vedere i cambiamenti):

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 = "[email protected]" } 
    return person 

ho portato questo tema all'attenzione degli altri sviluppatori servo in ogni caso, grazie! Non avevamo davvero pensato all'interazione tra enter e serveDirectory finora (beh, non l'ho fatto).

+0

Grazie mille per questo! – runeks