如何在此HTTP-Request接口上正确使用常见的LISP多值绑定和处理程序?
我正在使用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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的问题是您未能理解
多价值键
,处理程序
和let*
工作。 (可能还setf
和prog
。)TLDR
快速修复:
输出:
那么,为什么要获得当前结果?
对于
多重值键
,它将从values
表单返回的多个值绑定到相应的变量中。对于
Handler-case
,它在没有错误时返回表达式的值。出现错误时,它将执行相应的错误处理代码。对于
令*
表单,如果未提供init-forms
,变量将初始化为nil
。同样,令
表单。这些变量围绕没有初始形式的括号是不必要的。示例:组合这些知识
在
dex:get
成功(即无错误)时 ,它返回dex的值:get
,,(值body身体状态响应 -标题URI流)
。使用多重值BIND
,您的wenders-and-STATUS
绑定到的值(列表响应状态代码)
,它是(“ Big HTML” 200)
。由于两个
dex中的代码:http-request-bad-request
和dex:http-request-failed
仅在dex:get 失败,
响应
和状态代码
具有初始值nil
。这就是为什么您获得((“ Big HTML” 200)nil nil)
成功的原因。当
dex:get
失败时,响应>
和status-code
是setf
对新值。由于(setf响应nil)
突变响应
和返回nil
(新值集)和由于Prom
返回最后一个表单的值,因此您的Progn
返回nil
nil 对于两个错误处理案例。这就是为什么您的响应和状态
在失败时绑定到(nil nil)
。Your problem is that you failed to understand how
multiple-value-bind
,handler-case
, andlet*
work. (Probably alsosetf
andprogn
.)TLDR
Quick fix:
Output:
So, Why Do You Get Your Current Result?
Preliminary
For
multiple-value-bind
, it binds multiple values returning from avalues
form into the corresponding variables.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.For
let*
form, variables are initialised tonil
ifinit-forms
are not provided. Same goes to thelet
form. The parentheses around these variables without init-forms are unneeded. Example:Combining These Knowledges
When
dex:get
success (i.e. no error), it returns the values ofdex:get
, which is(values body status response-headers uri stream)
. Withmultiple-value-bind
, yourresponse-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
anddex:http-request-failed
will only be executed whendex:get
failed, bothresponse
andstatus-code
have the initial valuenil
. That's why you get(("big html" 200) nil nil)
on success.When
dex:get
failed, bothresponse
andstatus-code
aresetf
to new values. Since(setf response nil)
mutates the value ofresponse
and returnsnil
(the new value set), and sinceprogn
returns the value of last form, yourprogn
returnsnil
for both error handling cases. That's why yourresponse-and-status
is bound to(nil nil)
when failed.这是 Zacque的答案,描述问题和您对
多重VALUE-BIND &amp;其他事情很好。
如该答案中所述,
dex:get
返回五个值:身体,状态,响应标头,URI,流。但这也标志着失败的各种条件之一。因此,要做的一件明显的事情就是只有一个函数返回相同的五个值(没有理由将其中的某些值包装到列表中),但是处理错误,依靠处理程序返回合适的值。这样的功能非常简单,没有作业。如果您真的想将值包装到某种结构中,则可以做到这一点,但是通常很容易处理多个值。例如,此功能的用户现在只能忽略除第一个值以外的所有值,而不是解开各种grot,所以:
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.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: