在重构这个常见的LISP代码段时,如何成功删除COND子句包装处理程序案例(错误处理)情况?
我正在使用SBCL,EMACS,SLIME和DEXADOR(HTTP请求的库)。我有此功能 :
(defun old-handle-response-and-status (final-url method &optional user-content)
(let ((status-code)
(response))
(cond ((equal method "get")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:get final-url)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code))))
((equal method "post")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:post final-url
:content user-content)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))))
它适用于get
,post
和错误处理 works 正如预期的那样当HTTP请求面临错误时。证明其工作的标志性示例是:
CL-USER> (old-handle-response-and-status "http://www.paulgraham.com" "get")
("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
(big HTML omitted)
</html>"
200)
CL-USER> (old-handle-response-and-status "https://httpbin.org/post" "post" '(("name" . "pedro")))
("{
medium JSON omitted
}
"
200)
CL-USER> (old-handle-response-and-status "https://httpstat.us/409" "get")
(NIL "The server returned a failed request of 409 status.")
确定。在重构此代码时,我试图删除cond
子句。因此,我做了一个新的较短版本:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case method-call
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
它主要工作,但是仅在请求成功:
CL-USER> (new-handle-response-and-status (dex:get "http://www.paulgraham.com"))
("
HTML omitted
</html>"
NIL)
CL-USER> (new-handle-response-and-status (dex:post "https://httpbin.org/post" :content '(("name" . "pedro"))))
("{
medium JSON omitted
}
"
NIL)
当请求失败时,重构没有按预期工作! 打电话时:
CL-USER> (new-handle-response-and-status (dex:get "https://httpstat.us/409"))
粘液调试器投掷:
An HTTP request to "https://httpstat.us/409" returned 409 conflict.
我期望:
(NIL "The server returned a failed request of 409 status.")
我尝试调整输入是引用的s表达,并插入eval eval
:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case (eval method-call)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
但是
CL-USER> (new-handle-response-and-status '(dex:get "https://httpstat.us/409"))
(NIL "The server returned a failed request of 409 status.")
,这感觉是不好的练习 - 不是真的与重构工作兼容。有没有办法在不使用eval
的情况下进行修复?
也许使用funcall
?
I am using SBCL, Emacs, Slime, and Dexador (a library for HTTP requests). I have this function that works:
(defun old-handle-response-and-status (final-url method &optional user-content)
(let ((status-code)
(response))
(cond ((equal method "get")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:get final-url)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code))))
((equal method "post")
(multiple-value-bind (bresponse bstatus-code)
(handler-case (dex:post final-url
:content user-content)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))))
It works for GET
, POST
, and the error handling works as expected when a HTTP request faces errors. The iconic examples to show it working are:
CL-USER> (old-handle-response-and-status "http://www.paulgraham.com" "get")
("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
(big HTML omitted)
</html>"
200)
CL-USER> (old-handle-response-and-status "https://httpbin.org/post" "post" '(("name" . "pedro")))
("{
medium JSON omitted
}
"
200)
CL-USER> (old-handle-response-and-status "https://httpstat.us/409" "get")
(NIL "The server returned a failed request of 409 status.")
Ok. While refactoring this code, I was trying to remove the cond
clause. Thus I did a new shorter version:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case method-call
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
It majorly works, but only when the request is successful:
CL-USER> (new-handle-response-and-status (dex:get "http://www.paulgraham.com"))
("
HTML omitted
</html>"
NIL)
CL-USER> (new-handle-response-and-status (dex:post "https://httpbin.org/post" :content '(("name" . "pedro"))))
("{
medium JSON omitted
}
"
NIL)
When the request is a failed HTTP request, the refactoring does not work as expected!
When calling:
CL-USER> (new-handle-response-and-status (dex:get "https://httpstat.us/409"))
The Slime Debugger throws:
An HTTP request to "https://httpstat.us/409" returned 409 conflict.
I was expecting:
(NIL "The server returned a failed request of 409 status.")
I tried tweaking the input to be a quoted s-expression and inserting an eval
:
(defun new-handle-response-and-status (method-call)
(let ((status-code)
(response))
(multiple-value-bind (bresponse bstatus-code)
(handler-case (eval method-call)
(dex:http-request-bad-request ()
(values nil
"The server returned a failed request of 400 (bad request) status."))
(dex:http-request-failed (e)
(values nil
(format nil "The server returned a failed request of ~a status." (dex:response-status e)))))
(list (setf response bresponse)
(setf status-code bstatus-code)))))
It works:
CL-USER> (new-handle-response-and-status '(dex:get "https://httpstat.us/409"))
(NIL "The server returned a failed request of 409 status.")
But, it feels as a bad practice - not really compatible with a refactoring effort. Is there a way to fix this without using eval
?
Maybe using funcall
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题在于您正在调用
dex:get
或dex:post
在调用该功能之前,因此处理程序绑定无效。您需要传递调用它的函数,然后调用该功能。
或者您可以将其转换为宏:
The problem is that you're calling
dex:get
ordex:post
before invoking the function, so the handler binding is not in effect.You need to pass a function that calls it, and then call that function.
Or you could convert it to a macro:
记住评估规则。
(打印'bar1)
评估foo
的外部。Remember evaluation rules.
(print 'bar1)
is evaluated outside offoo
.