3ENGINE

Programación y otros cachivaches

Etiqueta: http

Tecnologia

Cómo mostrar una imagen de progreso con jquery ajax (ajax loader)


Para mostrar una imagen de progreso con jquery ajax antes de nada tenemos que conseguir un gif animado. Por ejemplo podemos obtener de ajaxload.info un gif como este:

loading

Lo que yo quiero conseguir es que aparezca el gif animado sea cual sea la llamada jquery ajax. Esto se consigue con el siguiente código en jquery. Básicamente lo que hace es mostrar un div con id=ajaxBusy que contiene el gif, al iniciar una llamada jquery ajax y esconder el div al finalizar la misma:

$(document).ajaxStart(function () {
    $('#ajaxBusy').show();
}).ajaxStop(function () {
    $('#ajaxBusy').hide();
});

El código HTML con el gif es el siguiente:

<div id="ajaxBusy">
  <p>
    <img src="loading.gif">
  </p>
</div>

Que necesita su correspondiente css:

#ajaxBusy
{
    display: none; 
    margin: 0px;
    paddingLeft: 0px;
    paddingRight: 0px;
    paddingTop: 0px;
    paddingBottom: 0px;
    position: absolute;
    top: 50%;
    left: 50%;
    marginTop: -50px;
    marginLeft: -50px;
    width: 100px;
    height: 100px;
}

Para probar que todo esto funciona he escrito una demo. En la demo he adaptado un servidor HTTP muy simple escrito en Python que explique en la entrada Método rápido para servir archivos a través de HTTP (Servidor HTTP) con SimpleHTTPServer. La adaptación ha consistido en añadir en el método do_POST() un «echo» de modo que el servidor retorne un HTML con el mensaje enviado, en un tiempo opcionalmente parametrizable. En mi caso, esto es util para testear que el gif animado aparece:

from SimpleHTTPServer import SimpleHTTPRequestHandler
import SocketServer
import time
import urlparse
from cgi import parse_header, parse_multipart

class MyRequestHandler(SimpleHTTPRequestHandler):

    def __init__(self, *args):
        SimpleHTTPRequestHandler.__init__(self, *args)

    def parse_POST(self):
        ctype, pdict = parse_header(self.headers['content-type'])
        if ctype == 'multipart/form-data':
            postvars = parse_multipart(self.rfile, pdict)
        elif ctype == 'application/x-www-form-urlencoded':
            length = int(self.headers['content-length'])
            postvars = urlparse.parse_qs(
                    self.rfile.read(length), 
                    keep_blank_values=1)
        else:
            postvars = {}
        return postvars
    
    def do_GET(self):
        print self.command
        print self.path
        return SimpleHTTPRequestHandler.do_GET(self)

    def do_POST(self):
        print self.command
        print self.path
        parse = urlparse.urlparse(self.path)
        fields = self.parse_POST()
        self.send_response(200) 
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        if parse.path == '/echo':
            if fields['delay']:
                seconds = float(fields['delay'][0]) / 1000
                time.sleep(seconds)
            if fields['html']:        
                self.wfile.write(fields['html'][0])
        else:
            self.wfile.write('<ul>')
            for key, value in fields.iteritems():
                self.wfile.write('<li>%s: %s</li>' % (key, value))
            self.wfile.write('</ul>')

PORT = 8000
Handler = MyRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()

Al final tienes esto:

demo

Enlace para descargar la demo: demo.zip




Tecnologia

Método rápido para servir archivos a través de HTTP (Servidor HTTP) con SimpleHTTPServer


Python viene con un módulo llamado SimpleHTTPServer que permite montar un sencillo Servidor HTTP al que acceder través de localhost. Esto es útil a la hora de desarrollar porque hay cosas que no funcionan con ficheros. Por ejemplo las URLs en los navegadores web.

En la documentación de Python explica que también es posible «lanzar» directamente el servidor HTTP utilizando el modificador -m del intérprete y como argumento opcional el número de puerto.

Ejemplo:

python -m SimpleHTTPServer 8000

La clase encarga de hacer todo esto es SimpleHTTPRequestHandler que implementa la interface BaseHTTPRequestHandler. Esta clase es capaz de servir archivos del directorio actual y de cualquier archivo que esté por debajo, asi como la cartografía de la estructura de directorios en una petición HTTP:

Servidor HTTP

Un problemilla: SimpleHTTPRequestHandler no admite HTTP POST

Tengo form.html que contiene un formulario con un submit action de tipo POST:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <img src="python.png"/>
  <form action="action_page.php" method="post">
    First name:<br>
    <input type="text" name="firstname" value="Mickey"><br>
    Last name:<br>
    <input type="text" name="lastname" value="Mouse"><br><br>
    <input type="submit" value="Submit">
  </form>
</body>
</html>

Con el siguiente resultado:

form

Ahora bien si hago submit me encuentro con una desagradable sorpresa:

error

El problema es que SimpleHTTPRequestHandler implementa las funciones do_GET() y do_HEAD(), pero NO implementa do_POST() aunque si lo pensamos tiene cierta lógica.

Implementación de do_POST() con el objetivo de testear una aplicación

Si el objetivo es comprobar que información POST esta llegando al servidor esta implementación puede servirnos:

from SimpleHTTPServer import SimpleHTTPRequestHandler
import SocketServer
import time
import urlparse
from cgi import parse_header, parse_multipart

class MyRequestHandler(SimpleHTTPRequestHandler):

    def __init__(self, *args):
        SimpleHTTPRequestHandler.__init__(self, *args)

    def parse_POST(self):
        ctype, pdict = parse_header(self.headers['content-type'])
        if ctype == 'multipart/form-data':
            postvars = parse_multipart(self.rfile, pdict)
        elif ctype == 'application/x-www-form-urlencoded':
            length = int(self.headers['content-length'])
            postvars = urlparse.parse_qs(
                    self.rfile.read(length), 
                    keep_blank_values=1)
        else:
            postvars = {}
        return postvars
    
    def do_GET(self):
        print self.command
        print self.path
        return SimpleHTTPRequestHandler.do_GET(self)

    def do_POST(self):
        print self.command
        print self.path
        fields = self.parse_POST()
        self.send_response(200) 
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write('<ul>')
        for key, value in fields.iteritems():
            self.wfile.write('<li>%s: %s</li>' % (key, value))
        self.wfile.write('</ul>')

PORT = 8000
Handler = MyRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()

Si lanzo el script, ahora al hacer el submit de form.htm este es el resultado:

resultpost

Espero que os pueda servir.
demo: server.zip




Tecnologia

node.js: codificación correcta de caracteres de un HTTP response


Si en node.js definimos el siguiente callback para dar respuesta a una petición HTTP

var server = http.createServer(function (request, response) {
  response.writeHead(200);
  response.write('el niño es rápido');
  response.end();
});

Esto es lo que obtendremos en nuestro navegador Web:

el niño es rápido

Vemos que el navegador no interpreta bien ni los acentos ni la eñe de la frase. Esto pasa porque estamos incluyendo caracteres que están fueran del rango del código ASCII. El navegador necesita que en la cabecera HTTP le especifiquemos el juego de caracteres correcto. Esto se hace a través del Header HTTP Contec-Type, donde en nuestro caso le indicaremos UTF-8.

Modificamos la función:

var server = http.createServer(function (request, response) {
  res.writeHead(200, {"Content-Type": "text/html;charset=UTF-8"});
  response.write('el niño es rápido');
  response.end();
});

Y ahora el navegador Web interpreta correctamente la respuesta:

el niño es rápido

referencias:
http://nodejs.org/api/http.html
http://howtonode.org/hello-node