欢迎大家赞助一杯啤酒🍺 我们准备了下酒菜:Formal mathematics/Isabelle/ML, Formal verification/Coq/ACL2, C++/F#/Lisp
How Quixote Works
目录 |
The basic idea
Take the URL path and split it apart:
http://example.com/catalog/item/details → ['catalog', 'item', 'details']
Starting at a configured root package (e.g. 'store.web'), follow the URL path down:
http://example.com/ → store.web._q_index() http://.../catalog/ → store.web.catalog() or store.web.catalog._q_index
Special names
- _q_index: If traversal ends up at an object that isn't callable, this name is checked for and called.
- _q_lookup: if an attribute isn't found, this name is checked for and called with the attribute.
This example handles URLs such as /whatever/1/, .../2/, etc.
def _q_lookup (request, component): try: key = int(component) except ValueError: raise TraversalError("URL component is not an integer")
obj = ... database lookup (key) ... if obj is None: raise TraversalError("No such object.")
# Traversal will continue with the ObjectUI instance return ObjectUI(obj)
- _q_access: at every step, this name is checked for and called to perform access checks.
_q_access is always called before traversing any further.This example requires that all users must be logged in.
from quixote.errors import AccessError, TraversalError def _q_access (request): if request.session.user is None: raise AccessError("You must be logged in.") # exits quietly if nothing is wrong def _q_index [html] (request): """Here is some security-critical material ..."""
_q_access is used to impose an access control condition on an entire object; this saves the user from having to add access control checks to each attribute and running the risk of forgetting one. At every step of traversal, _q_access is checked for and called if present. The function can raise an exception to abort further traversal; if no exception is raised, any return value is ignored.
- _q_resolve: like a memoized version of _q_lookup (rarely used)
HTTPRequest class
- .response -- a HTTPResponse instance
- .session -- a Session instance
- request.get_environ('SERVER_PORT', 80)
-- various standard CGI environment vars
- request.get_form_var('user') -- get form variables
- request.get_cookie('session') -- get cookie values
- request.get_url(n=1)
-- get URL of request, chopping off n pieces
- request.get_accepted_types()
-- get a dict mapping {MIME type: quality value}
- browser, version = request.guess_browser_version()
- return request.redirect('../../catalog')
-- redirect to the specified URL
HTTPResponse class
- .headers -- dict of HTTP headers
- .cache -- number of seconds to cache response
- .set_content_type('text/plain')
-- specify MIME content type of the response
- .set_cookie('session', '12345', path='/')
-- return a Set-Cookie header to the client
- .expire_cookie('session')
-- delete cookie from the client
Enabling sessions
Instead of Publisher, use SessionPublisher:
from quixote.publisher import SessionPublisher app = SessionPublisher('quixote.demo')
The request will then have a .session attribute containing a Session instance.
Two other classes:
- SessionManager -- stores/retrieves sessions
- Session -- can hold .user attribute
Stand-alone
Running a server on localhost is really easy:
import os, time from quixote.server import medusa_http if __name__ == '__main__': s = medusa_http.Server('quixote.demo', port=8000) s.run()