Servant 中的 Html 页面 -- 如何合并 REST API 和静态 html 页面?
Html page in Servant -- how to combine REST API and static html pages?
我有一个简单的 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
我想知道为什么?
how to combine REST API and static html pages?
您可以通过 serveDirectory
在根路径提供包含静态网站的目录。它必须是你的仆人中的最后一个案例 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 崩溃,它将被隐藏。
why isn't it "/" :> Raw
?
看起来我的浏览器缓存了一些静态页面。 "/" :> Raw
在 /index.html
下清理缓存后没有响应。
api 中的字符串文字将首先编码为合法的 uri 部分,因此 "/"
将是 "%2F"
并且您的文件将映射到 /%2F/index.html
等等.
do you know how can I handle the root case?
要在根路径提供响应,您可以定义一个 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")] }
it already returns index.html. Hm, why exactly index.html?
serveDirectory
使用设置 defaultFileServerSettings
调用 wai 应用程序 staticApp
。在该设置中,如果出现问题,用户将被重定向到 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
}
我有一个简单的 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
我想知道为什么?
how to combine REST API and static html pages?
您可以通过 serveDirectory
在根路径提供包含静态网站的目录。它必须是你的仆人中的最后一个案例 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 崩溃,它将被隐藏。
why isn't it
"/" :> Raw
?
看起来我的浏览器缓存了一些静态页面。 "/" :> Raw
在 /index.html
下清理缓存后没有响应。
api 中的字符串文字将首先编码为合法的 uri 部分,因此 "/"
将是 "%2F"
并且您的文件将映射到 /%2F/index.html
等等.
do you know how can I handle the root case?
要在根路径提供响应,您可以定义一个 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")] }
it already returns index.html. Hm, why exactly index.html?
serveDirectory
使用设置 defaultFileServerSettings
调用 wai 应用程序 staticApp
。在该设置中,如果出现问题,用户将被重定向到 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
}