通过 WAI 中间件提供静态文件

问题描述 投票:0回答:1

我正在使用 Servant 在 Haskell 中开发一个 Web 应用程序。我成功在服务器上加载文件并将它们移动到我的 SFTP 服务器。现在我希望用户能够在 SFTP 上加载文件(我需要用servant为他们提供静态文件)。为了管理 CORS 问题,我使用了 WAI 中间件库。这是我的代码,我在其中引入中间件来设置 CORS 标头。

    newtype Welcome
      = Welcome {
            greeting :: String
        }
      deriving (Generic, Eq, Show)

    instance ToJSON Welcome
    instance FromJSON Welcome

    welcomeMesg :: Welcome
    welcomeMesg = Welcome "Mbote Ba'a Mpagi!"

    type API =
        -- | test for home page
            "home"  -- 1
          :> Get '[JSON] Welcome 
        :<|> "tenants" 
          :> Capture "tenantid" Text
          :> "informations"
          :> MultipartForm Tmp (MultipartData Tmp)
          :> Post '[JSON] (GenericRestResponse InformationCreated ConstructionError)
        :<|> "tenants" -- 3
          :> Capture "tenantid" Text
          :> "informations"
          :> Capture "informationid" Text
          :> "documents"
          :> Raw 

    addOriginsAllowed :: Response -> Response
    addOriginsAllowed  = 
      let final = (:) ("Access-Control-Allow-Origin", "*") . 
                  (:) ("Access-Control-Allow-Methods", "GET, POST, PUT, OPTION") .
                  (:) ("Access-Control-Allow-Headers", "Content-Type, Authorization")
      in mapResponseHeaders  final


    addAllOriginsMiddleware :: Application -> Application
    addAllOriginsMiddleware baseApp req responseFunc =
        let newResponseFunc :: Response -> IO ResponseReceived
            newResponseFunc = responseFunc . addOriginsAllowed
        in baseApp req newResponseFunc


    startApp :: IO ()
    startApp = run 9080 $ addAllOriginsMiddleware app

    proxy :: Proxy API
    proxy = Proxy

    optionMidlleware :: Middleware
    optionMidlleware = provideOptions proxy


    app ::  Application
    app =  optionMidlleware  $ serve proxy routes

    routes :: Server API
    routes =
      return
        welcomeMesg -- 1
      :<|> handleCreateInformation --2
      :<|> handlerGetInformationDocuments -- 3

如果我没有明确要求

Raw
提供静态文件,一切都会正常运行。最后一个 enpoint 生成此错误:

• No instance for (servant-foreign-0.16:Servant.Foreign.Internal.GenerateList
                     Servant.API.ContentTypes.NoContent
                     (http-types-0.12.4:Network.HTTP.Types.Method.Method
                      -> servant-foreign-0.16:Servant.Foreign.Internal.Req
                           Servant.API.ContentTypes.NoContent))
    arising from a use of ‘provideOptions’
    (maybe you haven't applied a function to enough arguments?)
• In the expression: provideOptions proxy
  In an equation for ‘optionMidlleware’:
      optionMidlleware = provideOptions proxytypecheck(-Wdeferred-type-errors)
provideOptions :: forall api.
(GenerateList NoContent (Foreign NoContent api),
 HasForeign NoTypes NoContent api) =>
Proxy api -> Middleware
Defined in ‘Network.Wai.Middleware.Servant.Options’ (servant-options-0.1.0.0)

_ :: Proxy API -> Middleware 

这个错误发生在我调用

provideOptions
(WAI中间件库的函数)的地方

我被屏蔽了。我能做什么来克服这个错误。 预先感谢您的帮助

我尝试返回 ByteString 但 i 不是 ToJson 和 FromJson 的实例

api haskell servant wai
1个回答
0
投票

发生此错误是因为

Raw
端点只是非 Servant 请求/响应处理的“逃生舱口”,因此
provideOptions
(Servant 特定的中间件)无法正确处理
Raw
API 中的端点。

更详细地说,

provideOptions
使用特定于端点的
HasForeign
实例来查询API以获取支持的方法,以便构建正确的
Allow
标头来响应
OPTIONS
请求。它可以对所有 Servant 端点执行此操作,除了
Raw
,它没有
HasForiegn
实例。

也许可以在您的特定情况下找到一些工作,但我的猜测是您根本不需要

Raw
端点。 Servant 说明书中的示例使用
Raw
端点来提供静态文件,但这仅仅是因为它们想要使用本质上非 Servant 处理函数 (
serveDirectoryWebApp
) 来提供整个目录。 (是的,
serveDirectoryWebApp
是在
servant-server
包中定义的,但它实际上是一个非Servant处理程序。它并没有真正使用Servant API或任何Servant处理程序设施。它只是来自
serveDirectoryWith
Network.Wai.Application.Static
的包装器
。)

在您的情况下,听起来您只是想编写一个简单的Servant处理程序

handlerGetInformationDocuments
,它直接访问文件并将其内容收集在
ByteString
中,并且您希望提供该
ByteString
作为内容响应客户的
GET
请求而返回。如果是这样,您只需要一个返回八位字节流的
Raw
端点,而不是
Get
端点,例如:

import qualified Text.ByteString.Lazy as LBS

... :> "documents" :> Get '[OctetStream] LBS.ByteString

处理程序只需返回

ByteString
:

handlerGetInformationDocuments :: Text -> Text -> Handler LBS.ByteString                                 
handlerGetInformationDocuments tenantid informationid = do                                               
  content <- liftIO $ LBS.readFile "file.bin"                                                            
  pure content 

上面,我使用了惰性

ByteString
,如果文件有点大,这可能是最好的,尽管您要注意在开始提供文件后不要修改文件。严格的
ByteString
也可以很好地工作,尤其是在文件很小的情况下。

© www.soinside.com 2019 - 2024. All rights reserved.