|
||||
|
||||
Construyendo servicios webUna de las aplicaciones que antes surgieron en el entorno web fueron los templates. Smarty y muchas otras tecnologías han venido a suplir la necesidad de no tener que escribir echo en cada control dinámico que queramos generar, permitiendo la evolución del modelo MVC. En este ejemplo vamos a ver como podemos pedirle al servidor HTML, a través de AJAX, y como insertarlo en un espacio de nuestra página que previamente hemos definido para ello. Si leemos el fichero dyn_html.php, nos encontraremos con esto:
dyn_html.php $("#link").click(function() { $.ajax({ url: "phpAjax/dynamic_html.php?type=1", dataType: "html", async: true, success : function(data, textStatus) { $("#capa").html(data); } }); }); $("#bold").click(function() { $.ajax({ url: "phpAjax/dynamic_html.php?type=2", dataType: "html", async: true, success : function(data, textStatus) { $("#capa").html(data); } }); }); Básicamente lo que hace es que al hacer click en cada uno de los elementos, muestra, en una capa, un vínculo a Google o un texto en negrita en función del elemento en el que hayamos pulsado. Lo primero distinto que nos encontramos es ("#link"), siendo la almohadilla lo distinto respecto del ejemplo anterior. La almohadilla significa que vamos a acceder un elemento de HTML por su atributo id, del mismo modo que si hiciésemos javascript:getElementById(id_elemento), y lo que hacemos es definir un manejador para el evento onClick de ese elemento con id link. Realizamos una llamada AJAX a una URL a la que pasamos un parámetro type, y le especificamos que el tipo de retorno de dato es HTML. Por último, definimos una función para el evento success, que se dispararía al realizarse (que no completarse), la llamada AJAX con éxito. Esa función es un callback, con lo que admite los argumentos data y textStatus. data contiene el HTML a mostrar en el control con id capa, y eso es lo que hace la función al recibir este parámetro. Haced alert(textStatus); para que veais lo que contiene, es interesante.
Y eso es todo por parte del código de cliente. Veamos ahora el código de servidor:
phpAjax/dynamic_html.php <?php $tipo = (empty($_GET["type"])) ? 1 : $_GET["type"]; if($tipo == 1) { echo "<a href=\"http://www.google.es\">Google</a>"; } else { echo "<b>Texto en negrita</b>"; } die(); ?> ¿Sencillo, no? Se recibe el parámetro $_GET["type"] y en caso de que no exista se inicializa a 1, para mostrar el vínculo a Google. Se llama a la miniclase que lo único que hace es poner un atributo privado a un valor determinado en función del parámetro del constructor, y por último llamamos a die(), y le pasamos el parámetro que representa el getter del atributo html, para que el script termine su ejecución y muestre el código HTML. Muy simple, pero un poco mas útil que el ejemplo anterior. Seguro que a mas de uno se le van ocurriendo ideas para generar un montón de HTML en servidor y mostrarlo de esta manera en vez de con recarga de página y templates. Este script es muy sencillo y tambien es accesible por navegador tecleando directamente la URL, mostrando el vínculo a Google en caso de no suministrarse argumento type.
Por si alguien se lo estaba preguntando, no es posible acceder, a través de llamadas AJAX, a una página que no esté en el mismo dominio que el script que la esta intentando llamar. Esto es una medida de seguridad implementada en AJAX que nos permite estar seguros de que nadie podrá acceder a los scripts a través de sus propias llamadas AJAX. Pero, ¿y el navegador?. En este capítulo vamos a ver algo de desde donde vienen las peticiones AJAX, y como prevenir accesos no deseados a nuestra página. Lo que vamos a definir es una clase base que llamaremos JSONWebServiceBase, y a partir de ella heredaremos todas las clases que vayan a ser accedidas como un servicio web. La clase está en el fichero phpAjax/JSONWebServiceBase.class.php de los ficheros del tutorial.
phpAjax/JSONWebServiceBase.class.php <?php define("REQUEST_DENIED", 0); define("REQUEST_ACCEPT", 1); define("WHO_AJAX", 0); define("WHO_BROWSER", 1); define("HOW_GET", 0); define("HOW_POST", 1); define("XMLHTTP_DENIED", "Las peticiones a través de AJAX no se aceptan en este servicio"); define("GET_DENIED", "Las peticiones GET no se aceptan en este servicio"); define("POST_DENIED", "Las peticiones POST no se aceptan en este servicio"); define("BROWSER_DENIED", "Las peticiones por navegador no se aceptan en este servicio"); define("REQUEST_STRING_REQUIRED", "Las peticiones a este servicio deben ir acompañadas de una serie de argumentos"); define("BAD_ARGUMENT_LIST", "La lista de nombres de argumento suministrada no es un elemento de tipo Array"); define("ARGUMENTS_NOT_SAME", "La lista de nombres de argumento no coincide con los nombres de argumento sumistrados al servicio"); define("REQUIRE_QUERY_NULL", "No se puede especificar el parámetro \$require_query del contructor sin que vaya acompañado de \$arg_list como array conteniendo los pares clave-valor"); class JSONWebServiceBase { /** * El array de JSON para devolver las peticiones * @var unknown_type */ protected $_jsonArray; /** * Almacenaje interno de las variables que entran al servicio * @var unknown_type */ protected $_requestData; /** * Almacenaje interno de los métodos de acceso al servicio * @var unknown_type */ protected $_accessMethod; /** * Construye un servicio web que acepta peticiones HTTP y devuelve datos JSON * @param boolean $xmlhttp_request Accesso a traves de XMLHttpRequest * @param boolean $post_request Accesso a traves de POST * @param boolean $get_request Accesso a traves de GET * @param boolean $browser_request Acceso a traves de navegador * @param boolean $require_query Si se requieren datos para acceder al servicio * @param array $arg_list La lista de nombres de las variables que acepta el servicio * @return JSONWebServiceBase */ protected function __construct($xmlhttp_request, $post_request, $get_request,$browser_request, $require_query, $arg_list) { $this->_accessMethod["how"] = ($_SERVER["REQUEST_METHOD"] == "GET") ? HOW_GET : HOW_POST; $this->_accessMethod["who"] = (array_key_exists("HTTP_X_REQUESTED_WITH", $_SERVER) && $_SERVER["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest") ? WHO_AJAX : WHO_BROWSER; if($xmlhttp_request == REQUEST_DENIED && $this->_accessMethod["who"] == WHO_AJAX) { $this->error(XMLHTTP_DENIED); } if($browser_request == REQUEST_DENIED && $this->_accessMethod["who"] == WHO_BROWSER) { $this->error(BROWSER_DENIED); } if($get_request == REQUEST_DENIED && $this->_accessMethod["how"] == HOW_GET) { $this->error(GET_DENIED); } if($post_request == REQUEST_DENIED && $this->_accessMethod["how"] == HOW_POST) { $this->error(POST_DENIED); } if($require_query == true && !is_array($arg_list)) { $this->error(REQUIRE_QUERY_NULL); } if($arg_list != null && !is_array($arg_list)) { $this->error(BAD_ARGUMENT_LIST); } //¿Qué datos me traes? if($_SERVER["QUERY_STRING"] != "") { $tempArray = explode("&", $_SERVER["QUERY_STRING"]); for($i=0;$i<count($tempArray);$i++) { $tempArray_2 = explode("=", $tempArray[$i]); $this->_requestData[addslashes($tempArray_2[0])] = addslashes($tempArray_2[1]); unset($tempArray_2); } unset($tempArray); } else { //... quizás yo sea accesible por POST y los traigas por ahí if($post_request == REQUEST_ACCEPT && count($_POST) > 0) { $claves = array_keys($_POST); for($i=0;$i<count($claves);$i++) { $this->_requestData[addslashes($claves[$i])] = addslashes($_POST[$claves[$i]]); } unset($claves); } else { $this->error(REQUEST_STRING_REQUIRED); } } $this->compareKeys($arg_list); } /** * Compara que los arrays de nombres de argumento suministrados en la QUERY_STRING y en el argumento * $arg_list del constructor sean exactamente iguales * @param array $original */ private function compareKeys($original) { $tempKeys = array_keys($this->_requestData); $n = (count(array_diff($tempKeys, $original)) - count(array_diff($original, $tempKeys))); unset($tempKeys); if($n != 0) { $this->error(ARGUMENTS_NOT_SAME); } } /** * Escribe los errores que se generan en la clase * @param string $errCode El codigo de error de los define del principio del fichero de la clase * @return string Devuelve el error (en JSON), o muestra el objeto entero por navegador */ private function error($errCode) { $this->_jsonArray["CODE"] = "ERROR"; $this->_jsonArray["EXPLAIN"] = utf8_encode($errCode); if($this->_accessMethod["who"] == WHO_BROWSER) { die(utf8_encode($errCode)); } else { $this->dieJSON(); } } /** * Devuelve el array de datos que hayamos creado en formato de JSON * @return array El array de JSON */ public function dieJSON() { die(json_encode($this->_jsonArray)); } /** * Escribe la clase por pantalla * @return string El resultado del print_r() */ protected function toString() { echo "<pre>"; print_r($this); echo "</pre>"; } } ?> La clase es simple, lo que hace es comprobar lo primero de donde le está viniendo la petición, y en función de los argumentos de su constructor termina su ejecución si encuentra un error. Tengamos en cuenta que la idea es que este servicio sea accesible, mayormente, a través de AJAX, aunque la idea es poder reutilizarlo para cualquier tipo de petición HTTP que nos pueda venir. Una vez ha comprobado que ninguna condición de acceso es incorrecta, comienza a parsear las variables dentro del miembro $this->_requestData, el cual tiene la estructura de array de pares clave de texto-valor. Asimismo, el servicio, al saber de donde le vienen las peticiones, termina su ejecución con la salida a navegador o a un array de JSON. En el capítulo 5 del tutorial vamos a ver por qué es esto importante, de momento tomémoslo como una funcionalidad mas de la clase. Y con esta clase en mente, pasemos a la siguiente parte: heredar de ella para construir nuestros propios servicios.
|
||||
|
||||