我使用Servant提供API,全部由Snap管理。在Servant中,假设它具有FromJSON
实例,很容易将任意数据类型包含在POST请求中。例如,我可能有以下端点:
ReqBody '[JSON] RequestData :> Post '[JSON] [ResponseData]
如何对GET请求执行相同的操作?根据我的理解,我需要使用查询参数,但我的请求数据由复杂的数据类型(列表,嵌套字典)组成,这些数据类型似乎不易被读取,例如QueryParam "vals" [Int] :> Post '[JSON] [Int]
会导致错误No instance for (FromHttpApiData [Int])
解决方法是使用POST请求,它具有易于阅读的请求体。但是,这会与我在Nginx中的缓存方案发生冲突,因为对POST请求的响应并不容易缓存。即使我可以缓存它们,我也不想缓存所有发布请求,因此它是一种混乱的方法。
感谢您的帮助!
答案 0 :(得分: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