如何在此HTTP-Request接口上正确使用常见的LISP多值绑定和处理程序?

发布于 2025-01-22 08:45:14 字数 3200 浏览 0 评论 0原文

我正在使用SBCL,Emacs和Slime。此外,我正在使用库 dexador

Dexador文档提供了一个有关如何处理失败HTTP请求的示例。

从官方文档中说:

;; Handles 400 bad request
(handler-case (dex:get "http://lisp.org")
  (dex:http-request-bad-request ()
    ;; Runs when 400 bad request returned
    )
  (dex:http-request-failed (e)
    ;; For other 4xx or 5xx
    (format *error-output* "The server returned ~D" (dex:response-status e))))

因此,我尝试了以下内容。必须强调的是,这是一个主要系统的一部分,因此我将其简化为:

;;Good source to test errors:  https://httpstat.us/ 

(defun my-get (final-url) 
  (let* ((status-code)
         (response)
         (response-and-status (multiple-value-bind (response status-code) 
                                  (handler-case (dex:get final-url)
                                    (dex:http-request-bad-request ()
                                      (progn
                                        (setf status-code
                                              "The server returned a failed request of 400 (bad request) status.")
                                        (setf response nil)))
                                    (dex:http-request-failed (e)
                                      (progn
                                        (setf status-code
                                              (format nil
                                                      "The server returned a failed request of ~a status."
                                                      (dex:response-status e)))
                                        (setf response nil))))
                                (list response status-code))))
    (list response-and-status response status-code)))

我的代码输出是 Close 与我想要的。但是我不了解它是输出。

当HTTP请求成功时,这就是输出:


CL-USER> (my-get "http://www.paulgraham.com")

(("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html><script type=\"text/javascript\"> 
 <!-- 
... big HTML omitted...
</script>
</html>"
  200)
 NIL NIL)

我期望(或希望)输出为:'((“ Big HTML” 200)“ Big HTML” 200)

但是,当HTTP请求失败时,事情甚至都在怪异。例如:

CL-USER> (my-get "https://httpstat.us/400")

((NIL NIL) NIL
 "The server returned a failed request of 400 (bad request) status.")

我期望:'((nil“服务器返回了400(不良请求)状态的失败请求。”)nil“服务器返回了失败的400(不良请求)状态的失败请求。”)<)< /code>

or:

CL-USER> (my-get "https://httpstat.us/425")
((NIL NIL) NIL "The server returned a failed request of 425 status.")

同样,我期望:((nil“服务器返回了425个状态的失败请求。”)nil“服务器返回了425个状态的失败请求。”)

我是害怕有一个变量遮盖问题发生 - 不过,不确定。

如何创建一个函数,以便我可以安全地存储在变量中的响应和状态代码,而不是失败或成功的请求?

如果请求成功,我有(“ HTML” 200)。如果失败,它将是:(nil 400)或其他数字(nil 425) - 取决于错误消息。

I am using SBCL, Emacs, and Slime. In addition, I am using the library Dexador.

Dexador documentation provides an example on how to handle failed HTTP requests.

From the official documentation, it says:

;; Handles 400 bad request
(handler-case (dex:get "http://lisp.org")
  (dex:http-request-bad-request ()
    ;; Runs when 400 bad request returned
    )
  (dex:http-request-failed (e)
    ;; For other 4xx or 5xx
    (format *error-output* "The server returned ~D" (dex:response-status e))))

Thus, I tried the following. It must be highlighted that this is part of a major system, so I am simplifying it as:

;;Good source to test errors:  https://httpstat.us/ 

(defun my-get (final-url) 
  (let* ((status-code)
         (response)
         (response-and-status (multiple-value-bind (response status-code) 
                                  (handler-case (dex:get final-url)
                                    (dex:http-request-bad-request ()
                                      (progn
                                        (setf status-code
                                              "The server returned a failed request of 400 (bad request) status.")
                                        (setf response nil)))
                                    (dex:http-request-failed (e)
                                      (progn
                                        (setf status-code
                                              (format nil
                                                      "The server returned a failed request of ~a status."
                                                      (dex:response-status e)))
                                        (setf response nil))))
                                (list response status-code))))
    (list response-and-status response status-code)))

My code output is close to what I want. But I do not understand it is output.

When the HTTP request is successful, this is the output:


CL-USER> (my-get "http://www.paulgraham.com")

(("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html><script type=\"text/javascript\"> 
 <!-- 
... big HTML omitted...
</script>
</html>"
  200)
 NIL NIL)

I was expecting (or wished) the output to be something like: '(("big html" 200) "big html" 200).

But, things happen to be even weirder when the HTTP request fails. For instance:

CL-USER> (my-get "https://httpstat.us/400")

((NIL NIL) NIL
 "The server returned a failed request of 400 (bad request) status.")

I was expecting: '((NIL "The server returned a failed request of 400 (bad request) status.") NIL "The server returned a failed request of 400 (bad request) status.")

Or:

CL-USER> (my-get "https://httpstat.us/425")
((NIL NIL) NIL "The server returned a failed request of 425 status.")

Again, I was expecting: ((NIL "The server returned a failed request of 425 status.") NIL "The server returned a failed request of 425 status.")

I am afraid there is a variable overshadowing problem happening - not sure, though.

How can I create a function so that I can safely store in variables the response and the status-code independent of being a failed or successful request?

If the request is successful, I have ("html" 200). If it fails, it would be: (nil 400) or other number (nil 425) - depending on the error message.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(2

牵你的手,一向走下去 2025-01-29 08:45:14

您的问题是您未能理解多价值键处理程序let*工作。 (可能还setfprog。)

TLDR

快速修复:

(defun my-get (final-url)
  (let* ((status-code)
     (response)
     (response-and-status
       (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)))))
    (list response-and-status response status-code)))

输出:

CL-USER> (my-get "http://www.paulgraham.com")
(("big html" 200) "big html" 200)
CL-USER> (my-get "https://httpstat.us/400")
((NIL "The server returned a failed request of 400 (bad request) status.") NIL "The server returned a failed request of 400 (bad request) status.")
CL-USER> (my-get "https://httpstat.us/425")
((NIL "The server returned a failed request of 425 status.") NIL "The server returned a failed request of 425 status.")

那么,为什么要获得当前结果?

对于

多重值键,它将从values表单返回的多个值绑定到相应的变量中。

CL-USER> (multiple-value-bind (a b)
             nil
           (list a b))
(NIL NIL)
CL-USER> (multiple-value-bind (a b)
             (values 1 2)
           (list a b))
(1 2)
CL-USER> (multiple-value-bind (a b)
             (values 1 2 3 4 5)
           (list a b))
(1 2)

对于Handler-case,它在没有错误时返回表达式的值。出现错误时,它将执行相应的错误处理代码。

CL-USER> (handler-case (values 1 2 3)
            (type-error () 'blah1)
            (error () 'blah2))
1
2
3
CL-USER> (handler-case (signal 'type-error)
            (type-error () 'blah1)
            (error () 'blah2))
BLAH1
CL-USER> (handler-case (signal 'error)
            (type-error () 'blah1)
            (error () 'blah2))
BLAH2

对于令*表单,如果未提供init-forms,变量将初始化为nil。同样,表单。这些变量围绕没有初始形式的括号是不必要的。示例:

CL-USER> (let* (a b (c 3))
            (list a b c))
(NIL NIL 3)

组合这些知识

dex:get成功(即无错误)时 ,它返回dex的值:get,,(值body身体状态响应 -标题URI流)。使用多重值BIND,您的wenders-and-STATUS绑定到的值(列表响应状态代码),它是(“ Big HTML” 200)

由于两个dex中的代码:http-request-bad-requestdex:http-request-failed仅在dex:get 失败,响应状态代码具有初始值nil。这就是为什么您获得((“ Big HTML” 200)nil nil)成功的原因。

dex:get失败时,响应>status-codesetf对新值。由于(setf响应nil)突变响应 返回nil(新值集)和由于Prom返回最后一个表单的值,因此您的Progn返回nil nil 对于两个错误处理案例。这就是为什么您的响应和状态在失败时绑定到(nil nil)

Your problem is that you failed to understand how multiple-value-bind, handler-case, and let* work. (Probably also setf and progn.)

TLDR

Quick fix:

(defun my-get (final-url)
  (let* ((status-code)
     (response)
     (response-and-status
       (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)))))
    (list response-and-status response status-code)))

Output:

CL-USER> (my-get "http://www.paulgraham.com")
(("big html" 200) "big html" 200)
CL-USER> (my-get "https://httpstat.us/400")
((NIL "The server returned a failed request of 400 (bad request) status.") NIL "The server returned a failed request of 400 (bad request) status.")
CL-USER> (my-get "https://httpstat.us/425")
((NIL "The server returned a failed request of 425 status.") NIL "The server returned a failed request of 425 status.")

So, Why Do You Get Your Current Result?

Preliminary

For multiple-value-bind, it binds multiple values returning from a values form into the corresponding variables.

CL-USER> (multiple-value-bind (a b)
             nil
           (list a b))
(NIL NIL)
CL-USER> (multiple-value-bind (a b)
             (values 1 2)
           (list a b))
(1 2)
CL-USER> (multiple-value-bind (a b)
             (values 1 2 3 4 5)
           (list a b))
(1 2)

For handler-case, it returns the value of the expression form when there is no error. When there is an error, it will execute the corresponding error-handling code.

CL-USER> (handler-case (values 1 2 3)
            (type-error () 'blah1)
            (error () 'blah2))
1
2
3
CL-USER> (handler-case (signal 'type-error)
            (type-error () 'blah1)
            (error () 'blah2))
BLAH1
CL-USER> (handler-case (signal 'error)
            (type-error () 'blah1)
            (error () 'blah2))
BLAH2

For let* form, variables are initialised to nil if init-forms are not provided. Same goes to the let form. The parentheses around these variables without init-forms are unneeded. Example:

CL-USER> (let* (a b (c 3))
            (list a b c))
(NIL NIL 3)

Combining These Knowledges

When dex:get success (i.e. no error), it returns the values of dex:get, which is (values body status response-headers uri stream). With multiple-value-bind, your response-and-status is bound to the value of (list response status-code), which is ("big html" 200).

Since the code in both dex:http-request-bad-request and dex:http-request-failed will only be executed when dex:get failed, both response and status-code have the initial value nil. That's why you get (("big html" 200) nil nil) on success.

When dex:get failed, both response and status-code are setf to new values. Since (setf response nil) mutates the value of response and returns nil (the new value set), and since progn returns the value of last form, your progn returns nil for both error handling cases. That's why your response-and-status is bound to (nil nil) when failed.

公布 2025-01-29 08:45:14

这是 Zacque的答案,描述问题和您对多重VALUE-BIND &amp;其他事情很好。

如该答案中所述,dex:get返回五个值:身体,状态,响应标头,URI,流。但这也标志着失败的各种条件之一。因此,要做的一件明显的事情就是只有一个函数返回相同的五个值(没有理由将其中的某些值包装到列表中),但是处理错误,依靠处理程序返回合适的值。这样的功能非常简单,没有作业。

(defun my-get (final-url)
  (handler-case (dex:get final-url)
    (dex:http-request-failed (e)
      ;; These are probably not the right set of values, but if the
      ;; first one is NIL we're basically OK.
      (values nil
              (dex:response-status e)
              e
              (format nil
                      "The server returned a failed request of ~a status."
                      (dex:response-status e))
              nil))))

如果您真的想将值包装到某种结构中,则可以做到这一点,但是通常很容易处理多个值。例如,此功能的用户现在只能忽略除第一个值以外的所有值,而不是解开各种grot,所以:

(defun my-get-user (url)
  (or (my-get url)
      (error "oops")))

This is an addendum to zacque's answer, which describes the problem and your confusions about multiple-value-bind & other things pretty well.

As mentioned in that answer, dex:get returns five values: body, status, response-headers, uri, stream. But it also signals one of various conditions on failure. So one obvious thing to do is simply have a function which returns the same five values (there's no reason to package some of them into a list), but handles the errors, relying on the handler to return suitable values. Such a function is terribly simple, and has no assignment.

(defun my-get (final-url)
  (handler-case (dex:get final-url)
    (dex:http-request-failed (e)
      ;; These are probably not the right set of values, but if the
      ;; first one is NIL we're basically OK.
      (values nil
              (dex:response-status e)
              e
              (format nil
                      "The server returned a failed request of ~a status."
                      (dex:response-status e))
              nil))))

If you really want to package the values up into some structure you can do that, but generally it's easy to just handle multiple values. For instance a user of this function can now just ignore all but the first value rather than unpacking all sorts of grot, so:

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