在重构这个常见的LISP代码段时,如何成功删除COND子句包装处理程序案例(错误处理)情况?

发布于 2025-01-23 18:55:26 字数 4847 浏览 2 评论 0原文

我正在使用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)))))))

它适用于getpost错误处理 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

是伱的 2025-01-30 18:55:26

问题在于您正在调用dex:getdex:post在调用该功能之前,因此处理程序绑定无效。

您需要传递调用它的函数,然后调用该功能。

(defun new-handle-response-and-status (method-call)
  (let ((status-code)
        (response))
    (multiple-value-bind (bresponse bstatus-code)
        (handler-case (funcall 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)))))

(new-handle-response-and-status (lambda () (dex:get "https://httpstat.us/409")))

或者您可以将其转换为宏:

(defmacro 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)))))

The problem is that you're calling dex:get or dex: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.

(defun new-handle-response-and-status (method-call)
  (let ((status-code)
        (response))
    (multiple-value-bind (bresponse bstatus-code)
        (handler-case (funcall 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)))))

(new-handle-response-and-status (lambda () (dex:get "https://httpstat.us/409")))

Or you could convert it to a macro:

(defmacro 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)))))

狂之美人 2025-01-30 18:55:26

记住评估规则。

CL-USER 36 > (defun foo (a)
               (print 'foo1)
               a    
               (print 'foo2)
               'return-value)
FOO

CL-USER 37 > (foo (print 'bar1))

BAR1 
FOO1 
FOO2 
RETURN-VALUE

(打印'bar1)评估 foo的外部。

Remember evaluation rules.

CL-USER 36 > (defun foo (a)
               (print 'foo1)
               a    
               (print 'foo2)
               'return-value)
FOO

CL-USER 37 > (foo (print 'bar1))

BAR1 
FOO1 
FOO2 
RETURN-VALUE

(print 'bar1) is evaluated outside of foo.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文