2015-06-24 19 views
7

Ich befolge dieses Tutorial http://www.parsonsmatt.org/programming/2015/06/07/servant-persistent.html, um APIs über Servant zu erstellen. Ich möchte den Server so anpassen, dass er auch statische Dateien bereitstellt, aber ich konnte keinen Weg finden, dies zu tun.Serving Statische Dateien mit Servant/Wai

Ich verwende das stack Build-Tool.

Ich änderte den Dateilauf, um static (run port $ static $ logger $ app cfg) zu enthalten, und ich importierte Network.Wai.Middleware.Static (static). Ich habe auch wai-middleware-static >=0.7.0 && < 0.71 zu meiner Cabal-Datei hinzugefügt.

Als ich stack build laufen erhalte ich: (Update:. Dieser Teil total mein Fehler ist habe ich das das Paket an der falschen Clique Datei .. lahm importieren Network.Wai.Middleware.Static Werke und Aufschlägen. . statische Dateien unten für sie, falls jemand den Fehler Weggehen sucht und findet es nützlich)

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) 

Next ich habe versucht, die Verwendung von Diener serveDirectory wie folgt (vereinfacht):.

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

ich diesen Fehler:

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 

Ich bin ein Anfänger Haskell und ich bin nicht mit Wai so unsicher, wo man sogar anfangen. Welche Änderungen muss ich vornehmen, damit der Beispielcode im Blogpost statische Dateien bereitstellt?

bearbeitet: Da die Kommentare aus der Standardansicht versteckt zu bekommen, ich einfügen meinen letzten Kommentar hier ein:

Hier abgemildert wird Version von Matts code aus seinem Blog. Ich habe alle seine Module in einer einzigen Datei konsolidiert, alle Datenbank-Sachen entfernt, aber die Erweiterungen/Importe nicht bereinigt. Wenn ich diesen Code ausführe, erhalte ich den obigen Typ-Mismatch-Fehler. Bitte beachten Sie, dass dieser Code nicht Network.Wai.Middleware.Static verwendet und ich einen qualifizierten Import von Servant StaticFiles verwende.

Danke!

+1

Für die erste, ich denke, Sie müssen Wai-App-Static zu den Build-Abhängigkeiten in Ihrer .cabal-Datei hinzufügen. –

+0

Danke, Michael. Ich hatte das Paket eigentlich in die falsche Kabale gelegt. So funktioniert Wai-Middleware-Statik gut. Ich spiele mit dem Code im verlinkten Blogpost und habe festgestellt, dass es Middleware-Ketten gibt, also entschied ich mich für die statische Middleware. Ich werde wahrscheinlich länger brauchen, um herauszufinden, wie man 'wai-app-static' in diesem Kontext verwenden kann. – Ecognium

+0

@Ecognium Kennen Sie [diesen Teil] (https://haskell-servant.github.io/tutorial/server.html#serving-static-files) des Servant Tutorials? –

Antwort

8

Wie in the relevant section of servant's tutorial beschrieben, ist die ganze Sache mit enter ist Ihre Anfrage Handler einige Monade verwenden haben m (in Ihrem Fall einige ReaderT Monade) und einen Weg, um eine Berechnung in m auf eine Berechnung in Diener "konvertieren s Standard EitherT ServantErr IO Monad.

Das Problem hierbei ist aber, dass Sie eine Reihe von Request-Handler in ReaderTund zusätzlich ein dienen statische Dateien und rufen enter auf alle diese definieren. Die ReaderT Handler werden in EitherT ... Handler problemlos konvertiert, aber enter versucht, den serveDirectory Anruf von ReaderT ... zu EitherT ... zu konvertieren. Dies wird natürlich nicht in absehbarer Zeit passieren, denn serveDirectory ist keine Berechnung in ReaderT ... zu beginnen!

Diener könnte wohl nur serveDirectory allein lassen - an diesem Punkt habe ich nicht eine bestimmte Meinung darüber, ob wir das tun sollten oder nicht, oder ob es besser ist, müssen nur die File-Serving-Handler separat geklebt werden , zu dem Ergebnis des Aufrufs enter auf allen anderen Endpunkten. Hier ist, wie diese aussehen würde (Blick für - NEW um die Änderungen zu sehen):

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 

ich dieses Thema, um die Aufmerksamkeit der anderen Diener Entwickler ohnehin dank gebracht haben! Wir hatten bisher nicht wirklich über die Interaktion zwischen enter und serveDirectory nachgedacht (na ja, habe ich nicht).

+0

Vielen Dank dafür! – runeks

Verwandte Themen