【问题标题】:Haskell Servant: GET Requests with arbitrary request dataHaskell Servant:使用任意请求数据获取请求
【发布时间】:2018-06-04 01:45:42
【问题描述】:

我正在使用 Servant 提供一个 API,所有这些都由 Snap 管理。在 Servant 中,很容易在 POST 请求中包含任意数据类型,假设它有一个 FromJSON 实例。例如,我可能有以下端点:

ReqBody '[JSON] RequestData :> Post '[JSON] [ResponseData]

我如何为 GET 请求做同样的事情?据我了解,我需要使用查询参数,但我的请求数据由复杂的数据类型(列表、嵌套字典)组成,这些数据类型似乎不容易读取,例如QueryParam "vals" [Int] :> Post '[JSON] [Int] 导致错误No instance for (FromHttpApiData [Int])

一种解决方法是使用具有易于阅读的请求正文的 POST 请求。但是,这会与我在 Nginx 中的缓存方案发生冲突,因为对 POST 请求的响应不是那么容易缓存的。即使我可以缓存它们,我也不想缓存所有的 post 请求,所以这将是一个混乱的方法。

感谢您的帮助!

【问题讨论】:

  • 如果你不介意孤儿实例,为什么不自己为(FromHttpApiData a) => FromHttpApiData [a]定义一个实例
  • @Probie 谢谢,对于单一请求类型,这是一种干净的方法。但是,我有很多请求类型。我需要为每个数据类型RequestData1, RequestData2,... 编写一个FromHttpApiData 实例,这感觉很奇怪,所有这些都可能很复杂。由于可以自动解析 POST 请求正文,看来我应该能够重用那里使用的代码,或者是否存在阻止这样做的技术限制?

标签: haskell haskell-snap-framework servant


【解决方案1】:

如果您想要与 JSON 帖子正文相同级别的自动派生,一个简单的解决方案是将查询参数作为 JSON 发送

import Data.Aeson
import Servant.API
import qualified Data.Text as T
import Data.Text.Encoding
import qualified Data.ByteString.Lazy as LBS

newtype JSONEncoded a = JSONEncoded { unJSONEncoded :: a }
  deriving (Eq, Show)

instance (FromJSON a) => FromHttpApiData (JSONEncoded a) where
  parseQueryParam x = case eitherDecode $ LBS.fromStrict $ encodeUtf8 x of
    Left err -> Left (T.pack err)
    Right val -> Right (JSONEncoded val)

instance (ToJSON a) => ToHttpApiData (JSONEncoded a) where
  toQueryParam (JSONEncoded x) = decodeUtf8 $ LBS.toStrict $ encode x

【讨论】:

  • 这非常有效。由于这种方法返回一个Maybe (JSONEncoded a)),我添加了一个maybeHandler :: HasDefault d => (a -> Handler b Service d) -> Maybe (JSONEncoded a) -> Handler b Service d 类型的助手,这样我就可以继续使用我现有的API,它是通过a -> Handler b Service d 类型的函数定义的。每当无法解析 JSON 时,就会返回 d 的默认值。
  • 如果我的查询参数包含一个大列表会怎样?对于会膨胀到数千个字符的 URL 是否有任何顾虑?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
  • 2018-05-23
  • 2017-02-13
  • 1970-01-01
相关资源
最近更新 更多