← Volver al listado de tecnologías
Capítulo 6: Servidor Web Básico
Servidor Web Básico
Un servidor HTTP con routing, manejo de métodos y respuestas JSON.
Conceptos que Aprenderás
- Sockets TCP y binding
- Protocolo HTTP desde cero
- Routing de peticiones
- Concurrencia básica
Python
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
from typing import Callable
from urllib.parse import urlparse, parse_qs
Handler = Callable[['Router', dict], tuple[int, dict]]
class Router:
def __init__(self):
self.rutas: dict[str, dict[str, Handler]] = {}
def ruta(self, metodo: str, path: str):
def decorator(fn: Handler):
if path not in self.rutas:
self.rutas[path] = {}
self.rutas[path][metodo] = fn
return fn
return decorator
def resolver(self, metodo: str, path: str) -> Handler | None:
ruta = self.rutas.get(path)
if ruta:
return ruta.get(metodo)
return None
router = Router()
datos = {"items": []}
@router.ruta("GET", "/")
def index(ctx, params):
return 200, {"mensaje": "API funcionando", "endpoints": ["/items"]}
@router.ruta("GET", "/items")
def listar_items(ctx, params):
return 200, {"items": datos["items"]}
@router.ruta("POST", "/items")
def crear_item(ctx, params):
item = {"id": len(datos["items"]) + 1, **params}
datos["items"].append(item)
return 201, item
class Handler(BaseHTTPRequestHandler):
def responder(self, status: int, body: dict):
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(body).encode())
def do_GET(self):
parsed = urlparse(self.path)
handler = router.resolver("GET", parsed.path)
if handler:
status, body = handler({}, parse_qs(parsed.query))
self.responder(status, body)
else:
self.responder(404, {"error": "No encontrado"})
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(length)) if length else {}
handler = router.resolver("POST", self.path)
if handler:
status, resp = handler({}, body)
self.responder(status, resp)
else:
self.responder(404, {"error": "No encontrado"})
def main():
server = HTTPServer(("", 8080), Handler)
print("Servidor en http://localhost:8080")
server.serve_forever()
if __name__ == "__main__":
main()
Zig
const std = @import("std");
const net = std.net;
const Respuesta = struct {
status: []const u8,
body: []const u8,
};
fn construirRespuesta(status: []const u8, body: []const u8) []const u8 {
return std.fmt.allocPrint(std.heap.page_allocator,
"HTTP/1.1 {s}\r\nContent-Type: application/json\r\nContent-Length: {d}\r\n\r\n{s}",
.{ status, body.len, body },
) catch "";
}
fn manejarConexion(conn: net.Server.Connection) void {
defer conn.stream.close();
var buf: [4096]u8 = undefined;
const len = conn.stream.read(&buf) catch return;
const request = buf[0..len];
// Parsear primera línea
var lines = std.mem.splitScalar(u8, request, '\n');
const primera_linea = lines.next() orelse return;
var partes = std.mem.splitScalar(u8, primera_linea, ' ');
const metodo = partes.next() orelse return;
const path = partes.next() orelse return;
const respuesta = if (std.mem.eql(u8, path, "/"))
construirRespuesta("200 OK", "{\"mensaje\":\"API funcionando\"}")
else if (std.mem.eql(u8, path, "/items") and std.mem.eql(u8, metodo, "GET"))
construirRespuesta("200 OK", "{\"items\":[]}")
else
construirRespuesta("404 Not Found", "{\"error\":\"No encontrado\"}");
_ = conn.stream.write(respuesta) catch {};
}
pub fn main() !void {
const address = net.Address.initIp4(.{ 0, 0, 0, 0 }, 8080);
var server = try address.listen(.{});
defer server.deinit();
const stdout = std.io.getStdOut().writer();
try stdout.print("Servidor en http://localhost:8080\n", .{});
while (true) {
const conn = server.accept() catch continue;
manejarConexion(conn);
}
}
Diferencias Clave
| Aspecto | Python | Zig |
|---|---|---|
| Servidor | HTTPServer | net.Address.listen |
| Routing | Decoradores | Condicionales |
| Parsing | BaseHTTPRequestHandler | Manual |
| Hilos | ThreadingMixIn | Secuencial |
Ejercicios
- Agrega soporte para archivos estáticos
- Implementa middleware de logging
- Agrega autenticación básica
Proyecto Final
Combina todos los capítulos: crea una API REST que gestione tareas (Cap. 3), las exporte a CSV (Cap. 4), y se comunique con servicios externos (Cap. 5).