Cómo crear un cliente C# para un Web API de ASP.NET Core (II)

En el anterior post de este blog Cómo crear un cliente C# para un Web API de ASP.NET Core (I), vimos cómo implementar un cliente C# con la ayuda de NSwag Studio, para consumir un Web API de ASP.NET Core con autenticación de usuarios mediante JSON Web Tokens (JWT).

En este artículo, y como continuación al post anterior, veremos cómo integrar este cliente C# en una aplicación ASP.NET Core MVC, que nos permita realizar las operaciones básicas de CRUD sobre el Web API en cuestión.

webapi-client

 

Para comenzar recuperaremos la solución que contiene el proyecto WebApiClient.csproj que ya creamos en el anterior artículo Cómo crear un cliente C# para un Web API de ASP.NET Core (I)

A continuación y en la misma solución, crearemos un nuevo proyecto del tipo Aplicación Web ASP.NET Core MVC llamado AspNetCoreClient.csproj. Este proyecto será sobe el cual trabajaremos, así que lo primero que debemos hacer es añadir una referencia al cliente C# (proyecto WebApiClient.csproj) desde la sección Dependencias, botón derecho del ratón, Agregar referencia... .

 

Consideraciones previas

Antes de comenzar a crear los Controladores y las Vistas de nuestra aplicación, realizaremos una serie de configuraciones que posteriormente utilizaremos en el desarrollo.

La autenticación de usuarios

Como ya sabemos, el Web API que consumiremos desde nuestra aplicación, requiere la autenticación de usuarios mediante JSON Web Tokens (JWT).

Un token JWT, además de permitirnos el acceso al Web API, contiene también cierta información que nos puede ser de mucha utilidad a la hora de desarrollar nuestra aplicación. Si lo decodificamos, podemos a acceder a la información del usuario que lo solicitó (Claims), así como la fecha de caducidad del propio token.

Es por esto que crearemos una nueva clase de Modelo UsuarioInfo.cs , la cual contendrá toda la información relativa al usuario y al token que necesitemos. El código sería el siguiente:

    public class UsuarioInfo
    {
        public Guid Id { get; set; }
        public string Nombre { get; set; }
        public string Apellidos { get; set; }
        public string Email { get; set; }
        public string Rol { get; set; }
        public DateTime ValidoDesde { get; set; }
        public DateTime ValidoHasta { get; set; }
    }

A continuación crearemos una nueva clase de Servicio JwtTokenService.cs , la cual se encargará de decodificar el token JWT de acceso, y devolverá un objeto del tipo UsuarioInfo con la información del usuario. El código sería el siguiente:

    public class JwtTokenService : IJwtTokenService
    {
        public UsuarioInfo DecodeJwtToken(string token)
        {
            // EXTRAE LA INFORMACIÓN DEL TOKEN JWT.
            var handler = new JwtSecurityTokenHandler();            
            var _token = handler?.ReadJwtToken(token);

            // CREA EL PERFIL DE INFORMACIÓN DEL USUARIO
            // A PARTIR DE LOS CLAIMS DEL TOKEN JWT
            var _usuarioInfo = new UsuarioInfo()
            {
                Id = new Guid(_token?.Claims?.
                    SingleOrDefault(x => x.Type == "nameid")?.Value 
                    ?? _token.Id),

                Nombre = _token?.Claims?.
                    SingleOrDefault(x => x.Type == "nombre")?.Value,

                Apellidos = _token?.Claims?.
                    SingleOrDefault(x => x.Type == "apellidos")?.Value,

                Email = _token?.Claims?.
                    SingleOrDefault(x => x.Type == "email")?.Value,

                Rol = _token?.Claims?.
                    SingleOrDefault(x => x.Type.Contains("role"))?.Value,

                ValidoDesde = _token.ValidFrom,

                ValidoHasta = _token.ValidTo
            };
            return _usuarioInfo;
        }
    }

    // CREAMOS LA INTERFAZ DE LA CLASE, PARA PODER 
    // INYECTARLA POR DEPENDENCIAS.
    public interface IJwtTokenService
    {
        UsuarioInfo DecodeJwtToken(string token);
    }

La inyección de dependencias

Si ya le hemos echado un vistazo a nuestro cliente de Web API WebApiClient.dll, observaremos que las dos clases cliente principales LoginClientPaisClient requieren la inyección de un objeto del tipo HttpClient a través de su constructor.

A su vez, ambas clases, deberán también ser inyectadas en los Controladores de nuestra aplicación ASP.NET MVC para poder ser utilizadas. 

Afortunadamente ASP.NET Core nos permite solucionar estas dos necesidades de una sola atacada, mediante el método extensor AddHttpClient<> de la colección IServiceCollection. Para ello, accederemos al archivo Startup.cs de la aplicación, y añadiremos el siguiente código al método ConfigureServices(IServiceCollection services):

        // This method gets called by the runtime. 
        // Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // ...

            // SUMINISTRA LOS CLIENTES LoginClient Y PaisClient Y ADEMÁS
            // LES INYECTA POR CONSTRUCTOR UN HttpClient A CADA UNO.
            services.AddHttpClient<WebApiClient.ILoginClient, WebApiClient.LoginClient>();
            services.AddHttpClient<WebApiClient.IPaisClient, WebApiClient.PaisClient>();

            // SUMINISTRA UNA INSTANCIA DE LA CLASE DE SERVICIO JwtTokenService.
            services.AddScoped<IJwtTokenService, JwtTokenService>();

            // ...

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

Nota: Como podemos ver en el código, también hemos configurado la inyección de dependencias para el Servicio JwtTokenService, con el objetivo de poder acceder a una instancia del mismo a través del constructor de los Controladores MVC.

 

Los Controladores

En este punto, ya podremos comenzar a crear los Controladores de nuestra aplicación ASP.NET Core MVC. 

Como ya hemos visto, nuestro cliente de Web API WebApiClient expone dos clases cliente principales (WebApiClient.LoginClientWebApiClient.PaisClient). Siguiendo esta notación, nosotros crearemos entonces un Controlador para cada una de ellas (LoginController.csPaisController.cs).

El Controlador LoginController

Antes de exponer el código del Controlador LoginController.cs, es conveniente explicar una serie de puntos que serán comunes a todos los Controladores de nuestra aplicación.

La inyección de dependencias a través del constructor

Como ya hemos comentado, utilizaremos la inyección de dependencias nativa de ASP.NET Core, para suministrar a través del constructor del Controlador aquellos servicios necesarios para su funcionamiento.

En el caso que nos ocupa, inyectaremos su clase cliente principal y el servicio de decodificación del token JWT de acceso. El código para el Controlador quedaría de la siguiente manera:

    public class LoginController : Controller
    {
        private readonly WebApiClient.ILoginClient _loginClient;
        private readonly IJwtTokenService _jwtTokenService;

        // OBTENEMOS MEDIANTE INYECCIÓN DE DEPENDENCIAS LA INSTANCIA
        // DE LoginClient y JwtTokenService A TRAVÉS DEL CONSTRUCTOR.
        public LoginController(WebApiClient.ILoginClient loginClient,
                                            IJwtTokenService jwtTokenService)
        {
            _loginClient = loginClient;
            _jwtTokenService = jwtTokenService;                        
        }
     
        // ...

     }

Sobrescribiendo el método OnActionExecuting(...)

El método OnActionExecuting(...) de la clase Controller, se ejecuta siempre después del constructor de la clase, y antes de la llamada a cualquier Acción del Controlador. 

El objetivo de utilizar este método, estriba en poder recuperar el contexto de la aplicación (HttpContext) para poder leer la cookie donde se almacena el token JWT (esto lo veremos a continuación), y extraer la información del usuario mediante el servicio de decodificación JwtTokenService.

Nota: También es posible acceder al HttpContext de la aplicación desde cualquier Acción del Controlador, pero lo haremos desde el OnActionExecuting(...) para no repetir el código de acceso y lectura de la cookie en todas las Acciones.

Importante: Tener en cuenta que no podemos acceder al HttpContext de la aplicación desde el Constructor de la clase Controller, nos devolverá Null y lanzará la correspondiente excepción.

El código completo del Controlador LoginController.cs sería el siguiente:

    public class LoginController : Controller
    {
        private readonly WebApiClient.ILoginClient _loginClient;
        private readonly IJwtTokenService _jwtTokenService;
        private string _tokenJWT;
        private UsuarioInfo _usuarioInfo;

        // OBTENEMOS MEDIANTE INYECCIÓN DE DEPENDENCIAS LA INSTANCIA
        // DE LoginClient y JwtTokenService A TRAVÉS DEL CONSTRUCTOR.
        public LoginController(WebApiClient.ILoginClient loginClient,
                                            IJwtTokenService jwtTokenService)
        {
            _loginClient = loginClient;
            _jwtTokenService = jwtTokenService;                        
        }

        // SOBRESCRIBIMOS EL MÉTODO OnActionExecuting.
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            // OBTENEMOS EL TOKEN DE ACCESO DE LA COOKIE.
            _tokenJWT = context.HttpContext.Request.Cookies["_WebApiToken"];

            // DECODIFICAMOS EL TOKEN DE ACCESO, PARA OBTENER 
            // LOS DATOS DEL USUARIO AUTENTICADO.
            if (_tokenJWT != null)
            {
                _usuarioInfo = _jwtTokenService.DecodeJwtToken(_tokenJWT);
                // ALMACENAMOS LA INFORMACIÓN DEL USUARIO EN EL ViewData.
                ViewData["usuarioInfo"] = _usuarioInfo;
            }

            base.OnActionExecuting(context);
        }

        [HttpGet]
        public IActionResult Index(string error)
        {
            ViewData["error"] = error;
            return View();
        }

        [HttpPost]
        public IActionResult Index(WebApiClient.UsuarioLogin usuarioLogin)
        {            
            try
            {
                // OBTENEMOS EL OBJETO QUE CONTIENE EL TOKEN DE ACCESO DESDE
                // EL MÉTODO LoginAsync() DEL CLIENTE DE WEB API loginClient, 
                // PASÁNDOLE LAS CREDENCIALES DEL USUARIO usuarioLogin.            
                var loginResult = _loginClient.LoginAsync(usuarioLogin).Result.ToString();

                // CREAMOS UN OBJETO ANÓNIMO QUE REPRESENTE EL RESULTADO 
                // DEVUELTO POR EL MÉTODO LoginAsync() -> loginResult.
                var objetoAnonimo = new { nombre = string.Empty, token = string.Empty };

                // DESERIALIZAMOS loginResult SEGÚN EL OBJETO ANÓNIMO objetoAnonimo,
                // Y OBTENEMOS EL TOKEN JWT DE ACCESO.
                var tokenJWT = JsonConvert.DeserializeAnonymousType(loginResult, objetoAnonimo).token;

                // ALMACENAMOS EL TOKEN OBTENIDO EN UNA COOKIE.
                CookieOptions cookieOptions = new CookieOptions();
                // LE DAMOS 24 HORAS DE VIDA.
                cookieOptions.Expires = DateTime.Now.AddHours(24);
                cookieOptions.HttpOnly = true; // ??
                cookieOptions.Secure = true; // ??
                Response.Cookies.Append("_WebApiToken", tokenJWT, cookieOptions);

                // REDIRIGIMOS A LA ACCIÓN Index DEL CONTROLADOR PaisController
                return RedirectToAction("Index", "Pais");
            }
            catch (Exception ex)
            {
                // PARA CUALQUIER EXCEPCIÓN.
                ViewData["error"] = ex.Message;
                return View();
            }
        }
    }

Como vemos en el código, el funcionamiento está bastante claro y bien explicado. En la Acción Index del Controlador, recibiremos desde la Vista un objeto del tipo usuarioLogin con las credenciales de acceso del usuario, para poder obtener el token JWT de acceso.

Una vez obtenido el token, lo almacenaremos en una cookie para su posterior uso. En el método OnActionExecuting(...) leeremos la cookie (en el caso de que exista) para extraer la información del usuario, y enviaremos esta información a la Vista a través del ViewData[...].

El Controlador PaisController

Este será el Controlador principal donde realizaremos las acciones CRUD a través del cliente WebApiClient

La estructura básica del Controlador será la misma que vimos anteriormente, o sea, la inyección de dependencias a través del constructor y la utilización del método OnActionExecuting(...) para recuperar el token JWT de acceso almacenado en la cookie.

El código completo del controlador PaisController.cs sería el siguiente:

    public class PaisController : Controller
    {
        private readonly WebApiClient.IPaisClient _paisClient;
        private readonly IJwtTokenService _jwtTokenService;
        private string _tokenJWT;
        private UsuarioInfo _usuarioInfo;

        // OBTENEMOS MEDIANTE INYECCIÓN DE DEPENDENCIAS LA INSTANCIA
        // DE LoginClient y JwtTokenService A TRAVÉS DEL CONSTRUCTOR.
        public PaisController(WebApiClient.IPaisClient paisClient, 
                                           IJwtTokenService jwtTokenService)
        {
            _paisClient = paisClient;
            _jwtTokenService = jwtTokenService;
        }

        // SOBRESCRIBIMOS EL MÉTODO OnActionExecuting.
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            // OBTENEMOS EL TOKEN DE ACCESO DE LA COOKIE.
            _tokenJWT = context.HttpContext.Request.Cookies["_WebApiToken"];

            // DECODIFICAMOS EL TOKEN DE ACCESO, PARA OBTENER 
            // LOS DATOS DEL USUARIO AUTENTICADO.
            if (_tokenJWT != null)
            {
                _usuarioInfo = _jwtTokenService.DecodeJwtToken(_tokenJWT);
                // ALMACENAMOS LA INFORMACIÓN DEL USUARIO EN EL ViewData.
                ViewData["usuarioInfo"] = _usuarioInfo;
            }

            base.OnActionExecuting(context);
        }
       
        // GET: Pais
        public ActionResult Index()
        {
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // RECUPERAMOS LOS DATOS.
                var _result = _paisClient.GetPaisAllAsync().Result;
                // Y LOS PASAMOS A LA VISTA.
                return View(_result);
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }
        }

        // GET: Pais/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Pais/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(WebApiClient.Pais pais)
        {            
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // CREAMOS EL OBJETO.
                var _result = _paisClient.PostPaisAsync(pais).Result;
                // REDIRIGIMOS A LA ACCIÓN INDEX.
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }
        }

        // GET: Pais/Delete/5
        [HttpGet]
        public ActionResult Delete(Guid id)
        {
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // OBTENEMOS EL OBJETO A BORRAR.
                var _pais = _paisClient.GetPaisAsync(id).Result;
                // LO PASAMOS A LA VISTA.
                return View(_pais);
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }            
        }

        // POST: Pais/Delete/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Delete(Guid id, IFormCollection collection)
        {
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // ELIMINAMOS EL OBJETO.
                var _result = _paisClient.DeletePaisAsync(id).Result;
                // REDIRIGIMOS A LA ACCIÓN INDEX.
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }
        }

        // GET: Pais/Details/5
        public ActionResult Details(Guid id)
        {
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // OBTENEMOS EL OBJETO.
                var _pais = _paisClient.GetPaisAsync(id).Result;
                // LO PASAMOS A LA VISTA.
                return View(_pais);
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }
        }

        // GET: Pais/Edit/5
        public ActionResult Edit(Guid id)
        {
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // OBTENEMOS EL OBJETO.
                var _pais = _paisClient.GetPaisAsync(id).Result;
                // LO PASAMOS A LA VISTA.
                return View(_pais);
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }
        }

        // POST: Pais/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Guid id, WebApiClient.Pais pais)
        {
            try
            {
                // ASIGNAMOS EL TOKEN DE ACCESO A LA PROPIEDAD BearerTokenJWT.
                _paisClient.BearerTokenJWT = _tokenJWT;
                // MODIFICAMOS EL OBJETO.
                _paisClient.PutPaisAsync(id, pais);
                // REDIRIGIMOS A LA ACCIÓN INDEX.
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                // EVALÚA LA EXCEPCIÓN POR SI HA OCURRIDO
                // UN StatusCode 401 Unauthorized.
                return CompruebaTokenJWTValido(ex);
            }
        }

        ////////////////////////
        /// MÉTODOS PRIVADOS ///
        ////////////////////////
        private ActionResult CompruebaTokenJWTValido(Exception ex)
        {
            // SI HA OCURRIDO UNA EXCEPCIÓN DEL TIPO ApiException
            // COMPROBAMOS SI EL StatusCode ES 401 Unauthorized Y
            // REDIRIGIMOS A LA PÁGINA DE LOGIN.
            if (ex.InnerException is WebApiClient.ApiException)
            {
                var statusCode = ((WebApiClient.ApiException)ex.InnerException).StatusCode;
                if (statusCode == StatusCodes.Status401Unauthorized)
                {
                    // REDIRIGIMOS A LA ACCIÓN DE LOGIN 
                    // CON EL TEXTO DEL ERROR COMO PARÁMETRO DE RUTA.
                    return RedirectToAction("Index", "Login", new { error = ex.InnerException.Message });
                }
            }
            // PARA CUALQUIER OTRA EXCEPCIÓN.
            ViewData["error"] = ex.Message;
            return View();
        }
    }

Importante: Como podemos ver en el código, lo más importante a destacar, es la forma en que el cliente WebApiClient trata las respuestas (Http Status Codes) devueltas por el Web API. Siempre que la respuesta del Web API sea diferente a los resultados devueltos esperados, el cliente WebApiClient lanzará una Excepción que nosotros deberemos capturar y tratar. Es por esto que el código de todas las acciones se encuentra envuelto en un Try / Catch.

 

Las Vistas

En este punto del desarrollo y para finalizar, ya podemos crear las Vistas de nuestra aplicación ASP.NET Core MVC. Pero antes, crearemos la Vista parcial _UsuarioInfoPartial.cshtml, la cual insertaremos posteriormente en todas y cada una de las Vistas de la aplicación.

La Vista _UsuarioInfoPartial.cshtml nos mostrará la información del usuario actual obtenida a partir del token JWT, así como su periodo de validez para acceder al Web API expresado en fecha y hora (desde / hasta).

El código sería el siguiente:

@model UsuarioInfo

@if(Model != null)
{
    <div class="alert alert-secondary row">
        <small>
            <b>@Model.Nombre @Model.Apellidos</b> |
            @Model.Rol | Válido desde <b>@Model.ValidoDesde.ToShortDateString() @Model.ValidoDesde.ToShortTimeString()</b>
                hasta <b>@Model.ValidoDesde.ToShortDateString() @Model.ValidoHasta.ToShortTimeString()</b>
        </small>
    </div>
}

Las Vistas para LoginController

El Controlador LoginController dispondrá de una única Vista Index.cshtml, la cual consistirá en un formulario de validación de usuarios mediante usuario y contraseña.

@model WebApiClient.UsuarioLogin
@{
    ViewData["Title"] = "Index";
}

<partial name="_UsuarioInfoPartial" model="@ViewData["usuarioInfo"]" />

<div class="row">
    <div class="col-md-3 form-horizontal mx-auto text-center">
        <h1>Login</h1>
        <form action="/Login/Index" method="post">
            <br />
            <input asp-for="Usuario" class="form-control" placeholder="Usuario" required autofocus />
            <span asp-validation-for="Usuario" class="text-danger"></span>
            <br />
            <input asp-for="Password" class="form-control" placeholder="Contraseña" type="password" required>
            <span asp-validation-for="Password" class="text-danger"></span>
            <br />
            <button class="btn btn-primary" type="submit">
                Iniciar sesión
            </button>
        </form>
    </div>
</div>

<br />
@if (ViewData["error"] != null)
{
    <div class="alert alert-danger text-center">@ViewData["error"]</div>
}

Como vemos en el código, en la parte superior se mostrará la información del usuario actual autenticado, y en la inferior, se nos indicará cualquier error o advertencia relacionada con el proceso de login. 

Nota: Estos "banners" informativos superior e inferior, serán comunes para todas las Vistas de la aplicación.

webapiclient-login

Las Vistas para PaisController

Por último, para el Controlador PaisController, definiremos una Vista para cada una de las Acciones CRUD que lo conforman. 

La Vista Index

@model IEnumerable<WebApiClient.Pais>
@{
    ViewData["Title"] = "Index";
}

<partial name="_UsuarioInfoPartial" model="@ViewData["usuarioInfo"]" />

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Nombre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Habitantes)
            </th>
            <th></th>
        </tr>
    </thead>

    <tbody>
        @if (Model != null)
        {
            @foreach (var item in Model)
            {
                <tr>
                    @*<td>
                        @Html.DisplayFor(modelItem => item.Id)
                    </td>*@
                    <td>
                        @Html.DisplayFor(modelItem => item.Nombre)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Habitantes)
                    </td>
                    <td>
                        @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
                        @Html.ActionLink("Details", "Details", new { id=item.Id }) |
                        @Html.ActionLink("Delete", "Delete", new { id=item.Id })
                    </td>
                </tr>
            }
        }
    </tbody>
</table>

@if (ViewData["error"] != null)
{
    <div class="alert alert-danger text-center">
        @ViewData["error"]
    </div>
}

webapiclient-index

La Vista Create

@model WebApiClient.Pais
@{
    ViewData["Title"] = "Create";
}

<partial name="_UsuarioInfoPartial" model="@ViewData["usuarioInfo"]" />

<h1>Create</h1>

<h4>Pais</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            @*NO ES NECESARIO INDICAR EL Id DEL OBJETO,
                YA QUE EL WEB API LO CREARÁ POR NOSOTROS*@
            @*<div class="form-group">
                    <label asp-for="Id" class="control-label"></label>
                    <input asp-for="Id" class="form-control" />
                    <span asp-validation-for="Id" class="text-danger"></span>
                </div>*@
            <div class="form-group">
                <label asp-for="Nombre" class="control-label"></label>
                <input asp-for="Nombre" class="form-control" />
                <span asp-validation-for="Nombre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Habitantes" class="control-label"></label>
                <input asp-for="Habitantes" class="form-control" />
                <span asp-validation-for="Habitantes" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@if (ViewData["error"] != null)
{
    <div class="alert alert-danger text-center">
        @ViewData["error"]
    </div>
}

webapiclient-create

La Vista Details

@model WebApiClient.Pais
@{
    ViewData["Title"] = "Details";
}

<partial name="_UsuarioInfoPartial" model="@ViewData["usuarioInfo"]" />

<h1>Details</h1>

<div>
    <h4>Pais</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Id)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Id)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Nombre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Nombre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Habitantes)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Habitantes)
        </dd>
    </dl>
</div>
<div>
    @Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
    <a asp-action="Index">Back to List</a>
</div>

@if (ViewData["error"] != null)
{
    <div class="alert alert-danger text-center">
        @ViewData["error"]
    </div>
}

webapiclient-details

La Vista Edit

@model WebApiClient.Pais
@{
    ViewData["Title"] = "Edit";
}

<partial name="_UsuarioInfoPartial" model="@ViewData["usuarioInfo"]" />

<h1>Edit</h1>

<h4>Pais</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            @*POR SUPUESTO, NO PODEMOS MODIFICAR
            EL Id DEL OBJETO*@ 
            @*<div class="form-group">
                    <label asp-for="Id" class="control-label"></label>
                    <input asp-for="Id" class="form-control" />
                    <span asp-validation-for="Id" class="text-danger"></span>
                </div>*@
            <div class="form-group">
                <label asp-for="Nombre" class="control-label"></label>
                <input asp-for="Nombre" class="form-control" />
                <span asp-validation-for="Nombre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Habitantes" class="control-label"></label>
                <input asp-for="Habitantes" class="form-control" />
                <span asp-validation-for="Habitantes" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@if (ViewData["error"] != null)
{
    <div class="alert alert-danger text-center">
        @ViewData["error"]
    </div>
}

webapiclient-edit

La Vista Delete

@model WebApiClient.Pais
@{
    ViewData["Title"] = "Delete";
}

<partial name="_UsuarioInfoPartial" model="@ViewData["usuarioInfo"]" />

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Pais</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Id)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Id)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Nombre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Nombre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Habitantes)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Habitantes)
        </dd>
    </dl>

    <form asp-action="Delete">
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>

@if (ViewData["error"] != null)
{
    <div class="alert alert-danger text-center">
        @ViewData["error"]
    </div>
}

webapiclient-delete

 

   EtiquetasWeb API ASP.NET Core ASP.NET MVC C#

  Compartir


  Nuevo comentario

El campo Comentario es obligatorio.
El campo Nombre es obligatorio.

Enviando ...

  Comentarios

No hay comentarios para este Post.



  Etiquetas

.NET Core .NET Framework .NET MVC .NET Standard AJAX ASP.NET ASP.NET Core ASP.NET MVC Bootstrap Buenas prácticas C# Cookies Entity Framework JavaScript jQuery JSON PDF Pruebas Unitarias Seguridad SEO Sql Server SqLite Swagger Validación Web API Web Forms

  Populares


  Nuevos















Utilizamos cookies propias y de terceros para mejorar nuestros servicios y ofrecerle una mejor experiencia de navegación. Si continúa navegando consideramos que acepta su uso. Más información   Acepto