【问题标题】:Html page in Servant -- how to combine REST API and static html pages?Servant 中的 Html 页面——如何结合 REST API 和静态 html 页面?
【发布时间】:2016-04-02 11:51:10
【问题描述】:

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

更新:

我该如何结合:

type MyApi = "/" :> Raw

server :: Server MyApi
server = serveDirectory "static/" -- index.html, about.html 

我已经拥有的:

  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

更新2:

工作:

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

不工作,完全没有反应:

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

不知道为什么?

【问题讨论】:

  • 如果您的静态页面结构正确,您可以使用serveDirectory
  • @zakyggaps,已更新。
  • Servant API 将按从上到下的顺序匹配。你可以把它放在最后一行。
  • @zakyggaps,在第一个“服务器”中,我有 serveDirectory “static/”,在第二个中,我有“getItems : getItem”。我该如何组合它们?
  • 你不能这样做::&lt;|&gt; 是右关联的,如果 "/" :&gt; Raw 是最后一种情况,你必须定义你的 API,其中包含所有三种情况,如 case1 :&lt;|&gt; case2 :&lt;|&gt; case3

标签: haskell servant


【解决方案1】:

如何结合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/"

此外,如果任何静态页面名称与您的 API 发生冲突,它将被隐藏。


为什么不是"/" :&gt; Raw

看起来我的浏览器缓存了一些静态页面。 "/" :&gt; Raw 在清理缓存后在/index.html 下没有响应。

api 中的字符串文字将首先被编码为合法的 uri 部分,因此 "/" 将是 "%2F" 并且您的文件将映射到 /%2F/index.html 等等。


你知道我该如何处理根案例吗?

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

type API = Get '[HTML] RawHtml

它可以在你的 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

在你的控制器中:

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

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

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

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

serveDirectory 使用设置 defaultFileServerSettings 调用 wai 应用程序 staticApp。在该设置中,如果出现问题,用户将被重定向到 index.htmindex.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
    }

【讨论】:

  • 为什么不是:&lt;|&gt; "" :&gt; Raw:&lt;|&gt; "/" :&gt; Raw?为什么只是:&lt;|&gt; Raw
  • Raw"/" :&gt; Raw 都将指向根路径。我选择了较短的。
  • 实际上 :&lt;|&gt; "" :&gt; Raw:&lt;|&gt; "/" :&gt; Raw 不起作用。服务器不返回任何东西,就像它没有找到一样。我想知道为什么?
  • @OskarK。我无法重现。 "/" :&gt; RawRaw 与我尝试的结果相同。
  • 你知道如何处理root case,即当我请求“localhost:1234”时,如何返回页面“index.html”?意思是,我想为“localhost:1234”和“localhost:1234/index.html”返回“index.html”
猜你喜欢
  • 2011-03-28
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
  • 2016-10-19
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
  • 2012-04-15
相关资源
最近更新 更多