10 de enero de 2019

Consumir web api utilizando jQuery.ajax()

se consumirá un servicio web api desde MVC utilizando jQuery, ya se desarrolló el servicio y se consumió desde MVC utilizando HttpClient en esta entrada.


el servicio webApi se encuentra alojado en IIS Express puerto 55987



esta es la tabla en a que se mostraran los datos obtenidos con el verbo GET
ID Nombre Departamento Cargo
MÉTODO GET
Obtener todos los registros código WebApi
[Route("api/Empleados")]
[ResponseType(typeof(List))]
public List Getempleadoes()
{
 var Empleados = (from dato in db.empleadoes
  select new EmpleadoModelApi()
  {
  id = dato.id,
  Nombres = dato.nombre,
  Cargo = dato.Cargo.Cargo1,
  Dpto = dato.Dpto.Dpto1
  }).ToList();
 return Empleados;
}

codigo javascript, como ejemplo se ejecuta al presionar un botón
$("#boton_obtenerTodos").click(function () {
$.ajax({
 type: "GET",
 url: "http://localhost:55987/api/Empleados",
 contentType: "application/json; charset=utf-8",
 dataType: "json",
 success: function (data) {
  $('#Table > tbody').empty();
  $.each(data, function (i, item) {
  var rows = 
  "" +
  "" + item.id + "" +
  "" + item.Nombres + "" +
  "" + item.Cargo + "" +
  "" + item.Dpto + "" +
  "";
  $('#Table > tbody').append(rows);
  });
  console.log(data);
 },
 failure: function (data) {
  alert(data.responseText);
 },
 error: function (data) {
  alert(data.responseText);
 }
});
});

MÉTODO GET/{id}
Obtener un registro especifico según id código Web Api, se muestra solamente el encabezado.
[Route("api/Empleado/{id}")]
[ResponseType(typeof(empleado))]
public IHttpActionResult Getempleado(int id)

código javascript, como ejemplo se ejecuta al presionar un botón
$("#boton_buscar").click(function () {
var id = $("#busqueda").val();
$.ajax({
 type: "GET",
 url: "http://localhost:55987/api/Empleado/"+ id,
 contentType: "application/json; charset=utf-8",
 dataType: "json",
 success: function (data, textStatus, jqXHR) {
  $("#Table").empty();
   var rows = 
   "" +
   "" + data.id + "" +
   "" + data.Nombres + "" +
   "" + data.Cargo + "" +
   "" + data.Dpto + "" +
   "";
   $('#Table').append(rows);
   console.log(data);
   }      
  },
 failure: function (data) {
   alert(data.responseText);
 },
 error: function (data) {
   alert(data.responseText);
 }
 });
});

MÉTODO POST
Agregar un nuevo registro código Web Api
[Route("api/PostEmpleado")]
[ResponseType(typeof(empleado))]
public IHttpActionResult Postempleado(CrearEmpleadoApi empleado)
{
 try{
 empleado clase = new empleado();
 clase.id = Convert.ToInt32(empleado.id);
 clase.nombre = empleado.Nombres.ToString();
 clase.IdCargo = Convert.ToInt32(empleado.cargo);
 clase.IdDpto = Convert.ToInt32(empleado.Dpto);
 ...
 return Ok(empleado);
 }
 catch (DbUpdateException)
 { ... }
}

código javascript
$("#boton_ingresar").click(function () {
var idValue = 6969;
var NombresValue = "Prueba desde Web Api";
var cargoValue = 2;
var DptoValue = 2;
$.ajax({
  url: "http://localhost:55987/api/PostEmpleado",
  type: "Post",
  data: JSON.stringify({
   id: idValue,
   Nombres: NombresValue,
   cargo: cargoValue,
   Dpto: DptoValue
  }),
  contentType: 'application/json; charset=utf-8',
 success: function (data) {
  alert('Registro aregado exitosamente !!!');
 },
 failure: function (data)
 {
  alert(data.responseText);
 },
 error: function (data)
 {
  alert(data.responseText);
 }
});
});

MÉTODO DELETE
Eliminar registro especifico según id código WebApi
[Route("api/DeleteEmpleado/{id}")]
[ResponseType(typeof(empleado))]
public IHttpActionResult Deleteempleado(int id)

código javascript
$("#boton_borrar").click(function () {
var id = $("#busqueda").val();
$.ajax({
  type: "Delete",
  url: "http://localhost:55987/api/DeleteEmpleado/" + id,
  contentType: "application/json; charset=utf-8",
  dataType: "json",
 success: function (data) {
  alert('Registro Eliminado');
 },
 failure: function (data) {
  alert(data.responseText);
 },
 error: function (data) {
  alert(data.responseText);
 }
});
});


4 de enero de 2019

JWT en Web Api


JWT es un estándar RFC 7519 para transmitir información con la identidad y claims de un usuario de forma segura entre un cliente/servidor. Dicha información puede ser verificada y confiable porque está firmada digitalmente.


Así en vez de crear una sesión en el servidor y estar enviando cookies a los usuarios, se utilizará un mecanismo de autenticación sin estado (la sesión del usuario nunca se guarda en el proveedor de identidad o en el proveedor del servicio) retornando un JSON Web Token via HTTP Header siempre y cuando el usuario ingrese sus credenciales correctamente.

El usuario será ahora el responsable de guardar el token que el servidor le envia en el proceso de autenticación a fin de acceder a una ruta protegida del servicio Web Api, si el token está presente y es válido, el proveedor del servicio otorga accesos a los recursos protegidos. Como los JWTs contienen toda la información necesaria en sí misma, se reduce la necesidad de consultar base de datos u otras fuentes de información múltiples veces.

Propiedades claim estándar
El estándar define los siguientes campos que pueden incluirse en los tokens JWT:
·         Issuer (iss) - Identifica el proveedor de identidad que emitió el JWT.
·         Subject (sub) - Identifica el objeto o usuario en nombre del cual fue emitido el JWT, se puede usar para limitar su uso a ciertos casos.
·         Audience (aud) - Identifica la audiencia o receptores para lo que el JWT fue emitido. Cada servicio que recibe un JWT para su validación tiene que controlar la audiencia a la que el JWT está destinado. Si el proveedor del servicio no se encuentra presente en el campo aud, entonces el JWT tiene que ser rechazado.
·         Expiration Time (exp) - Una fecha que sirva para verificar si el JWT está vencido y obligar al usuario a volver a autenticarse.
·         Not Before (nbf) - Indica desde que momento se va a empezar a aceptar un JWT,  EL JWT no tiene que ser aceptado si el token es utilizando antes de este tiempo. 
·         Issued At (iat) - Indica cuando fue creado el JWT.
·         JWT ID (jti) - Un identificador único para cada JWT.

para el ejemplo lo primero será crear la contraseña a utilizar como secret key en el proceso de autenticación, se creó en la página https://passwordsgenerator.net/ con una longitud de 128 tal como se muestra

se generó el password +$3Vs@JmcdncM?@yBs$D_yFJ$TR5#pDbKwuX6787=k@6^GYN*x*#9hrEv9tBefaEG7e3XHh=Lw62$$hxN-g#artN8=Xqg&6Pt#W=yYU5!RT6B#VRcq4+v$L+rM4UEj25

la aplicación cliente que consumirá el servicio se encuentra en el puerto 55987 en IIS Express tal como se muestra en la figura, este numero de puerto se utilizara en los claims issuer y audience del JWT.


colocar en el controlador Empleado de la aplicación Web Api el atributo Authorize, de esta manera solamente los clientes que envíen el token podrán consumir el servicio


se utilizará la aplicación Web Api creada en esta entrada y se agregaran:
el archivo Models.cs en el dentro de la carpeta Models.
el archivo LoginController dentro de la carpeta Controllers.
el archivo TokenValidationHandler en el directorio raíz, con lo que el Solution Explorer de la aplicación queda


en el archivo Models.cs se definen las clases para login de usuario y para respuesta ante login fallido
namespace WebApi2MVC.Models
{
 public class Models
 {}
  
 public class LoginRequest{
 public string Username { get; set; }
 public string Password { get; set; }
 }

 public class LoginResponse{
 public LoginResponse()
 {
 this.Token = "";
 this.responseMsg = new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.Unauthorized };
 }
      
 public string Token { get; set; }
 public HttpResponseMessage responseMsg { get; set; }
 }
}

Ahora debe agregarse el archivo TokenValidationHandler.cs en el cual se utilizará DelegatingHandler para capturar los mensajes HTTP enviados al servicio a fin de verificar que existe un token valido.
namespace WebApi2MVC
{
internal class TokenValidationHandler : DelegatingHandler
 {
 private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
 {
  token = null;
  IEnumerable authzHeaders;
  if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
  {
  return false;
  }
  var bearerToken = authzHeaders.ElementAt(0);
  token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
  return true;
 }

 protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 {
  HttpStatusCode statusCode;
  string token;
  if (!TryRetrieveToken(request, out token))
  {
   statusCode = HttpStatusCode.Unauthorized;
   return base.SendAsync(request, cancellationToken);
  }

  try
  {
   const string sec = "+$3Vs@JmcdncM?@yBs$D_yFJ$TR5#pDbKwuX6787=k@6^GYN*x*#9hrEv9tBefaEG7e3XHh=Lw62$$hxN-g#artN8=Xqg&6Pt#W=yYU5!RT6B#VRcq4+v$L+rM4UEj25";
   var now = DateTime.UtcNow;
   var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));

   SecurityToken securityToken;
   JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
   TokenValidationParameters validationParameters = new TokenValidationParameters()
   {
    ValidAudience = "http://localhost:55987",
    ValidIssuer = "http://localhost:55987",
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,
    LifetimeValidator = this.LifetimeValidator,
    IssuerSigningKey = securityKey
   };
   Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
   HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
   return base.SendAsync(request, cancellationToken);
  }
  catch (SecurityTokenValidationException)
  {
   statusCode = HttpStatusCode.Unauthorized;
  }
  catch (Exception)
  {
   statusCode = HttpStatusCode.InternalServerError;
  }
   return Task.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
  }

  public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
  {
   if (expires != null)
   {
    if (DateTime.UtcNow < expires) return true;
   }
  return false;
  }

 }
}
el archivo LoginController será el encargado de validar las credenciales, en caso de credenciales validas se creará el token y se enviará al cliente
namespace WebApi2MVC.Controllers
{
[RoutePrefix("api/login")]
 public class LoginController : ApiController
 {
 [AllowAnonymous]
 [HttpPost]
 [Route("authenticate")]
 public IHttpActionResult Authenticate(LoginRequest login)
 {
 var loginResponse = new LoginResponse { };
 LoginRequest loginrequest = new LoginRequest { };
 loginrequest.Username = login.Username.ToLower();
 loginrequest.Password = login.Password;

 IHttpActionResult response;
 HttpResponseMessage responseMsg = new HttpResponseMessage();
 bool isUsernamePasswordValid = false;

 if (login != null)
 isUsernamePasswordValid = loginrequest.Password == "pabletoreto.blogspot.com" ? true : false;
     
 if (isUsernamePasswordValid)
 {
 string token = createToken(loginrequest.Username);
 return Ok(token);
 }
 else
 {
 loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
 response = ResponseMessage(loginResponse.responseMsg);
 return response;
 }
 }

 private string createToken(string username)
 {
 DateTime issuedAt = DateTime.UtcNow;
 DateTime expires = DateTime.UtcNow.AddDays(7);
 var tokenHandler = new JwtSecurityTokenHandler();

 ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
 {
 new Claim(ClaimTypes.Name, username)
 ,new Claim(ClaimTypes.Country, "El Salvador")
 ,new Claim(ClaimTypes.Gender, "Macho")
 ,new Claim(ClaimTypes.Surname, "pabletoreto")
 ,new Claim(ClaimTypes.Email, "pabletoreto@mail.com")
 ,new Claim(ClaimTypes.Role, "IT")
 });

 const string sec = "+$3Vs@JmcdncM?@yBs$D_yFJ$TR5#pDbKwuX6787=k@6^GYN*x*#9hrEv9tBefaEG7e3XHh=Lw62$$hxN-g#artN8=Xqg&6Pt#W=yYU5!RT6B#VRcq4+v$L+rM4UEj25";
 var now = DateTime.UtcNow;
 var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
 var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);

 var token =
 (JwtSecurityToken)tokenHandler.CreateJwtSecurityToken(
  issuer: "http://localhost:55987", 
  audience: "http://localhost:55987",
  subject: claimsIdentity,
  notBefore: issuedAt,
  expires: expires,
  signingCredentials: signingCredentials);
            
  // claims personalizados
  var jsonu = new { id = "pabletoreto el mero" };
  token.Payload["user"] = jsonu;

  var payload = new JwtPayload
  {
  { "some ", "hello "},
  { "scope", "http://dummy.com/"},
  };

  var tokenString = tokenHandler.WriteToken(token);
  return tokenString;
 }
}
}

para finalizar se debe registrar la clase TokenValidationHandler en el WebApiConfig.cs

public static class WebApiConfig
{
 public static void Register(HttpConfiguration config)
 {
 config.MapHttpAttributeRoutes();
 config.MessageHandlers.Add(new TokenValidationHandler());

 config.Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}/{id}",
  defaults: new { id = RouteParameter.Optional }
 );

 config.EnableCors(new EnableCorsAttribute("*", "*", "GET,PUT,POST,DELETE"));
 var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
 config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
 }
}
ahora se realizarán las pruebas desde Postman, primero se realizará el login


la respuesta es una cadena alfanumérica generada desde el servicio y es enviada al cliente para autenticaciones futuras, evitando tener que enviar credenciales en cada invocación.


esta cadena consta de tres partes separadas por un punto, para decodificar la cadena se puede utilizar la página https://jwt.io/

Ahora se realizará una peticion con el verbo GET para la cual deberemos enviar el token junto con la petición, este token se envía en el encabezamiento de Authorization utilizando el esquema Bearer, este esquema de autenticación fue creado como parte de OAuth 2.0 y en este caso implica darle acceso al portador de este token

6 de diciembre de 2018

Swagger en Web Api



en el proyecto Web Api descargar desde NuGet la librería Swashbuckle 


una vez la librería se instale en tu solución se habrá creado la clase SwaggerConfig.cs en el folder App_Start


El código del SwaggerConfig.cs debe ser:

using System.Web.Http;
using WebActivatorEx;
using WebApi2MVC;
using Swashbuckle.Application;

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]

namespace WebApi2MVC
{
 public class SwaggerConfig
 {
  public static void Register()
  {
   var thisAssembly = typeof(SwaggerConfig).Assembly;

   GlobalConfiguration.Configuration
   .EnableSwagger(c =>
   {
   c.SingleApiVersion("v1", "Ejemplo de Swagger");
   c.IncludeXmlComments(string.Format(@"{0}\bin\WebApi2MVC.XML", System.AppDomain.CurrentDomain.BaseDirectory));
   })
   .EnableSwaggerUi(c =>
   {
   });
  }
 }
}
ahora click derecho en el proyecto para acceder a las propidades y en la pestaña Build habilitar el XML documentation file y dejar la dirección que especifica por defecto


solo falta comentar el código en los controller del Web Api, por ejemplo


y al ejecutar la aplicación se debe especificar el uso de Swagger, en mi caso lo especifico asi: http://localhost:55987/swagger/ui/index


la vista del verbo GET que obtiene todos los empleados 


30 de noviembre de 2018

Consumir Web Api desde MVC(HTTPClient)

Se utilizaran estas tablas creadas en una base de datos utilizando SQL Server

el ejemplo se basara en la tabla empleado, ahora crear un nuevo proyecto vacío y seleccionar Web Api, luego crear el modelo de la base de datos utilizando Entity Framework, se guardará la conexión en el Web.config con el nombre RIAcnx

una vez creado el modelo de la base de datos se debe crear un controlador para la entidad empleado, seleccionando la opción que se muestra en la imagen

y en la capeta Models se crean dos clases, la primera se utilizará para mostrar todos los registros de la tabla empleados o bien un registro especifico.
public class EmpleadoModelApi
 {
  public int id { get; set; }
  public string Nombres { get; set; }
  public string Dpto { get; set; }
  public string Cargo { get; set; }
 }
esta clase se utilizará para crear un nuevo registro en la tabla empleados o bien para actualizar un registro existente.
public class CrearEmpleadoApi
 {
  public int id { get; set; }
  public string Nombres { get; set; }
  public int cargo { get; set; }
  public int Dpto { get; set; }
 }
la solución del proyecto Web Api queda según la imagen

antes de continuar se debe habilitar el CORS(Croos-Origin Request), para esto descargar desde NuGet la dll Microsoft.AspNet.WebApi.Cors y especificar en el WebApiConfig

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
   config.MapHttpAttributeRoutes();
   config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
    );
    config.EnableCors(new EnableCorsAttribute("*", "*", "GET,PUT,POST,DELETE"));
    var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
    config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
  }
}

se utilizará Postman para las pruebas de la Web Api, descargarlo desde la chrome web store, en caso no quieras utilizar Postman puedes utilizar Swagger explicado aqui

ahora el código para el método GET que recuperará todos los registros de la tabla empleados con sus cargo y departamento desde la tablas Cargo y Dpto respectivamente, la consulta utiliza la relación entre estas, igualmente la imagen inferior (click para agrandar)muestra la prueba en Postman con el status code 200 (The request was fulfilled).
private RIAcnx db = new RIAcnx();

[Route("api/Empleados")]
[ResponseType(typeof(List))]
public List Getempleadoes()
{
var Empleados = (from dato in db.empleadoes
                 select new EmpleadoModelApi()
                 {
                  id = dato.id,
                  Nombres = dato.nombre,
                  Cargo = dato.Cargo.Cargo1,
                  Dpto = dato.Dpto.Dpto1
                  }).ToList();
     return Empleados;
}

el código para el método GET que recupera un registro especifico según el id con su respectiva prueba en Postman(click en la imagén)
[Route("api/Empleado/{id}")]
[ResponseType(typeof(empleado))]
public IHttpActionResult Getempleado(int id)
{
 var employee = (from dato in db.empleadoes
                .Where(s => s.id == id)
                 select new EmpleadoModelApi()
                 {
                 id = dato.id,
                 Nombres = dato.nombre,
                 Cargo = dato.Cargo.Cargo1,
                 Dpto = dato.Dpto.Dpto1
                 }).FirstOrDefault();

  if (employee == null)
  {
  return NotFound();
  }
 return Ok(employee);
}

el método PUT para actualizar un empleado se muestra a contiuación  igualmente la imagen inferior (click para agrandar)muestra la prueba en Postman con el status code 200 (The request was fulfilled).  Notar que se especifica el Body en raw y JSON.
[Route("api/PutEmpleado")]
[ResponseType(typeof(EmpleadoModelApi))]
public IHttpActionResult Putempleado(EmpleadoModelApi empleado)
  {
   if (!ModelState.IsValid)
   {
   return BadRequest(ModelState);
   }

   if (empleado.id < 0)
   {
   return BadRequest(ModelState);
   }

   try
   {
   empleado clase = new empleado();
   clase.nombre = empleado.Nombres;
   clase.id = empleado.id;
   clase.IdDpto = Convert.ToInt32(empleado.Dpto);
   clase.IdCargo = Convert.ToInt32(empleado.Cargo);
   db.Entry(clase).State = EntityState.Modified;
   db.SaveChanges();
   }
   catch (DbUpdateConcurrencyException)
   {
   if (!empleadoExists(empleado.id))
    {
    return NotFound();
    }
   else
    {
    throw;
    }
   }
return Ok("Empleado modificado");
}

el método POST para ingresar un nuevo registro en la tabla empleados se muestra a continuación  igualmente la imagen inferior (click para agrandar)muestra la prueba en Postman con el status code 200 (The request was fulfilled).
[Route("api/PostEmpleado")]
[ResponseType(typeof(empleado))]
public IHttpActionResult Postempleado(CrearEmpleadoApi empleado)
{
  if (!ModelState.IsValid)
  {
  return BadRequest("el modelo enviado no es válido");
  }
  try
  {
  empleado clase = new empleado();
  clase.id = Convert.ToInt32(empleado.id);
  clase.nombre = empleado.Nombres.ToString();
  clase.IdCargo = Convert.ToInt32(empleado.cargo);
  clase.IdDpto = Convert.ToInt32(empleado.Dpto);
  db.empleadoes.Add(clase);
  db.SaveChanges();
  return Ok(empleado);
  }
  catch (DbUpdateException)
  {
   if (empleadoExists(empleado.id))
   {
   return Conflict();
   }
   else
   {
   throw;
   }
  }
}

El método DELETE para eliminar un registro especifico de la tabla empleados  igualmente la imagen inferior (click para agrandar)muestra la prueba en Postman con el status code 200 (The request was fulfilled).

[Route("api/DeleteEmpleado/{id}")]
[ResponseType(typeof(empleado))]
public IHttpActionResult Deleteempleado(int id)
{
 if (id <= 0)
 return BadRequest("no es un código válido");

 Int32 codigo = Convert.ToInt32(id);
 empleado empleado = db.empleadoes.Find(id);
  if (empleado == null)
  {
   return NotFound();
  }

  try
  {
  db.empleadoes.Remove(empleado);
  db.SaveChanges();
  }

  catch (DbUpdateConcurrencyException)
  {
  return NotFound();
  }
 return Ok("empleado eliminado");
}

CONSUMIR DESDE MVC UTILIZANDO HTTPCLIENT
Se debe crear un nuevo proyecto en una solución distinta de la anterior, utilizando MVC, luego descargar desde NuGet las líbrerias System.Net.Http y Microsoft ASP.NET Web API 2.2 Client Library



se crearán tres clases en la carpeta Models de la aplicación MVC, esta clase se utilizará para enviar los datos del nuevo empleado que ingresará como nuevo registro en la tabla empleados.
public class CrearEmpleado
 {
  [Required (ErrorMessage="debe especificar nombre de empleado")]
  public string Nombres { get; set; }
  [Required (ErrorMessage="especifique código del cargo")]
  public int cargo { get; set; }
  [Required (ErrorMessage="especifique código del departamento")]
  public int Dpto { get; set; }
  [Required(ErrorMessage = "especifique código del empleado")]
  public int id { get; set; }
 }

esta clase se utilizará para mostrar todos los registros de la tabla empleado o bien para mostrar un registro especifico
public class EmpleadoModelMVC
    {
        public int id { get; set; }
        public string Nombres { get; set; }
        public string Dpto { get; set; }
        public string Cargo { get; set; }
    }

esta clase se utilizará para obtener el id del registro en la tabla empleado que se desea eliminar, puede obviarse esta clase, utilizar un textbox de razor y validar del lado del cliente antes de realizar la llamada al Web Api.
public class EliminarMVC
 {
  [Required (ErrorMessage="Especificar numero entero")]
  public int codigo { get; set; }
 }
esta es la llamada desde MVC para obtener todos los registros de la tabla empleado.
[HttpGet]
public ActionResult Empleados()
{
 IList empleados = null;
 using (var client = new HttpClient())
 {
 try
  {
   client.BaseAddress = new Uri("http://localhost:55987/api/");
   client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   var responseTask = client.GetAsync("Empleados");
   responseTask.Wait();
   var result = responseTask.Result;
   if (result.IsSuccessStatusCode)
   {
    var readTask = result.Content.ReadAsAsync>();
    readTask.Wait();
    empleados = readTask.Result;
   }
   else
   {
    empleados = null;
    ModelState.AddModelError(string.Empty, "Server error !!!");
   }
  }
  catch (Exception ex)
  {
   throw new ArgumentException(ex.Message.ToString());
  }
 }
 return View(empleados);
}

esta es la llamada desde MVC para obtener un registro especifico de la tabla empleado, se utiliza la clase EliminarMVC.cs pues solamente se necesita el código del registro a eliminar.
[HttpGet]
public ActionResult BuscarEmpleado()
{
 return View();
}

[HttpPost]
public ActionResult BuscarEmpleado(EliminarMVC dato)
{
 if (!ModelState.IsValid)
 {
 ModelState.AddModelError("", "Error en el modelo, verificar !!!");
 }

 try
 {
 EmpleadoModelMVC empleados = null;
 Int32 codigo = Convert.ToInt32(dato.codigo);

 using (var client = new HttpClient())
 {
  client.BaseAddress = new Uri("http://localhost:55987/api/");
  client.DefaultRequestHeaders.Accept.Clear();
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  var GetTask = client.GetAsync("Empleado/" + codigo.ToString());
  GetTask.Wait();

  var result = GetTask.Result;
  if (result.IsSuccessStatusCode)
  {    
   var readTask = result.Content.ReadAsAsync();
   readTask.Wait();
   empleados = readTask.Result;
   return View("ResultadoEmpleado",empleados);
  }
  else
  {
   empleados = null;
   ModelState.AddModelError(string.Empty, "Server error !!!");
  }
 }
 }
 catch (Exception)
 {
 throw;
 }
 return View(dato);
}

esta es la llamada desde MVC para agregar un nuevo registro a la tabla empleado.
[HttpGet]
public ActionResult CrearEmpleado()
{
 return View();
}

[HttpPost]
public ActionResult CrearEmpleado(CrearEmpleado empleado)
{
 string excepcion = string.Empty;
 try
 {
 using (var client = new HttpClient())
 {
  client.BaseAddress = new Uri("http://localhost:55987/");
  client.DefaultRequestHeaders.Accept.Clear();
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  var postTask = client.PostAsJsonAsync("api/PostEmpleado", empleado);
  postTask.Wait();
  var result = postTask.Result;
  if (result.IsSuccessStatusCode)
  {
   return RedirectToAction("Empleados");
  }
 }
 }
 catch (Exception ex)
 {
 excepcion = ex.Message.ToString();
 }
 ModelState.AddModelError(excepcion, "Server Error. Please contact administrator.");
 return View(empleado);
}

esta es la llamada desde MVC para actualizarun registro especifico de la tabla empleado.
[HttpGet]
public ActionResult ActualizarEmpleado()
{
 return View();
}

[HttpPost]
public ActionResult ActualizarEmpleado(CrearEmpleado Empleado)
{
 string excepcion = string.Empty;
 try
 {
 using (var client = new HttpClient())
 {
  client.BaseAddress = new Uri("http://localhost:55987/");
  var postTask = client.PutAsJsonAsync("api/PutEmpleado", Empleado);
  client.DefaultRequestHeaders.Accept.Clear();
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  postTask.Wait();
  var result = postTask.Result;
  if (result.IsSuccessStatusCode)
  {
   return RedirectToAction("Empleados");
  }
  }
 }
 catch (Exception ex)
 {
 excepcion = ex.Message.ToString();
 }

 ModelState.AddModelError(excepcion, "Server Error. Please contact administrator.");
 return View(Empleado);
}

esta es la llamada desde MVC para eliminar un registro especifico de la tabla empleado.
[HttpGet]
public ActionResult EliminarEmpleado()
{
 return View();
}

[HttpPost]
public ActionResult EliminarEmpleado(EliminarMVC dato)
{
 if (!ModelState.IsValid)
 {
  ModelState.AddModelError("", "Error en el módelo, verificar !!!");
 }

 try
 {
 Int32 codigo = Convert.ToInt32(dato.codigo);
 using (var client = new HttpClient())
  {
   client.BaseAddress = new Uri("http://localhost:55987/api/");
   client.DefaultRequestHeaders.Accept.Clear();
   client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   var deleteTask = client.DeleteAsync("DeleteEmpleado/" + codigo.ToString());
   deleteTask.Wait();
   var result = deleteTask.Result;
   if (result.IsSuccessStatusCode)
   {
   return RedirectToAction("Empleados");
   }
  }
 }
 catch (Exception)
 {
  throw;
 }
 return View();
}

19 de septiembre de 2017

Cargar Archivos MVC

Para la carga del archivo se utilizará la clase HttpPostedFileBase la cual proporciona propiedades y métodos para obtener información sobre un archivo individual asi como para leer y guardar el archivo, además se debe utilizar el HtmlInputFile control para seleccionar y cargar archivos desde un cliente.
protected HttpPostedFileBase();
public virtual int ContentLength { get; }
public virtual string ContentType { get; }
public virtual string FileName { get; }
public virtual Stream InputStream { get; }
public virtual void SaveAs(string filename);
El valor predeterminado es 4 MB el cual puede modificarse estableciendo el atributo maxRequestLength del archivo Web.config, se debe considerar la configuración de la MaxRequestLength para evitar la denegación de ataques de servicio causado por los usuarios envían archivos de gran tamaño al servidor.

MODELO
Para la carga de archivos mediante un formulario MVC se debe especificar la propiedad como string o bien como HttpPostedFile
   
public class ArchivoModel   
{    
 [DataType(DataType.Upload)]    
 [Display(Name = "Archivo a cargar")]    
 [Required(ErrorMessage = "seleccione archivo")]    
  public string file { get; set; }    
}
propiedad tipo HttpPostedFileBase con especificaciones para la carga de una imagén
public class ArchivoModel
{
 [DataType(DataType.Upload)]    
 [Display(Name = "Archivo a cargar")]    
 [Required(ErrorMessage = "seleccione archivo")]   
 [FileSize(10240)]
 [FileTypes("jpg,jpeg,png")]
  public HttpPostedFileBase file { get; set; }
}
igualmente puede utilizarse una expresión regular para validar el tipo de archivo que se espera del usuario, muestro varios ejemplos para una misma propiedad tipo HttpPostedFilebase
public class ArchivoModel
{
 [Required(ErrorMessage = "seleccione un archivo")]
 [RegularExpression(@"([a-zA-Z0-9\s_\\.\-:])+(.doc|.docx|.pdf)$", ErrorMessage = "")] 
 [RegularExpression(@"([a-zA-Z0-9\s_\\.\-:])+(.xls|.xlsx)$", ErrorMessage = "")]  
 [RegularExpression(@"([a-zA-Z0-9\s_\\.\-:])+(.png|.jpg|.gif)$", ErrorMessage = "")]
 [RegularExpression(@"([a-zA-Z0-9\s_\\.\-:])+(.txt)$", ErrorMessage = "")] 
  public HttpPostedFileBase archivo { get; set; }
}

VISTA
En el formulario se debe cambiar el encoding type por defecto (application/x-www-form-urlencoded) por el encoding type (multipart/form-data) el cual indica que el contenido del cuerpo del mensaje HTTP va a ser un archivo y permitir el envio de una larga cantidad de información al servidor, además se debe especificar el control input html tipo FILE.
@model ProyectoMVC.Models.ArchivoModel

@using (Html.BeginForm("UploadFiles", "FileUpload", FormMethod.Post,new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true);
    

@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.file, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.file, new { htmlAttributes = new { @class = "form-control", @type="file"} }) @Html.ValidationMessageFor(model => model.file, "", new { @class = "text-danger" })
@Html.Raw(ViewBag.estadoArchivo
}

CONTROLADOR
Este controller tipo POST recibe desde la vista cualquier tipo de archivo sin ninguna restricción y lo almacenará en la carpeta Archivos la cual se encuentra en el directorio raiz de la aplicación, en caso de no existir la carpeta entonces se creará, tener en cuenta que en este controlador solamente se valida que se reciba un archivo y se almacena en la carpeta especificada, no se realiza ninguna validación sobre el tipo de archivo que se recibe.
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;

namespace UploadingFilesUsingMVC.Controllers
{
 public class FileUploadController : Controller
 {

[HttpPost]
public ActionResult UploadFiles(HttpPostedFileBase file)
{
 if (ModelState.IsValid)
 {
  try
  {
  if (file != null)
  {
  string path = Server.MapPath("~/Archivos/");
  if (!Directory.Exists(path))
   {
   Directory.CreateDirectory(path);
   }
 
   string fileName = Path.GetFileName(postedFile.FileName);
   postedFile.SaveAs(path + fileName);
   }
   ViewBag.estadoArchivo = "Archivo cargado exitosamente !!!";
  }
  catch (Exception)
  {
  ViewBag.estadoArchivo= "Error cargando archivo !!!";
  return RedirectToAction("Index");
  }
 }
return View("Index");
}
 
 }
}

CONTROLADOR
Como ejemplo más completo, en este controlador tipo POST se espera que el usuario haya cargado una imagen con hasta un tamaño máximo por lo que si se validará el archivo que se recibe: tamaño menor de 1024 y la extensión.
[HttpPost]
public ActionResult Upload(HttpPostedFileBase photo)
{
  if (photo != null && photo.ContentLength > 0)
  {
    string directory = @"D:\Temp\";
     
     if (photo.ContentLength > 10240)
     {
     ModelState.AddModelError("photo", "The size of the file should not exceed 10 KB");
     return View();
     }
 
     var supportedTypes = new[] { "jpg", "jpeg", "png" };
     var fileExt = System.IO.Path.GetExtension(photo.FileName).Substring(1);
 
     if (!supportedTypes.Contains(fileExt))
     {
     ModelState.AddModelError("photo", "Invalid type. Only the following types (jpg, jpeg, png) are supported.");
     return View();
     }
 
     var fileName = Path.GetFileName(photo.FileName);
     photo.SaveAs(Path.Combine(directory, fileName));
  }
 
 return RedirectToAction("Index");
}

CARGA DE MÚLTIPLES ARCHIVOS
El input type debe ser file y especificar el valor multiple
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
 Select File:
 
 
@Html.Raw(ViewBag.Message) }
El parametro que recibe el controlador es una lista de httppostedfilebase y ejecutara un loop para verificar la existencia de archivo para luego proceder a guardarlo.
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
 {
  return View();
 }
 
[HttpPost]
public ActionResult Index(IEnumerable files) {
 foreach (var file in files) {
  if (file.ContentLength > 0) {
   var fileName = Path.GetFileName(file.FileName);
   var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
   file.SaveAs(path);
  }
}
return RedirectToAction("Index");
}
}
CARGA DE DATA MODEL
En los ejemplos anteriores se ha trabajado con el caso aislado de un modelo cuya única propiedad era del tipo HttpPostedFileBase, pero lo más probable es que toque trabajar con un Modelo/ViewModel con varias propiedades y en el que una de estas sea del tipo HttpPostedFileBase, para ejemplificar creo este modelo con tres propiedades

MODELO MVC 
public class miModelo
{
[Required(ErrorMessage = "Especifique nombre completo")]
[Display(Name = "Nombre completo")]
[MaxLength(200)]
 public string NombreCompleto { get; set; }
 
[Required(ErrorMessage = "Especifique correo")]
[DataType(DataType.EmailAddress)]
[EmailAddress]
[Display(Name = "Correo electronico")]
 public String Email { get; set; }

[DataType(DataType.Upload)]
[Display(Name = "Archivo a cargar")]    
[Required(ErrorMessage = "seleccione archivo")]
 HttpPostedFileBase Imagen{ get; set; }
}
VISTA RAZOR
En la vista definimos la
@Html.LabelFor(m => m.ImageUpload) @Html.TextBoxFor(m => m.ImageUpload, new { type = "file" })
CONTROLADOR MVC
El controlador se encargará de administrar las diferentes propiedades del modelo y guardar el archivo recibido en una ubicación especifíca.
public ActionResult FileUpload()
{
 return View();
}
 
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult FileUpload(miModelo ejemplo)
{
var validacion = new string[]
 {
  "image/gif",
  "image/jpeg",
  "image/jpeg",
  "image/png"
 }

 if (ejemplo.imagen == null || ejemplo.imagen.ContentLength == 0)
 {
  ModelState.AddModelError("Mensaje", "Cargar imagen");
 }
 else if (!validacion.Contains(ejemplo.imagen.ContentType))
 {
  ModelState.AddModelError("Mensaje", "Formato incorrecto.");
 }

 if (ModelState.IsValid)
 {
  if (ejemplo.Archivo != null)
  {
 var fileSize = ejemplo.Archivo.ContentLength;
 var fileName = Path.GetFileName(ejemplo.Archivo.FileName);
 var path = Path.Combine(Server.MapPath("~/Content/Upload"), fileName);
 ejemplo.Archivo.SaveAs(path);
 ViewBag.Message = "Archivo cargado exitosamente !!!";
 ModelState.Clear();
 }
}
 return View();
}

VALIDACIÓN DE ATRIBUTO HttpPostedFileBase
Ahora se creara un modelo que tendra una validación personalizada para la propiedad HttpPostedfileBase
[Required(ErrorMessage = "Especifique su nombre completo")]
[Display(Name = "Nombre completo")]
 public string NombreCompleto { get; set; }
 
[Required(ErrorMessage = "Especifique su dirección")]
[Display(Name = "Dirección completa")]
[MaxLength(200)]
 public string Direccion { get; set; }
 
[Required(ErrorMessage = "Please Upload File")]
[Display(Name = "Upload File")]
[ValidateFile]
 public HttpPostedFileBase file { get; set; }

Esta es la validación para un archivo tipo imagén.
 
public class ValidateFileAttribute : ValidationAttribute
{
 public override bool IsValid(object value)
 {
 int MaxContentLength = 1024 * 1024 * 4; //4 MB
 string[] AllowedFileExtensions = new string[] { ".jpg", ".gif", ".png", ".pdf" };
 
 var file = value as HttpPostedFileBase;
 
 if (file == null)
 return false;
 else if (!AllowedFileExtensions.Contains(file.FileName.Substring(file.FileName.LastIndexOf('.'))))
 {
 ErrorMessage = "Formatos permitidos: " + string.Join(", ", AllowedFileExtensions);
 return false;
 }
 else if (file.ContentLength > MaxContentLength)
 {
 ErrorMessage = "Maximo tamaño permitido : " + (MaxContentLength / 1024).ToString() + "MB";
 return false;
 }
 else
 return true;
 }
}

18 de septiembre de 2017

Enviar correo MVC C# SMTP

Ejemplos envian correos vía SMTP (Simple Mail Transfer Protocol) utilizando las clases MailMessage, SmtpClient y el namespace System.Net.Mail.

Las instancias de la clase MailMessage se utilizan para construir mensajes de correo electrónico que se transmiten a un servidor SMTP  utilizando la clase SmtpClient, más especificamente en la clase MailMessage se definirán datos correspondientes al cuerpo y direcciones origen - destino(s) de correo electronico y en la clase SmtpClient se definiran datos del servidor SMTP, siendo su propiedad más importatnte SmtpClient.send(MailMessage).

Como una primera aproximación se muestra un muy sencillo ejemplo donde el desarrollador define explicitamente los valores de los parametros de las instancias de MailMessage y SmtpClient en el método EnviarMensaje()
using System.Net.Mail;
using System.Net.Mime;

protected void BtnEnviar_Click(object sender, EventArgs e)
 {
  EnviarCorreo();
 }

private void EnviarCorreo() {

MailMessage email = new MailMessage();
SmtpClient smtp = new SmtpClient();

 email.To.Add(new MailAddress("correo@destino.com"));
 email.From = new MailAddress("correo@origen.com");
 email.Subject = "Notificación ( " + DateTime.Now.ToString("dd / MMM / yyy hh:mm:ss") + " ) ";
 email.SubjectEncoding = System.Text.Encoding.UTF8;
 email.Body = "Tu mensaje | tu firma";
 email.IsBodyHtml = true;
 email.Priority = MailPriority.Normal;
 FileStream fs = new FileStream("E:\\TestFolder\\test.pdf", FileMode.Open, FileAccess.Read);
 Attachment a = new Attachment(fs, "test.pdf", MediaTypeNames.Application.Octet);
 email.Attachments.Add(a);

 smtp.Host = "192.XXX.X.XXX";  // IP empresa/institucional
 //smtp.Host = "smtp.hotmail.com";
 //smtp.Host = "smtp.gmail.com";
 smtp.Port = 25;
 smtp.Timeout = 50;
 smtp.EnableSsl = false;
 smtp.UseDefaultCredentials = false;
 smtp.Credentials = new NetworkCredential("correo@origen.com", "password");

 string lista= "ejemplo1@correo.com; ejemplo2@correo2.com;";
 string output = string.empty;

 var mails = lista.Text.Split(';');
 foreach (string dir in mails)
          email.To.Add(dir);
       
 try
    {
     smtp.Send(email);
     email.Dispose();
     output = "Correo electrónico fue enviado satisfactoriamente.";
    }
 catch (SmtpException exm)
         {
          throw exm.Message.ToString();
         }
 catch (Exception ex)
    {
     output = "Error enviando correo electrónico: " + ex.Message;
    }

 MessageBox.Show(output);
 }

Ahora algo mas funcional, se permitirá al usuario especificar la información básica del correo que desea enviar y adjuntar un archivo a este, para esto se necesitan los siguientes namespaces y se creará una clase (modelo), tener en cuenta que puede necesitar referenciarse la dll System.Web.Abstractions a tu proyecto en caso utilices una version de .NET < 4.0, en versiones más recientes esta dll esta deprecated y basta con agregar system.web
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.IO;

public class CorreoModel
{
  [Required, Display(Name = "Correo Destinatario"), EmailAddress]    
  public string Para { get; set; }
  [Required]
  public string Asunto { get; set; }
  public string Cuerpo { get; set; }
  public HttpPostedFileBase Adjunto { get; set; }
  [Required]
  [Required, Display(Name = "Correo Remitente"), EmailAddress]
  public string Correo { get; set; }
  [Required]
  [DataType(DataType.Password)]
  public string Pass { get; set; }
}
Para enviar un archivo al servidor vía POST en el formulario se debe cambiar el valor implicito de encoding type = application/x-www-form-urlencoded, por el valor encoding type= multipart/form-data, además el formulario debe contar con un control tipo file input para anexar el archivo, este es un ejemplo para la vista MVC.
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
 

EJEMPLO ENVIO CORREO MVC


@Html.AntiForgeryToken() @Html.ValidationSummary(true)
@Html.LabelFor(model => model.Para, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Para, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Para)
@Html.LabelFor(model => model.Asunto, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Asunto, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Asunto)
@Html.LabelFor(model => model.Cuerpo, new { @class = "col-md-2 control-label", rows = "3", cols = "20" })
@Html.TextBoxFor(model => model.Cuerpo, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Cuerpo)
@Html.LabelFor(model => model.Adjunto, new { type = "file", @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Adjunto, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Adjunto)
@Html.LabelFor(model => model.Correo, new { @class = "col-md-2 control-label" })
@Html.TextBoxFor(model => model.Correo, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Correo)
@Html.LabelFor(model => model.Pass, new { @class = "col-md-2 control-label", type = "password" })
@Html.TextBoxFor(model => model.Pass, new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.Pass)
}
el controlador que recibe la información del usuario y enviará el correo es
using System.Web.Mvc
using System.IO;
using System.Net;
using System.Net.Mail;

public class HomeController : Controller
{
 [HttpGet] 
 public ActionResult Index()
 {
  return View();
 }
 
 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Index(CorreoModel model)
 {
  if (ModelState.IsValid)
  { 
   using (MailMessage mm = new MailMessage(model.Correo, model.Para))
    {
      mm.Subject = model.Asunto;
      mm.Body = model.Cuerpo;
        if (model.Adjunto.ContentLength > 0 && model.Adjunto!= null)
           {
            string fileName = Path.GetFileName(model.Adjunto.FileName);
            mm.Attachments.Add(new Attachment(model.Adjunto.InputStream, fileName));
           }

      mm.IsBodyHtml = false;
       using (SmtpClient smtp = new SmtpClient())
          {
           smtp.Host = "smtp.gmail.com";
           smtp.EnableSsl = true;
           NetworkCredential NetworkCred = new NetworkCredential(model.Correo, model.Pass);
           smtp.UseDefaultCredentials = true;
           smtp.Credentials = NetworkCred;
           smtp.Port = 587;
           smtp.Send(mm);

           mm.Dispose();
           smtp.Dispose();
           return RedirectToAction("Sent");
          }
    }
  }
  return View();
 }
}
para agregar al codigo anterior, hay ocasiones en que el adjuntar un archivo es muy poco común, asi que los desarrolladores optan por no especificarlo en el modelo sino utilizar la clase HttpPostedFileBase, asi pues, lel controlador recibe de la vista una instancia de la clase HttpPostedFileBase para que luego el controlador valide si la instancia tiene algún valor o está vacío.
[HttpPost]  
public ActionResult Index( MyMailModel objModelMail, HttpPostedFileBase fileUploader)  
 {  
  if (ModelState.IsValid)  
   {  
    string from = "xyz@gmail.com"; //any valid GMail ID  
    using (MailMessage mail = new MailMessage(from, objModelMail.To))  
    {  
     mail.Subject = objModelMail.Subject;  
     mail.Body = objModelMail.Body;  
      if (fileUploader != null)  
       {  
        string fileName = Path.GetFileName(fileUploader.FileName);  
        mail.Attachments.Add(new Attachment(fileUploader.InputStream, fileName));  
       }  
     mail.IsBodyHtml = false;  
      SmtpClient smtp = new SmtpClient();  
      smtp.Host = "smtp.gmail.com";  
      smtp.EnableSsl = true;  
      NetworkCredential networkCredential = new NetworkCredential(from, "Gmail Id Password");  
      smtp.UseDefaultCredentials = true;  
      smtp.Credentials = networkCredential;  
      smtp.Port = 587;  
      smtp.Host = "localhost";  
      smtp.Send(mail);  
      ViewBag.Message = "Sent";  
      return View("Index", objModelMail);  
       }  
      }  
     else  
    {  
     return View();  
    }  
 }
y en la vista se debe especificar un control tipo file, teniendo en consideración que el nombre de este debe coincidir con el recibido en el controlador
   
y el modelo a utilizar simplemente se le elimina la propiedad HttpPostedFileBase Adjunto
using System.ComponentModel.DataAnnotations;
using System.IO;

public class CorreoModel
{
  [Required]    
  public string Para { get; set; }
  [Required]
  public string Asunto { get; set; }
  public string Cuerpo { get; set; }
  [Required]
  public string Correo { get; set; }
  [Required]
  [DataType(DataType.Password)]
  public string Pass { get; set; }
}