Servant中的HTML页面-如何结合REST API和静态HTML页面?

4 haskell servant

我有一个简单的hello world Servant应用程序。我需要向其中添加一些静态或动态html页面。我怎样才能做到这一点?在文档中没有提到。注意:我不想在Haskell代码中创建html布局,而是希望Haskell显示已经创建的html页面。

更新:

我该如何结合:

type MyApi = "/" :> Raw

server :: Server MyApi
server = serveDirectory "static/" -- index.html, about.html 
Run Code Online (Sandbox Code Playgroud)

我已经拥有的:

  type API = 
    "api" :> "items" :> Get '[JSON] [MyData] :<|>
    "api" :> "items" :> Capture "id" Int :> Get '[JSON] MyData


  app :: Application
  app = serve api server

  api :: Proxy API
  api = Proxy

  server :: Server API
  server = getItems :<|> getItem

  startApp :: IO ()
  startApp =  run 1234 app
Run Code Online (Sandbox Code Playgroud)

UPDATE2:

工作方式:

type API = 
    "api" :> "items" :> Get '[JSON] [MyData] :<|>
    "api" :> "items" :> Capture "id" Int :> Get '[JSON] MyData :<|>
    Raw
Run Code Online (Sandbox Code Playgroud)

无法运作,完全没有回应:

type API = 
    "api" :> "items" :> Get '[JSON] [MyData] :<|>
    "api" :> "items" :> Capture "id" Int :> Get '[JSON] MyData :<|>
    "/" :> Raw

-- or

type API = 
    "api" :> "items" :> Get '[JSON] [MyData] :<|>
    "api" :> "items" :> Capture "id" Int :> Get '[JSON] MyData :<|>
    "" :> Raw
Run Code Online (Sandbox Code Playgroud)

我想知道为什么?

zak*_*aps 5

如何结合REST API和静态HTML页面?

您可以通过在根目录路径下提供包含静态网站的目录serveDirectory。它必须是您的Servant API中的最后一种情况,否则其他情况将永远无法匹配。

type API = 
    "api" :> "items" :> Get '[JSON] [MyData] :<|>
    "api" :> "items" :> Capture "id" Int :> Get '[JSON] MyData :<|>
    Raw

api :: Proxy API 
api = Proxy

server :: Server API 
server = getItems
    :<|> getItem 
    :<|> serveDirectory "static/"
Run Code Online (Sandbox Code Playgroud)

同样,如果任何静态页面名称与您的API一起崩溃,也会被遮盖。


为什么不呢"/" :> Raw?

看来我的浏览器缓存了一些静态页面。清除缓存后"/" :> Raw,没有响应/index.html。

在API字符串文本将被编码为合法的URI第一部分,因此"/"将"%2F"您的文件映射到/%2F/index.html等等。


你知道我怎么处理根案吗?

要在根路径下提供响应,可以定义Get不带前缀的端点:

type API = Get '[HTML] RawHtml
Run Code Online (Sandbox Code Playgroud)

它可以在您的API中的最后一行之外的任何位置。

要将本地文件作为html响应,您必须将文件与其他字节字符串区分开,也许将其包装为新类型:

newtype RawHtml = RawHtml { unRaw :: BS.ByteString }

-- tell Servant how to render the newtype to html page, in this case simply unwrap it
instance MimeRender HTML RawHtml where
    mimeRender _ =  unRaw
Run Code Online (Sandbox Code Playgroud)

并在您的控制器中:

-- ...
:<|> fmap RawHtml (liftIO $ BS.readFile "your/file/path.html")
Run Code Online (Sandbox Code Playgroud)

或者,如果页面已经有另一个地址,则可以在那里重定向用户:

-- ...
:<|> throwError err301 { errHeaders = [("Location", "index.html")] }
Run Code Online (Sandbox Code Playgroud)

它已经返回index.html。嗯,为什么要用index.html?

serveDirectory调用staticApp带有设置的wai应用程序defaultFileServerSettings。在这种设置下,用户将被重定向到index.htm或index.html出现问题:

defaultFileServerSettings root = StaticSettings
    { ssLookupFile = fileSystemLookup (fmap Just . hashFile) root
    , ssMkRedirect = defaultMkRedirect
    , ssGetMimeType = return . defaultMimeLookup . fromPiece . fileName
    , ssMaxAge = NoMaxAge
    , ssListing = Just defaultListing
    , ssIndices = map unsafeToPiece ["index.html", "index.htm"]
    , ssRedirectToIndex = False
    , ssUseHash = False
    , ssAddTrailingSlash = False
    , ss404Handler = Nothing
    }
Run Code Online (Sandbox Code Playgroud)