Manejadores de páginas
Un recurso no es un objeto de almacenamiento. Un recurso no es el mecanismo que el servidor usa para manejar un objeto de almacenamiento. Un recurso es una asignación conceptual -- El servidor recibe el identificado (el que identifica la asignación) y lo aplica a la implementación actual de asignación (usualmente una combinación de inspecciones profundas de un árbol y/o tablas hash) para encontrar la implementación del manejador actualmente responsable y esta implementación del manejador entonces seleccionara la acción + respuesta apropiada basada en el contenido de la solicitud. Todas estas características especificas a la implementación se esconden detrás de la interfaz Web; el cliente que solo tiene acceso a la interfaz web no puede deducir la naturaleza de estas.
Roy Fielding
A la hora de responder a un recurso en Internet, no se responde con el recurso, porque los "recursos" son conceptos. Lo que se responder son las representaciones del recurso y CherryPy usa manejadores de páginas para lograr eso. Los manejadores de paginas son funciones; CherryPy llama una por cada solicitud y usa su respuesta, usualmente en forma de una cadena de texto de HTML, como la representación.
CherryPy usa la salida de la función apropiada manejadora de la página, la enlaza a cherrypy.response.body y esto se convierte en el cuerpo de la entidad de la respuesta HTTP. La función manejadora de página (y casi cualquier otra parte de CherryPy) puede ser asignar directamente cherrypy.response.status y .headers.
Despachadores
Antes de que CherryPy pueda llamar a los manejadores de pagina, debe saber 1) donde están y 2) a cual llamar según un 'identificador' (URI) determinado. En CherryPy usamos un objeto despachador para:
- Entender el arreglo de los manejadores
- Encontrar la función manejadora apropiada
- Envolver la función manejadora en un objeto PageHandler (ver abajo)
- Asignarle cherrypy.request.handler (a la envoltura de la función manejadora)
- Recolectar las entradas de configuración y ponerlas en cherrypy.request.config
- Recolectar los componentes de "rutas virtuales"
CherryPy 3 tiene un arreglo predeterminado de manejadores (ver abajo), pero también permite intercambiarlo por cualquier otro arreglo que el programador pueda pensar e implementar.
Despachador predeterminado
El despachador predeterminado de CherryPy usa un árbol de manejadores y almacena la raíz del árbol en Application.root. Por ejemplo:
class Raiz: def index(self, nombre): return "Hola, %s!" % nombre class Rama: def hoja(self, tamano): return str(int(tamano) + 3) raiz = Raiz() raiz.rama = Rama() ap = cherrypy.tree.mount(raiz, script_name='/')
Cuando se procesa una solicitud, la URI se divide en componentes, se buscan coincidencias entre cada uno y los nodos del árbol. Cualquier componente restante será una componente de "ruta virtual" y será enviado como argumento de posición. Por ejemplo, la URI "/rama/hoja/4" resultara en una llamada a ap.raiz.rama.hoja(4).
Se pueden usar puntos en una URL como en /ruta/a/mi.html pero los métodos en Python no pueden tener puntos. Para solucionar esto, el despachador predeterminado convierte todos los puntos en la URL a guiones bajos antes de intentar encontrar el manejador de la página. Por lo tanto, en el ejemplo, el manejador de pagina debe ser llamado def mi_html. Pero esto significa que la pagina también estará accesible en la URL /ruta/a/mi_html. Si este recurso debe ser protegido (con autenticación por ejemplo), debes proteger ambas URLs.
Otros despachadores
Pero anteriormente el Sr. Fielding menciono dos tipos de "implementaciones de asignación": arboles y tablas hash ('diccionarios' en Python). Algunos desarrolladores consideran que los arboles son difíciles de cambiar en la evolución de la aplicación y prefieren usar diccionarios (o un arreglo de listas). En estas esquemas, la clave de la asignación es usualmente una expresión regular y el valor es la función manejadora. Por ejemplo:
def raiz_index(nombre): return "Hola, %s!" % nombre def rama_hoja(tamano): return str(int(tamano) + 3) mappings = [ (r'^/([^/]+)$', raiz_index), (r'^/rama/rama/(\d+)$', rama_hoja), ]
En CherryPy se puede usar otro despachador que no sea el predeterminado. AL usar otro despachador (o escribir uno nuevo), se obtiene control completo sobre el arreglo y el comportamiento de los manejadores de páginas (y de la configuración). Para usar otro despachador hay que asignar la entrada de configuración request.dispatch con el despachador deseado:
d = cherrypy.dispatch.RoutesDispatcher() d.connect(name='cancun', route='cancun', controller=City('Can Cun')) d.connect(name='cozumel', route='cozumel', controller=City('Cozumel'), action='index', conditions=dict(method=['GET'])) d.mapper.connect('cozumel', controller='cozumel', action='update', conditions=dict(method=['POST'])) conf = {'/': {'request.dispatch': d}} cherrypy.tree.mount(root=None, config=conf)
Unas cuantas notas sobre el ejemplo anterior:
- Dado que el despachador RoutesDispatcher? (despachador de rutas) no tiene jerarquía, no hay nada que asignarle a la raiz en cherrypy.tree.mount; asi que se le asigna None (Ninguna) en este caso.
- Normalmente se usa el mismo despachador para toda la aplicación, así que es común asignarlo a la raíz ("/"). Pero se pueden usar diferentes despachadores para diferentes rutas de ser necesario.
- Ya que el despachador es tan importante para encontrar los manejadores (y sus ancestros), este es uno de los pocos casos en los que no se puede usar _cp_config; es un problema tipo el huevo y la gallina; no se le puede pedir a un manejador que aun no se ha encontrado como quiere ser encontrado.
- Puesto que las Routes son explícitas, no hay necesidad de fijar la exposed attribute. Siempre todas las Routes se exponen.
CherryPy incluye los siguientes despachadores adicionales (en cherrypy.dispatch):
MethodDispatcher
Despachador de métodos, agrega request.method, por ejemplo:
class Raiz: exposed = True def __init__(self, *cosas): self.things = list(cosas) def GET(self): return repr(self.cosas) def POST(self, thing): self.things.append(cosas) raiz = Raiz(1, 2, 3) d = cherrypy.dispatch.MethodDispatcher() conf = {'/': {'request.dispatch': d}} cherrypy.tree.mount(raiz, "/", conf)
RoutesDispatcher
Despachador de rutas, usa Rutas (contenido en ingles) para analizar la solicitud-URI. Ver el ejemplo anteriormente dado.
XMLRPCDispatcher
Despachador XMLRPC, un compañero critico de la herramienta XMLRPC; este despachador le quita el '/RPC2/' inicial de la URL y comprueba que la URL termine con diagonal.
Argumentos:
- next_dispatcher: El despachador que será usado a continuación; XMLRPCDispatcher modificara la ruta y la pasara al siguiente despachador. Este argumento es obligatorio.
VirtualHost
Selecciona un manejador de rama diferente basado en el encabezado 'Host'.
Argumentos:
- next_dispatcher: El despachador que será llamado a continuación. VirtualHost le agregara un prefijo a la ruta y se lo pasara al siguiente despachador. Este argumento es obligatorio.
- use_x_forwarded_host: de ser True (Verdadero) (valor predeterminado), se le agregara un encabezado 'X-Forwarded-Host' al encabezado 'Host'.
- **domains: Se usara cualquier argumento en la forma de pares de (dominio, prefijo de ruta).
Objetos PageHandler
Dado que el despachador asigna cherrypy.request.handler, este también puede controlar la entrada y la salida de la función manejadora envolviendo el manejador en sí. El despachador predeterminado pasa componentes de "ruta virtual" como argumentos posicionales y también pasa la cadena de texto de la consulta (query-string)y los parámetros de entidad (GET y POST) como argumentos de clave. Se usa un objeto PageHandler para esto, el cual luce asi:
class PageHandler(object): """Ejecutable, asigna response.body.""" def __init__(self, ejecutable, *args, **kwargs): self.callable = ejecutable self.args = args self.kwargs = kwargs def __call__(self): return self.ejecutable(*self.args, **self.kwargs)
El PageHandler predeterminado en si es un poco más complicado (porque los argumentos son enlazados después), pero esta es la idea. Es muy fácil proveer un comportamiento nuevo, ya sea que controle la entrada o que modifique la salida. Solo hay que recordar que cualquier cosa que el manejador regrese será enlazado a cherrypy.response.body y será usado como la entidad de respuesta.
Configuración
El arreglo predeterminado de los manejadores de CherryPy es un árbol. Esto permite una técnica de configuración poderosa: la configuración puede ser amarrada a un nodo en el árbol y caer en cascada a todos los hijos de ese nodo. Dado que la asignación de URI's a manejadores no es siempre de 1 a 1, esto da una flexibilidad que no puede lograrse fácilmente en otros arreglos mas planos.
Pero puesto que el arreglo de la configuración está directamente relacionado al arreglo de los manejadores, es la responsabilidad el despachador recolectar la configuración por cada manejador, mezclarla con la configuración global por URI y enlazar el resultado en cherrypy.request.config. Este diccionario es de una profundidad de un nivel y contendrá las entradas de configuración que están en efecto para la solicitud actual.

