Dominar las peticiones HTTP con los gestores de peticiones de 4D

En el mundo web actual, los servidores procesan un número abrumador de peticiones. Analizar, contar, interpretar y redirigir eficientemente estas solicitudes es esencial, especialmente cuando se aplican los principios MVC.

Con la introducción de HTTP Request Handlers en el servidor HTTP 4D, puede activar lógica de negocio específica basada en las peticiones recibidas. Esta poderosa funcionalidad abre muchas posibilidades, como se detalla en esta entrada de blog.

Con HTTP Request Handlers, puede definir tantos handlers como necesite, adaptados a patrones URL y verbos HTTP específicos. Este enfoque ofrece una granularidad mucho mayor que los métodos tradicionales de autenticación en la web y conexión en la web a bases de datos, ahorrándole la implementación de extensas declaraciones Case of.

HDI_HTTP_RequestHandler

¿por qué los Gestores de peticiones?

El manejo de gestores de peticiones conduce a varios escenarios como:

  • utilizar una URL dada como proveedor de recursos (por ejemplo, descargar varios archivos llamando a una URL específica)
  • utilizar una URL dada como caja de carga de archivos (por ejemplo, cargar varios archivos llamando a una URL específica)
  • gestionar la redirección en páginas específicas en función de un contexto empresarial (por ejemplo, usuario autenticado o no, página no encontrada, privilegios concedidos o no… etc.)
  • conservar las estadísticas y cualquier información relacionada con una solicitud recibida
  • gestionar una autenticación mediante oAuth 2.0

cómo configurar un gestor de peticiones HTTP

Los manejadores de peticiones HTTP deben ser configurados en el archivo HTTPHandlers.json en la carpeta Project/Sources.

Este archivo contiene una colección de objetos en formato JSON. Cada objeto se refiere a un manejador de peticiones con la siguiente información:

  • el patrón de URL a manejar
  • el verbo utilizado por la URL
  • la [clase singleton + función] donde se implementa el código del manejador de peticiones

 

ejemplo

Archivo HTTPHandlers.json:

[
    {
        "class": "GeneralHandling",
        "method": "gettingStarted",
        "pattern": "start",
        "verbs": "get, post"
    }
]

Pregunta: ¿Qué significa?

Respuesta: Significa que cuando se recibe en el servidor cualquier petición que empiece por /start/ con un verbo GET o POST, se ejecuta la función gettingStarted del singleton GeneralHandling.

cómo implementar el código del manejador de peticiones

La clase singleton mencionada anteriormente debe ser compartida.

Entrada: una instancia de la nueva clase 4D.IncomingMessage

La petición se recibe en el servidor como una instancia del objeto de la nueva clase 4D.IncomingMessage.

Se dispone de la siguiente información

  • la url completa de la petición
  • las partes de la url
  • el verbo de la petición
  • los encabezados de la petición
  • los parámetros introducidos en la URL (si los hay)
  • el cuerpo de la solicitud (si existe)

 

A continuación, el gestor de peticiones puede utilizar toda esta información para activar la lógica de negocio apropiada.

Salida: una instancia de la clase 4D.OutgoingMessage

Si es necesario, el gestor de peticiones puede devolver una instancia de la clase 4D.clase OutgoingMessage, es decir, algún contenido web completo listo para que un navegador lo maneje (por ejemplo, como contenido de archivos).

un ejemplo sencillo para empezar

Con el mismo archivo HTTPHandlers.json que arriba:

[
    {
        "class": "GeneralHandling",
        "method": "gettingStarted",
        "pattern": "start",
        "verbs": "get, post"
    }
]

La petición http://127.0.0.1/start/example?param=demo&name=4D se ejecuta con un verbo GET en un navegador. Es manejada por la función gettingStarted() de la clase singleton GeneralHandling a continuación:

return $resultshared singleton Class constructor()


Function gettingStarted($request : 4D.IncomingMessage) : 4D.OutgoingMessage

var $result:=4D.OutgoingMessage.new()
var $body : Text

$body:="Called URL: "+$request.url+"\n"

$body+="The parameters are received as an object:"+"\n"+JSON Stringify($request.urlQuery; *)+"\n"

$body+="The verb is: "+$request.verb+"\n"

$body+="There are "+String($request.urlPath.length)+" url parts - Url parts are: "+$request.urlPath.join(" - ")+"\n"+"\n"

$result.setBody($body)
$result.setHeader("Content-Type"; "text/plain")

return $result

La petición se recibe en el servidor como $request (un objeto instancia de la nueva clase 4D.IncomingMessage ).

Este objeto $request contiene las propiedades:

  • url (String) – URL de la petición
  • urlQuery (Object) – Parámetros de la petición.
  • verb (Cadena) – Verbo
  • urlPath (colección de Strings) – Partes de la URL de la petición

 

Aquí está la respuesta:

cómo manejar un cuerpo en la petición

La nueva clase 4D.IncomingMessage ofrece algunas funciones para obtener los encabezados y el cuerpo de la petición.

ejemplo

Vamos a ejecutar un ejemplo sencillo para subir un fichero al servidor.

El archivo HTTPHandlers.json:

[
    {
        "class": "UploadFile",
        "method": "uploadFile",
        "regexPattern": "/putFile",
        "verbs": "POST"
    }
]

Observe que el patrón de URL se da como una expresión regular con la propiedad regexPattern.

La petición http://127.0.0.1:8044/putFile?fileName=testFile se ejecuta con un verbo POST y un contenido de archivo en su cuerpo.

La clase singleton UploadFile:

Function uploadFile($request : 4D.IncomingMessage) : 4D.OutgoingMessage
	
var $response:=4D.OutgoingMessage.new()
var $body:="Not supported file"
var $fileName; $fileType : Text
var $file : 4D.File
var $picture : Picture
var $created : Boolean
	
//The file name is given as parameter in the URL
$fileName:=$request.urlQuery.fileName
	
// The header "Content-Type" provides the format of the body
$fileType:=$request.getHeader("Content-Type")
	
Case of 
	// The body contains a pdf file
	: ($fileType="application/pdf")
		$file:=File("/PACKAGE/Files/"+$fileName+".pdf")
		$created:=$file.create()
		// The getBlob() function returns the body of the request as a Blob
		$file.setContent($request.getBlob())
		$body:="Upload OK - File size: "+String($file.size)
			
	// The body contains a jpg image
	: ($fileType="image/jpeg")
		$file:=File("/PACKAGE/Files/"+$fileName+".jpg")
		// The getPicture() function returns the body of the request as a Picture
		$picture:=$request.getPicture()
		WRITE PICTURE FILE($file.platformPath; $picture)
		$body:="Upload OK - Image size: "+String($file.size)
End case 

$response.setBody($body)
$response.setHeader("Content-Type"; "TEXT/plain")
	
return $response

El nombre del fichero se da como parámetro (fileName) en la URL. Se recibe en el objeto urlQuery en la petición.

Funciones para obtener lOs encabezados y el cuerpo de la petición

En el ejemplo anterior, observe la función getHeader() que devuelve un encabezado determinado de la petición. La clase 4D.IncomingMessage también ofrece una propiedad headers (Object) que contiene todos los encabezados de la petición.

El encabezado Content-Type proporciona el formato del cuerpo recibido. En este ejemplo, sólo se manejan los formatos pdf y jpeg.

Observe las funciones getBlob() y getPicture() en el objeto $request. Proporcionan el contenido del cuerpo en el formato apropiado que coincide con el encabezado Content-Type.

Consulte la documentación para más detalles sobre estas funciones.

La función getText() también está disponible y ofrece el cuerpo de la petición como una cadena (si se ha enviado como una cadena).

Si el cuerpo se ha dado como una representación JSON válida, la función getJSON() proporciona su resolución con el tipo correcto (por ejemplo, Object, Collection, String, …).

manejar la redirección

El manejador de peticiones también es capaz de redirigir a una página específica devolviendo una instancia de la clase4D.OutgoingMessage. El encabezado Location debe utilizarse para indicar la página a redirigir.

El valor de estado HTTP 3xx indica que el cliente debe realizar una acción adicional para completar la solicitud.

Vamos a ejecutar este escenario:

– El usuario puede ver todas las páginas html de la carpeta WebFolder/pages una vez autenticado. Mientras el usuario no esté autenticado, se le redirige a una página de autenticación.

– Si la página solicitada no se encuentra en esta carpeta, se sirve una página No encontrada.

– Si el usuario pide una página en una subcarpeta de la carpeta /pages, se sirve una página No autorizada.

El archivo HTTPHandlers.json:

[
    {
        "class": "PagesHandling",
        "method": "handle",
        "regexPattern": "/pages/"
    }
]

La clase singleton PagesHandling:

shared singleton Class constructor()
	
Function handle($request : 4D.IncomingMessage) : 4D.OutgoingMessage
	
var $result:=4D.OutgoingMessage.new()
var $file : 4D.File
	
//The user is not authenticated
If (Session.isGuest())
	$result.setHeader("Location"; "/authentication/Authentication.html")
	$result.setStatus(307)
Else 
		
	//The URL is an html page in the /pages folder
	If (($request.urlPath.length=2) && (Position(".html"; $request.url)#0))
			
		$file:=File("/PACKAGE/WebFolder"+$request.url)
			
		//The pages exists
		If ($file.exists)
			$result.setBody($file.getContent())
			$result.setHeader("Content-Type"; "text/html")
			$result.setStatus(200)
				
		//The pages does not exist
		Else 
			$result.setHeader("Location"; "/error/NotFound.html")
			$result.setStatus(307)
		End if 
			
	//The URL is NOT a an html page in the /pages folder
	Else 
		$result.setHeader("Location"; "/error/NotAuthorized.html")
		$result.setStatus(307)
	End if 
End if 
	
return $result

Observe que el manejador de peticiones tiene acceso al objeto Session que permite una lógica de negocio más compleja (comprobar la presencia de algunos privilegios, utilizar el objeto compartido Session.storage ).

Descarga el IDH para practicar el manejo de las peticiones HTTP y no espere más para entrar en el mundo del modo proyecto proponiendo clases y otras poderosas funcionalidades.

Avatar
• Propietario de producto - Marie-Sophie Landrieu-Yvert ingresó al equipo de 4D Product como Propietario de producto en 2017. Como tal, está a cargo de escribir las historias de los usuarios y luego traducirlas en especificaciones funcionales. Su papel es también asegurarse de que la implementación de la funcionalidad entregada cumpla con las necesidades del cliente. Marie-Sophie se graduó en la Escuela de Ingeniería de ESIGELEC y comenzó su carrera como ingeniera en IBM en 1995. Participó en varios proyectos (de mantenimiento y creación) y trabajó como desarrolladora de Cobol. Luego trabajó como diseñadora de UML y desarrolladora de Java. Sus principales funciones fueron analizar y redactar requisitos funcionales, coordinar los equipos de negocio y de desarrollo.