Consumir un API REST desde un Front-End de escritorio WinForms
Aprende a consumir un API Rest desde un sistema de escritorio y haz de tu WinForms tu front-end.

En esta entrada aprenderemos a consumir un API REST precisamente desde un sistema desktop o de escritorio hecho con .Net Framework. Los sistemas de escritorio están aparentemente un poco relegados hoy en día ante el auge de los web apps o la nube, sin embargo están muy presentes en muchísimas empresas allá afuera, así que aprender a construir sistemas desktop con tecnologías y arquitecturas más modernas con RestFul será de gran valor para ti.

Al hacer que tu Winforms consuma de un API y no trabaje directamente con la Base de datos, haces que sea un cliente más, es decir tu front-end de escritorio, así como otro front-end podría ser tu interfaz móvil y web, eh que te parece?. Como podrás notar, esta forma de trabajar y arquitectura es muy provechosa y es en síntesis una forma de aplicar la Arquitectura SOA.

Forma de trabajo y entorno

Hay tres formas de consumir un API Restful en Winforms con .Net Framework ya que éste nos provee diversas librerías para ejecutar peticiones mediante http, estas clases son WebClient, HttpClient o HttpWebRequest, puedes hallar más información en esta entrada de InfoWorld.

En esta ocasión trabajaremos con el método HttpClient y nos ayudaremos del uso de Generics para hacer un método multifunción el cual nos permita reutilizar código.

Primero instalamos desde Nuget:

  • Newtonsoft.Json

Este proyecto tendrá una estructura de carpetas similar a esta:

Clase Reply y enumeración

Creamos esta clase para que estandaricemos las respuestas que nos dan los servicios web.

Como los métodos HTTP son conocidos y no queremos cometer errores tipográficos, es mejor agruparlos en una enumeración:

    public class Reply
    {
        public string StatusCode { get; set; }
        public object Data { get; set; }
    }

    public enum methodHttp
    {
        GET,
        POST,
        PUT,
        DELETE
    }

Clase Consumer

Esta clase será la principal, y tendrá dos métodos: CreateHttpMethod para que nos permita crear los métodos de forma dinámica. Y el método Execute, este es el método que hará todo el trabajo, hace uso de Generics <T> el cual permite en tiempo de ejecución asignar un tipo de forma dinámica:

    public class Consumer
    {
        private static HttpMethod CreateHttpMethod(methodHttp method)
        {
            switch (method)
            {
                case methodHttp.GET:
                    return HttpMethod.Get;
                case methodHttp.POST:
                    return HttpMethod.Post;
                case methodHttp.PUT:
                    return HttpMethod.Put;
                case methodHttp.DELETE:
                    return HttpMethod.Delete;
                default:
                    throw new NotImplementedException("Not implemented http method");
            }
        }

        public static async Task<Reply> Execute<T>(string url, methodHttp method, T objectRequest)
        {
            Reply oReply = new Reply();
            try
            {
                using (HttpClient client = new HttpClient())
                {

                    var myContent = JsonConvert.SerializeObject(objectRequest);
                    var bytecontent = new ByteArrayContent(Encoding.UTF8.GetBytes(myContent));
                    bytecontent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                    //Si es get o delete no le mandamos content
                    var request = new HttpRequestMessage(CreateHttpMethod(method), url)
                    {
                        Content = (method != methodHttp.GET) ? method != methodHttp.DELETE ? bytecontent : null : null
                    };

                    using (HttpResponseMessage res = await client.SendAsync(request))
                    {
                        using (HttpContent content = res.Content)
                        {
                            string data = await content.ReadAsStringAsync();
                            if (data != null)
                                oReply.Data = JsonConvert.DeserializeObject<T>(data);

                            oReply.StatusCode = res.StatusCode.ToString();
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                oReply.StatusCode = "ServerError";
                var response = (HttpWebResponse)ex.Response;
                if (response != null)
                    oReply.StatusCode = response.StatusCode.ToString();
            }
            catch (Exception ex)
            {
                oReply.StatusCode = "AppError";
            }
            return oReply;
        }
    }

Entidades

Existen en la web muchos API's para hacer pruebas y puedas aplicarle solicitudes HTTP, tenemos por ejemplo: jsonplaceholder, SWAPI, etc

Para este ejercicio utilizaremos JSONPlaceHolder, un API REST para pruebas disponible desde https://jsonplaceholder.typicode.com/

En el caso del método GET tenemos uno de posteos en un blog (posts) disponible desde el URI: https://jsonplaceholder.typicode.com/posts

Creamos entonces la entidad Post con la misma estructura provista por el API para poder utilizarla dentro de la aplicación:

No confundir entidad Post con el método POST, esta entidad es acerca de los posts de un blog que el API que estamos usando de pruebas pone a disposición nuestra

    public class Post
    {
        public int userId { get; set; }
        public int id { get; set; }
        public string title { get; set; }
        public string body { get; set; }

    }

Consumiendo el API desde el formulario

Vamos a crear unos formularios sencillos para poner a prueba nuestro proyecto y consumir el API JsonPlaceHolder:

GET

En este caso estaremos consumiendo un API GET, poblamos nuestro objeto de la clase Reply y finalmente asignamos la propiedad DataSource del control DataGridView.

Ojo que como estamos trabajando con métodos asíncronos, tienes que agregar la palabra reservada async al método en este caso del evento click

        private async void btnGet_Click(object sender, EventArgs e)
        {
            //Creamos el listado de Posts a llenar
            List<Post> listado = new List<Post>();
            //Instanciamos un objeto Reply
            Reply oReply = new Reply();
            //poblamos el objeto con el método generic Execute
            oReply = await Consumer.Execute<List<Post>>(this.txtUri.Text, ApiHelper.methodHttp.GET, listado);
            //Poblamos el datagridview
            this.dgvGet.DataSource = oReply.Data;
            //Mostramos el statuscode devuelto, podemos añadirle lógica de validación
            MessageBox.Show(oReply.StatusCode);
        }

El resultado es el siguiente:

Genial 😊

POST

En esta ocasión creamos un objeto de la clase Post y se la enviamos a un API mediante el método HTTP POST:

        private async void btnPost_Click(object sender, EventArgs e)
        {
            Post post = new Post()
            {
                userId = 1,
                title = "titulo del post",
                body = "Cuerpo del post"

            };

            Reply oReply = new Reply();
            
            oReply = await Consumer.Execute<Post>(this.txtUriPost.Text, ApiHelper.methodHttp.POST, post);

            MessageBox.Show(oReply.StatusCode);
        }

El resultado es el siguiente:

Los métodos PUT y DELETE te los dejo de tarea, sin embargo si llegaste hasta aquí te será fácil hacerlos.

El código completo de este proyecto está disponible en mi Github disponible desde https://github.com/GeaSmart/APIConsumer-Winforms

Espero te haya sido de provecho este proyecto crack, y ya sabes comparte este contenido para que otros más se puedan beneficiar de él 😊

Si lo hiciste te mereces un aplauso crack

6 comentarios en «Consumir un API REST desde un Front-End de escritorio WinForms»

  1. Excelente, lo observe de manera somera, estoy tomando un curso para desarrollo web y este articulo me ayudara ya que solo desarrollo windows forms. saludos

  2. a mi me sale este error /*no se puede deserializar la matriz Json actual (por ejemplo, [1,2,3]) * en el tipo ‘crud_api.postviewmodel’ porque el tipo requiere un objeto JSON * (por ejemplo, { » nombre»: «Valor»}) o cambiar el tipo deserializado a en una matriz * o un tipo que implementa una interfaz de colección (por ejemplo, ICollection, IList) * como List que se puede deserializar desde una matriz JSON . jsonArrayAttribute * también se puede agregar al tipo para obligarlo a deserializarse desde una matriz * JSON, ruta », línea 14, posición 1*/ cuando trato de realizar el post alguna sugerencia ?

    1. Parece ser un error puntual en tu código, puedes hacer 2 cosas: allí tienes mi código en github, compáralo con el tuyo, y lo otro sería que vayas a la línea que el depurador te indica y seguir la pista hasta hallar la solución. Gracias por pasarte por aquí, éxitos.

    1. No lo tengo en vb.net pero no debería ser difícil de pasarlo a visual basic, ya en el pasado he trabajado con VB y es increíble lo fácil que es de aprenderlo una vez que sabes C# son como lenguajes hermanos, así que con un poco de tiempo lo podrás lograr, anímate. Saludos!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *