1

我正在使用 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 请求的响应不是那么容易缓存的。即使我可以缓存它们,我也不想缓存所有发布请求,所以这将是一种混乱的方法。

谢谢你的帮助!

4

1 回答 1

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
于 2018-06-04T12:28:21.110 回答