asp.net-mvc
Model bindend
Zoeken…
Invoering
Modelbinding is het proces van het nemen van HTTP-parameters, meestal in de Query String van een GET-verzoek, of binnen de POST-body, en het toepassen op een object dat vervolgens op een objectgeoriënteerde manier kan worden gevalideerd en geconsumeerd zonder dat controller-acties nodig zijn grondige kennis hebben van het ophalen van HTTP-parameters.
Met andere woorden, modelbinding is wat acties, in MVC, toestaat om ofwel parameter (s) te hebben, of het nu een waardetype of een object is.
Opmerkingen
Om te proberen een instantie in de actie te maken, zoekt het bindmodelproces op verschillende plaatsen naar gegevens:
- Formuliergegevens
- Routegegevens
- Zoekopdrachtreeks
- Bestanden aangepast (cookies bijvoorbeeld)
Route waarde bindend
Gegeven een aantal standaardrouting zoals {controller=Home}/{action=Index}/{id?}
Als u de URL https://stackoverflow.com/questions/1558902
Dit zou naar de QuestionController gaan en de waarde 1558902 zou worden toegewezen aan een id-parameter van een indexactie, dwz
public ActionResult Index(int? id){
//id would be bound to id of the route
}
Zoekopdracht string bindend
Om uit te breiden op de route bindend zeggen dat je een url zoals https://stackoverflow.com/questions/1558902?sort=desc
en routing zoals {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"
}
Binden aan objecten
Vaak zou je met viewmodel-klassen in asp.net-mvc werken en zou je je hieraan willen binden. Dit werkt vergelijkbaar met het toewijzen aan individuele parameters.
Stel dat u een eenvoudig weergavemodel had, zo noemde PostViewModel
public class PostViewModel{
public int Id {get;set;}
public int SnappyTitle {get;set;}
}
Dan had u waarden van ID en SnappyTitle gepost vanuit een formulier in het http-verzoek, dan zouden ze direct aan dat model worden toegewezen als het model zelf de actieparameter was, bijv.
public ActionResult UpdatePost(PostViewModel viewModel){
//viewModel.Id would have our posted value
}
Het is vermeldenswaard dat de binding hoofdletterongevoelig is voor de parameter- en eigenschapsnamen. Waar mogelijk worden ook waarden geworpen. Ik laat meer randgevallen achter voor specifieke voorbeelden
Ajax bindend
Dit zijn formulierwaarden die in het HTTP-verzoek worden gebruikt met behulp van de POST-methode. (inclusief jQuery POST-aanvragen).
Stel dat je een Ajax-bericht leuk vond
$.ajax({
type: 'POST',
url: window.updatePost,
data: { id: 21, title: 'snappy title' },
//kept short for clarity
});
Hier zouden de twee waarden in json, id en titel gebonden zijn aan de overeenkomende actie, bijv
public JsonResult UpdatePost(int id, string title) {
...
}
Generieke, op sessie gebaseerde modelbinding
Soms moeten we het hele model behouden en overdragen via acties of zelfs controllers. Model opslaan bij sessie goede oplossing voor dit soort vereisten. Als we dit combineren met krachtige model binding kenmerken van MVC krijgen we elegante manier om dit te doen. We kunnen generieke sessiegebaseerde modelbinding maken in drie eenvoudige stappen:
Stap één: Modelbinder maken
Maak zelf een modelbinder. Persoonlijk heb ik gemaakt SessionDataModelBinder klasse / Infrastructuur / ModelBinders map.
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;
}
}
Stap twee: registerbinder registreren
Als we een model hebben zoals hieronder:
public class ReportInfo
{
public int ReportId { get; set; }
public ReportTypes TypeId { get; set; }
}
public enum ReportTypes
{
NotSpecified,
Monthly, Yearly
}
We kunnen op sessie gebaseerde modelbinder voor dit model registreren in Global.asax in de Application_Start- methode:
protected void Application_Start()
{
.........
// Model binders.
// Remember to specy unique SessionKey
ModelBinders.Binders.Add(typeof(ReportInfo),
new SessionDataModelBinder<ReportInfo>("ReportInfo"));
}
Stap drie: gebruik het!
Nu kunnen we profiteren van deze modelbinder door eenvoudig een parameter aan onze acties toe te voegen :
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();
}
}
Voorkom binding op PostModel
Overweegt een (post) model:
public class User
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
Met een dergelijke blik:
@using (Html.BeginForm()) {
@Html.EditorFor(model => model.FirstName)
<input type="submit" value="Save" />
}
Om te voorkomen dat een kwaadwillende gebruiker IsAdmin toewijst, kunt u het kenmerk Bind
in de actie:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Bestand upload
Model:
public class SampleViewModel
{
public HttpPostedFileBase file {get;set;}
}
Visie:
@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>
}
Actie:
[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);
}
Datumvelden handmatig valideren met dynamische indelingen met behulp van modelbinder
Als verschillende gebruikers een ander datetime-formaat nodig hebben, moet u mogelijk uw inkomende datareeks naar de werkelijke datum parseren volgens het formaat. In dit geval kan dit fragment u helpen.
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);
}