asp.net-mvc
Modelo de enlace
Buscar..
Introducción
El enlace de modelo es el proceso de tomar parámetros HTTP, normalmente en la Cadena de consulta de una solicitud GET, o dentro del cuerpo POST, y aplicarlo en un objeto que luego puede validarse y consumirse de una manera orientada a objetos sin la necesidad de acciones del Controlador tener un conocimiento íntimo de cómo recuperar los parámetros HTTP.
En otras palabras, el enlace de modelo es lo que permite que las acciones, en MVC, tengan cualquiera de los parámetros, ya sea un tipo de valor o un objeto.
Observaciones
Para intentar crear una instancia en la acción, el proceso del modelo de enlace buscará datos en varios lugares:
- Datos del formulario
- Datos de ruta
- Cadena de consulta
- Archivos personalizados (cookies, por ejemplo)
Enlace de valor de ruta
Dado algún enrutamiento predeterminado como {controller=Home}/{action=Index}/{id?}
Si tiene la url https://stackoverflow.com/questions/1558902
Esto iría al QuestionsController y el valor 1558902 se asignaría a un parámetro id de una acción de índice, es decir,
public ActionResult Index(int? id){
//id would be bound to id of the route
}
Enlace de cadena de consulta
Para ampliar la vinculación de ruta, diga que tiene una URL como https://stackoverflow.com/questions/1558902?sort=desc
y enrutamiento como {controller=Home}/{action=Index}/{id?}
public ActionResult Index(int? id, string sort){
//sort would bind to the value in the query string, i.e. "desc"
}
Atadura a objetos
A menudo, estarías trabajando con clases de viewmodel en asp.net-mvc y querrías unirte a las propiedades en estas. Esto funciona de manera similar a la asignación a parámetros individuales.
Digamos que tienes un modelo de vista simple llamado PostViewModel como este
public class PostViewModel{
public int Id {get;set;}
public int SnappyTitle {get;set;}
}
Luego, publicó los valores de Id y SnappyTitle desde un formulario en la solicitud http y luego se mapearían directamente en ese modelo si el modelo en sí fuera el parámetro de acción, por ejemplo
public ActionResult UpdatePost(PostViewModel viewModel){
//viewModel.Id would have our posted value
}
Vale la pena señalar que el enlace no distingue entre mayúsculas y minúsculas para los nombres de parámetros y propiedades. También emitirá valores cuando sea posible. Estoy dejando más casos de borde para ejemplos específicos
Ajax vinculante
Estos son valores de formulario que van en la solicitud HTTP usando el método POST. (incluidas las solicitudes POST de jQuery).
Digamos que hiciste un post de ajax como
$.ajax({
type: 'POST',
url: window.updatePost,
data: { id: 21, title: 'snappy title' },
//kept short for clarity
});
Aquí los dos valores en json, id y title, estarían vinculados a la acción correspondiente, por ejemplo,
public JsonResult UpdatePost(int id, string title) {
...
}
Generic, enlace basado en modelo de sesión
A veces necesitamos preservar todo el modelo y transferirlo a través de acciones o incluso controladores. Almacenamiento de modelo en la sesión buena solución para este tipo de requisitos. Si combinamos esto con las poderosas características de unión de modelos de MVC, obtendremos una forma elegante de hacerlo. Podemos crear un enlace de modelo basado en sesión genérica en tres sencillos pasos:
Paso uno: Crear carpeta de modelos
Crear un cuaderno de modelo en sí. Personalmente creé la clase SessionDataModelBinder en la carpeta / Infrastructure / ModelBinders .
using System;
using System.Web.Mvc;
public class SessionDataModelBinder<TModel>
: IModelBinder
where TModel : class
{
private string SessionKey { get; set; }
public SessionDataModelBinder(string sessionKey)
{
if (string.IsNullOrEmpty(sessionKey))
throw new ArgumentNullException(nameof(sessionKey));
SessionKey = sessionKey;
}
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
// Get model from session
TModel model = controllerContext
.HttpContext
.Session[SessionKey] as TModel;
// Create model if it wasn't found from session and store it
if (model == null)
{
model = Activator.CreateInstance<TModel>();
controllerContext.HttpContext.Session[SessionKey] = model;
}
// Return the model
return model;
}
}
Paso dos: registrar carpeta
Si tenemos modelo como abajo:
public class ReportInfo
{
public int ReportId { get; set; }
public ReportTypes TypeId { get; set; }
}
public enum ReportTypes
{
NotSpecified,
Monthly, Yearly
}
Podemos registrar el cuaderno de modelos basado en sesión para este modelo en Global.asax en el método Application_Start :
protected void Application_Start()
{
.........
// Model binders.
// Remember to specy unique SessionKey
ModelBinders.Binders.Add(typeof(ReportInfo),
new SessionDataModelBinder<ReportInfo>("ReportInfo"));
}
Paso tres: ¡úsalo!
Ahora podemos beneficiarnos de este modelo de carpeta simplemente agregando un parámetro a nuestras acciones :
public class HomeController : Controller
{
public ActionResult Index(ReportInfo reportInfo)
{
// Simply set properties
reportInfo.TypeId = ReportTypes.Monthly;
return View();
}
public ActionResult About(ReportInfo reportInfo)
{
// reportInfo.TypeId is Monthly now because we set
// it previously in Index action.
ReportTypes currentReportType = reportInfo.TypeId;
return View();
}
}
Prevenir el enlace en PostModel
Considerando un modelo (post):
public class User
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
Con una vista así:
@using (Html.BeginForm()) {
@Html.EditorFor(model => model.FirstName)
<input type="submit" value="Save" />
}
Para evitar que un usuario malintencionado asigne IsAdmin, puede usar el atributo Bind
en la acción:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Subir archivo
Modelo:
public class SampleViewModel
{
public HttpPostedFileBase file {get;set;}
}
Ver:
@model HelloWorldMvcApp.SampleViewModel
@using (Html.BeginForm("Index","Home",FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
@Html.TextBoxFor(model => model.file, new {@class="form-control", type="file"})
@Html.ValidationMessageFor(model => model.file)
</div>
<button type="submit" class="btn btn-success submit">Upload</button>
}
Acción:
[HttpPost]
public ActionResult Index(SampleViewModel model)
{
if (model.file.ContentLength > 0)
{
string fileName = Path.GetFileName(model.file.FileName);
string fileLocation = "~/App_Data/uploads/"+ fileName;
model.file.SaveAs(Server.MapPath(fileLocation));
}
return View(model);
}
Validación de campos de fecha manualmente con formatos dinámicos utilizando el cuaderno de modelos
Si diferentes usuarios necesitan un formato de fecha y hora diferente, es posible que deba analizar su cadena de fecha entrante a la fecha real de acuerdo con el formato. En este caso este fragmento puede ayudarte.
public class DateTimeBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
DateTime date;
var displayFormat = Session["DateTimeFormat"];
if (value.AttemptedValue != "")
{
if (DateTime.TryParseExact(value.AttemptedValue, displayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
return date;
}
else
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Invalid date format");
}
}
}
return base.BindModel(controllerContext, bindingContext);
}