如何从CGI脚本发送响应头和状态

4 投票
2 回答
6352 浏览
提问于 2025-04-21 05:52

我正在使用 CGIHTTPServer.py 来创建一个简单的CGI服务器。我希望我的CGI脚本能够处理响应代码,以便在某些操作出错时能够正确反馈。请问我该怎么做呢?

以下是我CGI脚本中的一段代码。

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'})

这是请求处理程序中的一部分代码,

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('+', ' ')

2 个回答

3

使用标准库的HTTP服务器,你是无法做到这一点的。根据库的文档

注意,由CGIHTTPRequestHandler类运行的CGI脚本无法执行重定向(HTTP代码302),因为在执行CGI脚本之前,服务器会先发送200代码(脚本输出跟随)。这就导致状态码被提前发送了。

这意味着服务器不支持来自脚本的Status: <status-code> <reason>头信息。你已经正确识别了代码中显示这一点的部分:服务器在运行脚本之前就发送了200状态码。你无法在脚本内部更改这一点。

在Python的错误追踪系统中,有几个相关的记录,有些还提供了修复方案,比如issue13893。所以你可以选择修补标准库来添加这个功能。

不过,我强烈建议你换用其他技术,而不是CGI(或者直接使用一个真正的网络服务器)。

4

可以实现一个支持 Status: code message 头的功能,这个功能可以覆盖HTTP状态行(HTTP响应的第一行,比如 HTTP/1.0 200 OK)。要做到这一点,需要:

  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 - 这些都是不错的选择。

撰写回答