问题描述
我目前正在尝试使用镜头使代码的部分更简洁。特别是,我有一个HTTP Request,我想在其中将标头的值替换为名称Private-Header
。
我设法编写了更新RequestHeaders
的函数:
updateHeaders :: RequestHeaders -> RequestHeaders
updateHeaders headers = headers & traverse . filtered (\header -> fst header == "Private-Header") %~ set _2 "xxxxxx"
但是,我正在努力提出一个从请求中提取标头并更新它们的函数。没有镜头,它看起来可能像这样:
updateRequest :: Request -> Request
updateRequest req = req {requestHeaders = updateHeaders (requestHeaders req)}
有没有办法使用镜头实现此功能?
解决方法
当然。首先,您需要一个光学元件来表示"Private-Header"
对象中RequestHeaders
标头的值。合理的候选对象是遍历,它允许在另一种类型中出现零次或多次出现。 (通常,您只有零个或一个私有标头,但是RequestHeader
类型并没有阻止两个或多个同名标头的基本知识,因此遍历似乎是最安全的选择。)
此光学元件的合适类型是:
privateHeader :: Traversal' RequestHeaders ByteString
您已经完成了updateHeaders
中定义此光学元件的大部分工作,您只需要重新排列零件。表达式:
traverse . filtered (\header -> fst header == "Private-Header")
是一种光学器件,可从Header
中提取匹配的RequestHeader
值。只要您不使用它来修改键并中断过滤,它都是有效的遍历,因此我们可以直接将其与镜头_2
组合以创建一个新的遍历,该遍历从{{1 }}:
type Header = (ByteString,ByteString)
顺便说一下,这种新的遍历也使我们能够简化privateHeader = traverse . filtered (\header -> fst header == "Private-Header") . _2
的实现。
updateHeaders
第二,我们需要一个光学器件来表示updateHeaders :: RequestHeaders -> RequestHeaders
updateHeaders = set privateHeader "xxxxxx"
的{{1}}字段的值。您可以使用RequestHeaders
函数构建一个:
Request
现在,您可以组成lens
和headers :: Lens' Request RequestHeaders
headers = lens getter setter
where getter = requestHeaders
setter req hdrs = req { requestHeaders = hdrs }
来创建新的遍历:
headers
和privateHeaders
可以实现为:
privateHeaderInRequest :: Traversal' Request ByteString
privateHeaderInRequest = headers . privateHeader
完整代码:
updateRequest