do_
Escribir un servlet es bastante sencillo. Primero hay que crear una subclase de HTTPServlet::AbstractServlet. A continuación, dependiendo de si se quiere dar servicio a peticiones GET, POST, OPTIONS o HEAD, añadir respectivamente los métodos do_GET, do_POST, do_OPTIONS o do_HEAD. Si se quiere dar servicio a algunas de las peticiones menos frecuentes , como PUT, sólo hay que crear el correspondiente método do_, e.g. do_PUT.
AbstractServlet ya implementa por nosotros los métodos do_HEAD y do_OPTIONS. El método do_HEAD llama simplemente a do_GET (el cual es necesario proporcionar) y devuelve todo excepto el cuerpo del mensaje HTTP. do_OPTIONS devuelve una lista con los métodos do_ disponibles.
Nos podemos preguntar, "¿Qué es lo que debe hacer un método do_?". Eso depende de cada uno. WEBrick llamará a un método do_ con dos argumentos: los objetos que representan la petición y la respuesta. Lo que se querrá normalmente es interrogar a la petición y definir correspondientemente el objeto respuesta.
class GreetingServlet < HTTPServlet::AbstractServlet
def do_GET( req, res )
if req.query['name'] then
res.body =
"#{@options[0]} #{req.query['name']}. #{@options[1]}"
raise HTTPStatus::OK
else
raise HTTPStatus::PreconditionFailed.new(
"falta el atributo: 'name'"
)
end
end
alias do_POST do_GET
end #GreetingServlet
start_webrick do |server|
server.mount("/greet",GreetingServlet,"Hola","¿Has tenido un buen dia?")
end
siendo el resultado:
[jfglez@localhost docs]$ w3m -dump http://localhost:8080/greet
Precondition Failed
falta el atributo: 'name'
---------------------------------------------------------------------------
WEBrick/1.3.1 (Ruby/1.8.1/2003-12-25) at localhost:8080
[jfglez@localhost docs]$ w3m -dump http://localhost:8080/greet?name=jfglez
Hola jfglez. ¿Has tenido un buen día?
Existen dos formas de fijar el código de estado de la respuesta, La primera, como se a mostrado en el punto anterior, es lanzar una excepción HTTPStatus. Recomiendo este método porque, en el caso de un código de estado erróneo, se devuelve una página HTML con una traza. Si es necesario proporcionar una página de error a medida,
Establecer manualmente el código de estado y el cuerpo de la respuesta, o
Extender la clase
HTTPResponse con el método
create_error_page al que se llamará en
caso de error.
a mi me gusta más el primer método dado que no se accede a la excepción generada desde el interior del método create_error_page
class GreetingWithCustomizedErrorPageServlet < HTTPServlet::AbstractServlet
def do_GET( req, res )
if req.query['name'] then
res.body= "#{@options[0]} #{req.query['name']}, #{@options[1]}"
raise HTTPStatus::OK
else
res.status= 412
res.body= "Error dentro de #{self.class}"
res['content-type']= 'text/plain'
end
end #do_GET
end # GreetingWithCustomizedErrorPageServlet
class GreetingWithExtendedResponseObjectServlet < HTTPServlet::AbstractServlet
def do_GET( req, res )
# Extendemos el objeto res
class << res
def create_error_page
# 'content-type' por defecto es 'text/html'
self['content-type']= 'text/plain'
self.body= "Error dentro de " +
"GreetingWithExtendedResponseObjectServlet"
# el código de estado se determina a partor de la
# excepción HTTPStatus que se produzca
end #create_error_page
end
raise HTTPStatus::PreconditionFailed unless req.query['name']
res.body= "#{@options[0]} #{req.query['name']}, #{@options[1]}"
raise HTTPStatus::OK
end # do_GET
end # GreetingWithExtendedResponseObjectServlet
start_webrick do |server|
server.mount('/greet1',GreetingWithCustomizedErrorPageServlet,
'Hi','¿Qué tal día estas teniendo?')
server.mount('/greet2',GreetingWithExtendedResponseObjectServlet,
'Hi','¿Qué tal día estas teniendo?')
end
Siendo el resultado:
[jfglez@localhost docs]$ w3m -dump http://localhost:8080/greet1 Error dentro de GreetingWithCustomizedErrorPageServlet [jfglez@localhost docs]$ w3m -dump http://localhost:8080/greet2 Error dentro de GreetingWithExtendedResponseObjectServletPero, ¿Qué excepciones
HTTPStatus tenemos disponibles?. Muchas; La siguiente tabla muestra las posibles excepciones:
Tabla 2. Excepciones HTTPStatus
| Código | Excepción |
|---|---|
| 100 | Continue |
| 101 | SwitchingProtocols |
| 200 | OK |
| 201 | Created |
| 202 | Accepted |
| 203 | NonAuthoritativeInformation |
| 204 | NoContent |
| 205 | ResetContent |
| 206 | PartialContent |
| 300 | MultipleChoices |
| 301 | MovedPermanently |
| 302 | Found |
| 303 | SeeOther |
| 304 | NotModified |
| 305 | UseProxy |
| 307 | TemporaryRedirect |
| 400 | BadRequest |
| 401 | Unauthorized |
| 402 | PaymentRequired |
| 403 | Forbidden |
| 404 | NotFound |
| 405 | MethodNotAllowed |
| 406 | NotAcceptable |
| 407 | ProxyAuthenticationRequired |
| 408 | RequestTimeout |
| 409 | Conflict |
| 410 | Gone |
| 411 | LengthRequired |
| 412 | PreconditionFailed |
| 413 | RequestEntityTooLarge |
| 414 | RequestURITooLarge |
| 415 | UnsupportedMediaType |
| 416 | RequestRangeNotSatisfiable |
| 417 | ExpectationFailed |
| 500 | InternalServerError |
| 501 | NotImplemented |
| 502 | BadGateway |
| 503 | ServiceUnavailable |
| 504 | GatewayTimeout |
| 505 | HTTPVersionNotSupported |
String, también se le puede pasar un objeto IO. Este puede ser de mucha utilidad si la respuesta es muy larga, e.g. hay que devolver el contenido de un fichero de 16MB de longitud.
Existen ciertos momentos en los que no se desea que WEBrick cree automáticamente un nuevo objeto de un servlet. Por ejemplo, si la parte de inicialización del servlet es muy costosa, es preferible reusar el mismo objeto o por lo menos gestionar una agrupación de objetos.
WEBrick llama al método de clase get_instance con los parámetros config y options. Este método debe devolver un objeto que WEBrick pueda utilizar para dar servicio a la petición. Es recomendable colocar un mutex que controle la sección crítica dado que ahora se puede acceder al mismo objeto desde más de un hilo de ejecución a la vez.
require 'thread'
class CounterServlet < HTTPServlet::AbstractServlet
@@instance= nil
@@instance_creation_mutex= Mutex.new
def self.get_instance( config, *options )
@@instance_creation_mutex.synchronize {
@@instance= @@instance || self.new( config, *options )
}
end #get_instance
attr_reader :count
attr :count_mutex
def initialize( config, starting_count )
super
@count= starting_count
@count_mutex= Mutex.new
end #initialize
def do_GET( req, res )
res['content-type']= 'text/plain'
@count_mutex.synchronize {
res.body= @count
@count+= 1
}
end # do_GET
end # CounterServlet
start_webrick do |server|
server.mount( '/count_from_0', CounterServlet, 0 )
# 100 no tiene efecto
server.mount( '/count_from_0_too', CounterServlet, 100 )
end
Siendo el resultado:
[jfglez@localhost docs]$ w3m -dump http://localhost:8080/count_from_0 0 [jfglez@localhost docs]$ w3m -dump http://localhost:8080/count_from_0 1 [jfglez@localhost docs]$ w3m -dump http://localhost:8080/count_from_0 2 [jfglez@localhost docs]$ w3m -dump http://localhost:8080/count_from_0 3 [jfglez@localhost docs]$ w3m -dump http://localhost:8080/count_from_0_too 4 [jfglez@localhost docs]$ w3m -dump http://localhost:8080/count_from_0_too 5
Eric Hodel ha permitido gentilmente reproducir aquí su artículo
sobre la utilización de cookies en WEBrick en beneficio de los lectores de copias en papel. También se ha copiado la estructura de WEBrick::Cookies en la sección de referencia.
WEBrick expone las cookies a través de una clase sencilla y de fácil uso Cookie que además sigue la RFC 2109 sobre cookies. Los objetos HTTPRequest y HTTPRespomse permiten leer y establecer las cookies de una forma muy sencilla.
(las cookies son delicadas delicias)
La clase WEBrick::Cookie arropa una cookie y
expone sus propiedades. En WEBrick una
cookie se crea a través de
WEBrick::Cookie.new a la vez que se le da
un nombre y un valor. Una vez creado un objeto cookie se puede
acceder a sus propiedades con los siguientes métodos (Las
descripciones corresponden a la RFC 2109 y de las especificaciones sobre Cookies de Netscape):
nameEl nombre de la cookie. Este sólo se puede leer no se puede modificar.
valueEl valor de la cookie, debe venir codificado en ASCII imprimible.
versionIdentifica la especificación que sigue la cookie. 0, el valor por defecto, para las Cookies de Netscape y 1, para las cookies que siguen la RFC 2109.
domainEl dominio en el cual es válida la cookie. Una especificación explícita de un dominio debe comenzar siempre por punto.
expires
Un objeto Time o String que representa el momento en el que expira la cookie. Siempre debe tener el siguiente formato: DiaSem, DD-Mes-AAAA HH.MM.SS GMT
max_ageEl tiempo de vida de la cookie en segundos desde el momento en que se envía. Un valor cero indica que se debe descartar inmediatamente.
commentPermite al servidor de origen documentar el uso que se pretende dar a la cookie. El usuario puede inspeccionar esta información y decidir si iniciar o continuar una sesión con esta cookie.
pathEl subconjunto de la URL a la que se aplica la cookie.
secureAl poner a true, la cookie debe devolverse al origen sólo a través de una conexión segura.
WEBrick::HTTPRequest y se guardan en un Array al que se puede acceder través de la propiedad HTTPRequest#cookies. En la respuesta se pueden añadir al Array HTTPResponde#cookies una vez creado un objeto WEBrick::HTTPResponse.
Las cookies no se copian automáticamente del objeto HTTPRequest al objeto HTTPResponse. Esto hay que hacerlo explícitamente.