包罗万象或默认路由
Catch-all or default routing
如今,如果请求的路由与现有的 API 端点或其他端点不匹配,则需要从后端 return 文件(例如,index.html
)的情况并不少见静态资产。这在使用 react-router
and browserHistory
.
时特别方便
对于如何使用 Servant 处理这个问题,我有点难过。我确实想知道拦截 404 是否是可行的方法,但当然有时 API 需要合法地发出 404。这是我一直用来试验的东西:
data Wombat = Wombat
{ id :: Int
, name :: String
} deriving (Eq, Show, Generic)
instance ToJSON Wombat
wombatStore :: [Wombat]
wombatStore =
[ Wombat 0 "Gertrude"
, Wombat 1 "Horace"
, Wombat 2 "Maisie"
, Wombat 3 "Julius"
]
wombats :: Handler [Wombat]
wombats = return wombatStore
wombat :: Int -> Handler Wombat
wombat wid = do
case find (\w -> Main.id w == wid) wombatStore of
Just x -> return x
Nothing -> throwE err404
type API =
"api" :> "wombats" :> Get '[JSON] [Wombat] :<|>
"api" :> "wombats" :> Capture "id" Int :> Get '[JSON] Wombat :<|>
Raw
api :: Proxy API
api = Proxy
server :: Server API
server = wombats
:<|> wombat
:<|> serveDirectory "static"
app :: Application
app = serve api server
main :: IO ()
main = run 3000 app
我很想看到一个示例,说明如果请求与 API 端点不匹配,我如何添加 'default route' 发送 HTML 响应,或者静态目录中的任何内容。玩具回购 here.
基本上你明白了。 serveDirectory "static"
可以替换为任何 wai Application
,因此例如,我们可以:
...
{-# LANGUAGE OverloadedStrings #-}
...
import Network.Wai (responseLBS)
import Network.HTTP.Types (status200)
...
server :: Server API
server = wombats
:<|> wombat
:<|> hello
hello :: Application
hello req respond = respond $
responseLBS
status200 --
[("Content-Type", "text/plain")] -- headers
"Hello, World!" -- content
...
初步估计,wai 应用程序只是 Request -> Response
,但 the docs 讲述了一个更完整的故事:
Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
因此,由于您可以访问 IO,因此您可以检查文件是否存在,如果存在则提供它,否则做任何你想做的事。事实上,wai 定义了 type Middleware = Application -> Application
,因此您可能会想出一个方便的中间件,它将 hello
(或任何其他应用程序!)包装在文件 existence-checker-and-server.
中
这是另一条路线:
serveDirectory
定义为
serveDirectory = staticApp . defaultFileServerSettings . addTrailingPathSeparator
defaultFileServerSettings
包含一个字段 ssLookupFile
,如果找不到文件,您可以更改该字段以提供您想要的服务。也许:
import WaiAppStatic.Types
import WaiAppStatic.Storage.Filesystem
import Network.Wai.Application.Static
import System.FilePath
fileOrIndex root pieces = do
res <- ssLookupFile (defaultFileServerSettings root) pieces
case res of
LRNotFound -> undefined -- index.html here
_ -> return res
serveStatic root =
let root' = addTrailingPathSeparator root in
staticApp $ (defaultFileServerSettings root') {ssLookupFile = fileOrIndex root'}
如今,如果请求的路由与现有的 API 端点或其他端点不匹配,则需要从后端 return 文件(例如,index.html
)的情况并不少见静态资产。这在使用 react-router
and browserHistory
.
对于如何使用 Servant 处理这个问题,我有点难过。我确实想知道拦截 404 是否是可行的方法,但当然有时 API 需要合法地发出 404。这是我一直用来试验的东西:
data Wombat = Wombat
{ id :: Int
, name :: String
} deriving (Eq, Show, Generic)
instance ToJSON Wombat
wombatStore :: [Wombat]
wombatStore =
[ Wombat 0 "Gertrude"
, Wombat 1 "Horace"
, Wombat 2 "Maisie"
, Wombat 3 "Julius"
]
wombats :: Handler [Wombat]
wombats = return wombatStore
wombat :: Int -> Handler Wombat
wombat wid = do
case find (\w -> Main.id w == wid) wombatStore of
Just x -> return x
Nothing -> throwE err404
type API =
"api" :> "wombats" :> Get '[JSON] [Wombat] :<|>
"api" :> "wombats" :> Capture "id" Int :> Get '[JSON] Wombat :<|>
Raw
api :: Proxy API
api = Proxy
server :: Server API
server = wombats
:<|> wombat
:<|> serveDirectory "static"
app :: Application
app = serve api server
main :: IO ()
main = run 3000 app
我很想看到一个示例,说明如果请求与 API 端点不匹配,我如何添加 'default route' 发送 HTML 响应,或者静态目录中的任何内容。玩具回购 here.
基本上你明白了。 serveDirectory "static"
可以替换为任何 wai Application
,因此例如,我们可以:
...
{-# LANGUAGE OverloadedStrings #-}
...
import Network.Wai (responseLBS)
import Network.HTTP.Types (status200)
...
server :: Server API
server = wombats
:<|> wombat
:<|> hello
hello :: Application
hello req respond = respond $
responseLBS
status200 --
[("Content-Type", "text/plain")] -- headers
"Hello, World!" -- content
...
初步估计,wai 应用程序只是 Request -> Response
,但 the docs 讲述了一个更完整的故事:
Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
因此,由于您可以访问 IO,因此您可以检查文件是否存在,如果存在则提供它,否则做任何你想做的事。事实上,wai 定义了 type Middleware = Application -> Application
,因此您可能会想出一个方便的中间件,它将 hello
(或任何其他应用程序!)包装在文件 existence-checker-and-server.
这是另一条路线:
serveDirectory
定义为
serveDirectory = staticApp . defaultFileServerSettings . addTrailingPathSeparator
defaultFileServerSettings
包含一个字段 ssLookupFile
,如果找不到文件,您可以更改该字段以提供您想要的服务。也许:
import WaiAppStatic.Types
import WaiAppStatic.Storage.Filesystem
import Network.Wai.Application.Static
import System.FilePath
fileOrIndex root pieces = do
res <- ssLookupFile (defaultFileServerSettings root) pieces
case res of
LRNotFound -> undefined -- index.html here
_ -> return res
serveStatic root =
let root' = addTrailingPathSeparator root in
staticApp $ (defaultFileServerSettings root') {ssLookupFile = fileOrIndex root'}