【问题标题】:How to send response headers and status from CGI scripts如何从 CGI 脚本发送响应标头和状态
【发布时间】:2014-09-12 03:44:07
【问题描述】:

我正在使用CGIHTTPServer.py 创建简单的 CGI 服务器。如果某些操作出错,我希望我的 CGI 脚本处理响应代码。我该怎么做?

我的 CGI 脚本中的代码 sn-p。

if authmxn.authenticate():
     stats = Stats()
     print "Content-Type: application/json"
     print 'Status: 200 OK'
     print

     print json.dumps(stats.getStats())
 else:
     print 'Content-Type: application/json'
     print 'Status: 403 Forbidden'
     print
     print json.dumps({'msg': 'request is not authenticated'})

一些来自请求处理程序的sn-p,

def run_cgi(self):
'''
    rest of code
'''
    if not os.path.exists(scriptfile):
        self.send_error(404, "No such CGI script (%s)" % `scriptname`)
        return
    if not os.path.isfile(scriptfile):
        self.send_error(403, "CGI script is not a plain file (%s)" %
                        `scriptname`)
        return
    ispy = self.is_python(scriptname)
    if not ispy:
        if not (self.have_fork or self.have_popen2):
            self.send_error(403, "CGI script is not a Python script (%s)" %
                            `scriptname`)
            return
        if not self.is_executable(scriptfile):
            self.send_error(403, "CGI script is not executable (%s)" %
                            `scriptname`)
            return

    if not self.have_fork:
        # Since we're setting the env in the parent, provide empty
        # values to override previously set values
        for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
                  'HTTP_USER_AGENT', 'HTTP_COOKIE'):
            env.setdefault(k, "")

    self.send_response(200, "Script output follows") # overrides the headers

    decoded_query = query.replace('+', ' ')

【问题讨论】:

  • 究竟是什么问题?你所拥有的似乎应该可以工作。你在谈论异常处理吗? (在这种情况下,您可以捕获异常并返回 http 响应代码 500 内部服务器错误。
  • @mhawke 你可以在上面的评论中检查上面的代码,你会看到它的覆盖标题。你明白这个问题了吗?

标签: python cgi


【解决方案1】:

可以实现对覆盖 HTTP 状态行(HTTP 响应的第一行,例如 HTTP/1.0 200 OK)的 Status: code message 标头的支持。这需要:

  1. 对CGIHTTPRequestHandler 进行子类化,以诱骗将CGI 脚本的输出写入StringIO 对象,而不是直接写入套接字。
  2. 然后,一旦 CGI 脚本完成,使用 Status: 标头中提供的值更新 HTTP 状态行。

这是一个 hack,但还不错,不需要修补任何标准库代码。

import BaseHTTPServer
import SimpleHTTPServer

from CGIHTTPServer import CGIHTTPRequestHandler
from cStringIO import StringIO

class BufferedCGIHTTPRequestHandler(CGIHTTPRequestHandler):
    def setup(self):
        """
        Arrange for CGI response to be buffered in a StringIO rather than
        sent directly to the client.
        """
        CGIHTTPRequestHandler.setup(self)
        self.original_wfile = self.wfile
        self.wfile = StringIO()
        # prevent use of os.dup(self.wfile...) forces use of subprocess instead
        self.have_fork = False

    def run_cgi(self):
        """
        Post-process CGI script response before sending to client.
        Override HTTP status line with value of "Status:" header, if set.
        """
        CGIHTTPRequestHandler.run_cgi(self)
        self.wfile.seek(0)
        headers = []
        for line in self.wfile:
            headers.append(line)    # includes new line character
            if line.strip() == '':  # blank line signals end of headers
                body = self.wfile.read()
                break
            elif line.startswith('Status:'):
                # Use status header to override premature HTTP status line.
                # Header format is: "Status: code message"
                status = line.split(':')[1].strip()
                headers[0] = '%s %s' % (self.protocol_version, status)

        self.original_wfile.write(''.join(headers))
        self.original_wfile.write(body)


def test(HandlerClass = BufferedCGIHTTPRequestHandler,
         ServerClass = BaseHTTPServer.HTTPServer):
    SimpleHTTPServer.test(HandlerClass, ServerClass)

if __name__ == '__main__':
    test()

不用说,这可能不是最好的方法,您应该查看非 CGIHTTPServer 解决方案,例如一个微框架,例如 bottle,一个合适的网络服务器(根据内存,不建议将 CGIHTTPServer 用于生产环境),fastcgi 或 WSGI - 仅举几个选项。

【讨论】:

【解决方案2】:

使用标准库 HTTP 服务器,您无法做到这一点。来自library documentation:

注意由 CGIHTTPRequestHandler 类运行的 CGI 脚本不能执行重定向(HTTP 代码 302),因为代码 200(脚本输出如下)是在执行 CGI 脚本之前发送的。这会抢占状态码。

这意味着服务器不支持脚本中的Status: <status-code> <reason> 标头。您正确识别了代码中显示此支持不存在的部分:服务器甚至在运行脚本之前发送状态代码 200。您无法从脚本中更改它。

Python bugtracker 中有几张与此相关的票证,其中一些带有补丁,例如,请参阅issue13893。因此,您的一个选择是修补标准库以添加此功能。

但是,我强烈建议您改用其他技术而不是 CGI(或运行真正的 Web 服务器)。

【讨论】:

  • 太棒了...我可以使用 WSCGI 来实现吗?
猜你喜欢
  • 2015-04-06
  • 2010-09-30
  • 2023-03-10
  • 2019-04-11
  • 1970-01-01
  • 2018-10-03
  • 2016-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多