Peticiones y respuestas web
Recién tuve que realizar la integración en un sistema para permitir el cobro con tarjetas. Ya había hecho esto con PHP, y al ser un lenguaje interpretado, realmente no tuve muchos lios, además de que tenía yo acceso total. En esta ocasión el escenario se pintó diferente: Tienda en línea basada en .NET (código compilado = DLLs), ambiente de programación un poco lento y pesado, proceso de pruebas en producción restringidas por velocidad del proceso y por estar siempre en línea, acceso totalmente dependiente de otros y carga de trabajo principal distinta de este proyecto.
Consejo de desarrollo #1. Si vas a modificar un producto compilado, analiza si puedes obtener el código fuente y asegurate que es la misma versión.
Consejo de desarrollo #2. Si no hay un ambiente de pruebas y desarrollo dentro de la organización para la que realizarás el proyecto, crea tu propio ambiente de desarrollo y establece horarios en los que trabajarás dentro de las instalaciones y fuera de ellas.
Consejo de desarrollo #3. Deja por escrito en qué consiste el desarrollo que vas a realizar, y que si requieren algo adicional se los cotizarás; de forma podrás decidir si lo harás (cuánto tardarás, lo que requieres y cuánto cobrarás) o no, sin generar conflictos.
Consejo de desarrollo #4. Establece por escrito el costo por cada etapa, incluyendo análisis, diseño, programación, pruebas, implementación y algunas cosas más, según creas conveniente. Así podrás exigir tranquilamente el pago por tu trabajo.
Bueno, es hora de entrar en materia... WebRequest y WebResponse: cómo hacer submit y post a formularios externos, y recuperar las respuestas.
El escenario: desde un sitio web, mediante algún mecanismo mostrar un formulario hospedado en un sitio externo al cual se le pasa algunos datos mediante un POST, a este formulario el visitante le ingresa su información, da clic en el botón enviar y recibe una respuesta en consecuencia, esta respuesta debe ser procesada por el sitio original.
Lo que hice fue que, como el sitio original ya cuenta con un formulario que pide esa misma información, se capturara una sola vez la información y enviarla al sitio externo, llenando automáticamente ese formulario y enviando los datos por POST, y realizar la captura de la respuesta, que para este caso consistió en enviar la respuesta y los datos a un segundo formulario con submit automático (siempre que provenga directamente del formulario externo original).
- Obtuve la URL del formulario externo, además revisé la documentación para saber qué datos espera el formulario y en qué campos de entrada, después revisé el formulario para conocer los nombres y campos visibles y ocultos que debían llenarse para ser enviados. Esto lo hice así porque requería recuperar el Id de sesión que genera el servidor externo. Entonces capturé la respuesta junto con la sesión.
- Obtuve la segunda URL, la que corresponde al ACTION del primer formulario externo. Reconfiguré y llené los campos para ser enviados como si fuera el formulario, entonces capturé la respuesta.
Este proceso tuve que repetirlo algunas veces más, ya que hacía llamados en cadena a otros formularios donde cada uno tiene su propia lógica y llamaba funciones, y me devolvían un resultado: el formulario procesado y la URL del siguiente.
Tanto para un POST como para un GET los datos se pasan en una cadena de pares campo=valor separados por ampersands (&), con la diferencia de que en un GET es visible y en un POST va empaquetado (y si es a través de un HTTPS, además se cifran por seguridad)
// ... código previo
StringBuilder values = new StringBuilder();
values.Append("campo1=" + valor1);
values.Append("&campo2=" + valor2);
values.Append("&campo3=" + valor3);
// ... más campos si requieres
Aquí, cada campo-N corresponde al nombre que tiene dentro del formulario. Por ejemplo:<input name="txtnombre" id="TxT_Nombre" type="text" /> entonces sería values.Append("txtnombre=" + valor); donde valor es un valor fijo u obtenido por otro medio, y que será asignado al campo txtnombreUri uri = new Uri("Url_del_formulario_original");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.UseDefaultCredentials = true;
request.ContentType = "application/x-www-form-urlencoded";
// Contenedor de cookie para el Id de la sesión
CookieContainer LaSession = new CookieContainer();
request.CookieContainer = LaSession;
// Codificación de la cadena con valores: requerimos su longitud
UTF8Encoding encoding = new UTF8Encoding();
byte[] bytes = encoding.GetBytes(values.ToString());
request.ContentLength = bytes.Length;
try {
// La petición al sitio
Stream writeStream = request.GetRequestStream();
writeStream.Write(bytes, 0, bytes.Length);
writeStream.Flush();
writeStream.Close();
using (WebResponse response = request.GetResponse()) {
var status = ((HttpWebResponse)response).StatusDescription;
writeStream = response.GetResponseStream();
StreamReader reader = new StreamReader(writeStream);
// Esto regresa un HTML que requieres procesar
var responseFromServer = reader.ReadToEnd();
reader.Close();
writeStream.Close();
response.Close();
}
} catch (WebException wex) {
throw wex;
}
// ... Procesamiento de la respuesta, y después sigue esto
var parametros = new List<KeyValuePair<string, string>>();
uri = new Uri("Url_Del_Segundo_Formulario_O_Action_del_primero");
using (var client = new HttpClient()) {
client.BaseAddress = uri;
var contenido = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("campo1", valor1),
new KeyValuePair<string,string>("campo2", valor2),
new KeyValuePair<string,string>("campo3", valor3)
});
var r = client.PostAsync("", contenido).Result;
var r_contenido = r.Content.ReadAsStringAsync();
// Resto del procesamiento que necesites...
Durante el desarrollo de la modificación me encontré con un problema de seguridad que arrojaba una excepción con mensaje sobre que no se podía establecer un canal seguro SSL/TSL. Para solucionar eso utilicé
var protocol = ServicePointManager.SecurityProtocol; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;Antes de realizar la petición y obtener la respuesta. Puede ser incluso antes de crear las instancias de WebRequest, WebResponse y HttpClient. Ya al finalizar su uso utilicé
ServicePointManager.SecurityProtocol = protocol; para restaurar el protocolo de seguridad.
Comentarios
Publicar un comentario