3

宿題の1つでは、Webサーバーを起動する必要があり、Webサーバーのルートにアクセスすると、CGIスクリプトが実行されます。

しかし、localhost:8080を開くと、次のエラーメッセージが表示されます。

Error code 403.

Message: CGI script is not executable ('/cgi-bin/todolist.cgi').

Error code explanation: 403 = Request forbidden -- authorization will not help.

私のサーバーコード:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import getopt
import logging
import BaseHTTPServer
from CGIHTTPServer import CGIHTTPRequestHandler
import json
from DBBackend import TodoDao

# CRUD to REST conventions
# POST   Create
# GET    Retrieve
# PUT    Update
# DELETE Delete

"""
API REST del servidor.

GET /todos        - Recupera la lista de tareas (ToDos)
DELETE /todos/id  - Elimina la tarea con el id especificado
POST /todos       - Añade una nueva tarea con los valores especificados como parámetros
PUT /todos/id     - Actualiza los valores espcificados en los parámetros para la tarea con
                    el id dado

Tanto los parámetros (en el cuerpo de la petición) como las respuestas son en formato JSON.
"""

logging.basicConfig(level=logging.DEBUG)

class RESTHTTPRequestHandler(CGIHTTPRequestHandler):

    dao = TodoDao()
    res_string = dao.tableName
    res_path = "/" + res_string

    def _GET(self):
        if self.path == self.res_path:
            tasks = self.dao.findTasks()
            return {'code': 'ok', 'data': tasks}
        else:
            _,res,id = self.path.split("/")
            int(id)
            assert(res==self.res_string)
            data = self.dao.retrieveTask(id)
            return {'code': 'ok', 'data': data}

    def _POST(self):
        assert(self.path == self.res_path)
        if 'Content-length' in self.headers:
            data = json.loads(self.rfile.read(int(self.headers['Content-length'])))
        else:
            data = json.load(self.rfile)
        self.dao.createTask(data)
        return {'code': 'ok'}


    def _PUT(self):
        _,res,id = self.path.split("/")
        int(id)
        assert(res==self.res_string)
        if 'Content-length' in self.headers:
            data = json.loads(self.rfile.read(int(self.headers['Content-length'])))
        else:
            data = json.load(self.rfile)
        self.dao.updateTask(id, data)
        return {'code': 'ok'}


    def _DELETE(self):
        _,res,id = self.path.split("/")
        int(id)
        assert(res==self.res_string)
        self.dao.deleteTask(id)
        return {'code': 'ok'}


    def _send(self, data):
        response = json.dumps(data)
        self.send_response(200)
        self.send_header("Content-type", "application/json")
        self.send_header("Content-Length", len(response))
        self.end_headers()
        self.wfile.write(response)


    # El BaseHTTPRequestHandler no está pensado para ésto :(
    def do_POST(self):
        self._reroute()
    def do_PUT(self):
        self._reroute()
    def do_GET(self):
        self._reroute()
    def do_DELETE(self):
        self._reroute()
    def _reroute(self):
        try:
            if self.path.startswith(self.res_path):
                method_name = '_' + self.command
                method = getattr(self, method_name)
                try:
                    self._send(method())
                except (ValueError, AssertionError):
                    self.send_error(400, "Invalid request")
                except:
                    logging.exception("Database access error")
                    self.send_error(500, "DDBB error")
            else:
                if self.path == "/" or self.path == "/index.html":
                    self.path = "/cgi-bin/todolist.cgi"
                method_name = 'do_' + self.command
                method = getattr(CGIHTTPRequestHandler, method_name)
                method(self)
        except AttributeError:
            self.send_error(501, "Unsupported method (%r)" % self.command)


#---- Defaults
port = "8080"
basedir = "www/"
#----

#----------------------------------------
def usage():
    print "Uso: " +  os.path.basename(sys.argv[0]) + " -h -p port"
    print "     -h         Muestra este mensaje"
    print "     -p port    Sirve en el puerto indicado (def={0})".format(port)
    print "     -d dirname Sirve el contenido del directorio indicado (def={0})".format(basedir)

#----------------------------------------

try:
    opts, args = getopt.getopt(sys.argv[1:], "hp:d:", ["help", "port=", "dir="])
except getopt.GetoptError:
    usage()
    sys.exit(2)

for o, a in opts:
    if o in ("-h", "--help"):
        usage()
        sys.exit()
    if o in ("-p", "--port"):
        port = a
    if o in ("-d", "--dir"):
        basedir = a

if (port == None):
    usage()
    sys.exit()

try:
    address = ('', int(port))
except ValueError:
    usage()
    sys.exit(2)

httpd = BaseHTTPServer.HTTPServer(address,
                                  RESTHTTPRequestHandler)
os.chdir(basedir)
httpd.serve_forever()

そして私のtodolist.cgi:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cgi
import sys
import os
import datetime
import locale
# TBD: Usar el locale del cliente
locale.setlocale(locale.LC_TIME,'')
date_format = locale.nl_langinfo(locale.D_FMT)

sys.path.append(os.path.join(os.path.dirname(__file__), "../.."))
import DBBackend

print "Content-Type: text/html"
print ""
print """
<!doctype html>

<html lang="es">

<head>
<meta charset="utf-8"/>
<title>[IPM] Lista de tareas</title>
<meta name="author" content="David Cabrero"/>
<meta name="HandheldFriendly" content="true"/>
<meta name="viewport" content="width=device-width, initial-scale=1,maximun-scale=1,user-scalable=no"/>

<meta http-equiv="X-UA-Compatible" content="IE=ecdge,chrome=1">
<!--[if lt IE 9]>
  <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  <script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
<![endif]-->

<link rel="stylesheet" type="text/css" href="css/default.css" />
<link rel="stylesheet" type="text/css" href="css/wide-screen.css" 
      media="only screen and (min-width : 1024px)" />
<script src="js/todolist.js"></script>
</head>

<body>

<header>
<h1>ToDo</h1>
</header>

<ul>
"""
fname = os.path.join(os.getcwd(), "../", DBBackend.DATABASEFILENAME)
li = """
  <a href="#dialog"><li data-task-id="{0}">
    <p class="desc">{1}</p>
    <time datetime="{2}">{3}</time>
    <p class="done" data-task-done="{4}">{5}</p>
  </li></a>
"""
for task in DBBackend.TodoDao(fname).findTasks():
    id = str(task['id'])
    desc = task['desc'].encode('utf-8')
    deadline = datetime.datetime.strptime(task['deadline'], "%Y-%m-%d")
    done = task['done'] == 1
    print li.format(id, desc, deadline.strftime("%Y-%m-%d"), deadline.strftime(date_format),
                    "yes" if done else "no", "Hecho" if done else "Sin hacer")

print """
</ul>


<div id="updateTask" class="dialog"><div>
  <h1>Actualizar tarea</h1>
  <form>
    <p><input type="text" name="task_desc" placeholder="task description" autofocus="autofocus" /></p>
    <p><input type="date" name="task_deadline" placeholder="deadline" /></p>
    <p><input type="checkbox" name="task_done" /></p>
    <p class="okCancel">
      <button name="ok">OK</button> 
      <button name="cancel">Cancel</button>
    </p>
  </form>
</div></div>

</body>

</html>
"""

print """
"""

すべてのコードは教師から提供されたものであるため(Webアプリケーションを実行する必要があります)、サーバーを機能させることができない場合の開始方法がわかりません。私はWindows7とPythonforWindows(バージョン2.7)も実行しています。お役に立てば幸いです。

4

2 に答える 2

6

問題はコードではなく、ファイルシステムのパーミッションにあります。cgiファイルは実行可能としてマークする必要があります。chmod a+x todolist.cgiこれは、cgi-binディレクトリのシェルからで実行できます。

于 2012-11-14T21:29:39.350 に答える
4

OK、これをWindowsで機能させるために必要なことは、スクリプトの名前をtodolist.cgiからtodolist.pyに変更し、サーバーコードの次の行を変更することです。

self.path = "/cgi-bin/todolist.cgi"

これを次のように変更します。

self.path = "/cgi-bin/todolist.py"

これで、大騒ぎせずにすべてをWindowsで動作させることができます。それはすべて、CGIHTTPServerの内部動作と実行可能ファイルの処理方法に関係しています。私はそれを機能させるために他のさまざまなトリック(そしてモンキーパッチCGIHTTPServer.executable ...のような完全なハック)を試しましたが、これは最も簡単なようでした。

于 2012-11-14T21:30:16.840 に答える