asp.net-mvc
Modellbindning
Sök…
Introduktion
Modellbindning är processen att ta HTTP-parametrar, vanligtvis i frågesträngen för en GET-begäran, eller inom POST-kroppen, och tillämpa den i ett objekt som sedan kan valideras och konsumeras på ett objektorienterat sätt utan behov av kontrolleråtgärder ha intim kunskap om hur man hämtar HTTP-parametrar.
Med andra ord, modellbindning är det som gör att åtgärder i MVC kan ha endera parametrar, vare sig det är en värdetyp eller ett objekt.
Anmärkningar
För att försöka skapa instans i handlingen söker bindemodelprocessen data på olika platser:
- Formdata
- Ruttdata
- Frågesträng
- Files Custom (till exempel kakor)
Ruttvärde bindande
Med tanke på vissa standardrutningar som {controller=Home}/{action=Index}/{id?}
Om du hade url https://stackoverflow.com/questions/1558902
Detta skulle gå till QuestionsController och värdet 1558902 skulle mappas till en ID-parameter för en indexhandling, dvs.
public ActionResult Index(int? id){
//id would be bound to id of the route
}
Frågestrengbindning
För att förlänga ruttbindningen, säg att du hade en webbadress som https://stackoverflow.com/questions/1558902?sort=desc
och routing som {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"
}
Bindning till föremål
Ofta skulle du arbeta med viewmodel-klasser i asp.net-mvc och vill binda till egenskaper på dessa. Detta fungerar som kartläggning till enskilda parametrar.
Säg att du hade ett enkelt visningsmodellsamtal PostViewModel så här
public class PostViewModel{
public int Id {get;set;}
public int SnappyTitle {get;set;}
}
Sedan hade du publicerat värden på Id och SnappyTitle från ett formulär i http-begäran, då skulle de kartlägga rätt på den modellen om modellen i sig var handlingsparametern, t.ex.
public ActionResult UpdatePost(PostViewModel viewModel){
//viewModel.Id would have our posted value
}
Det är värt att notera att bindningen inte är känslig för parametrar och egendomens namn. Det kommer också att kasta värden där det är möjligt. Jag lämnar fler kantfall för specifika exempel
Ajax bindande
Dessa är formvärden som går i HTTP-begäran med hjälp av POST-metoden. (inklusive jQuery POST-förfrågningar).
Säg att du gjorde ett ajax-inlägg som
$.ajax({
type: 'POST',
url: window.updatePost,
data: { id: 21, title: 'snappy title' },
//kept short for clarity
});
Här skulle de två värdena i json, id och title, vara bundna till den matchande åtgärden, t.ex.
public JsonResult UpdatePost(int id, string title) {
...
}
Generisk, sessionbaserad modellbindning
Ibland behöver vi bevara hela modellen och överföra den över handlingar eller till och med kontrollörer. Lagra modell vid sessionen bra lösning för denna typ av krav. Om vi kombinerar detta med kraftfulla modellbindande funktioner hos MVC får vi ett elegant sätt att göra det. Vi kan skapa generisk sessionbaserad modellbindning i tre enkla steg:
Steg ett: Skapa modellbindemedel
Skapa ett modellband själv. Personligen skapade jag SessionDataModelBinder- klassen i / Infrastructure / ModelBinders- mappen.
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;
}
}
Steg två: registrera bindemedlet
Om vi har en modell som nedan:
public class ReportInfo
{
public int ReportId { get; set; }
public ReportTypes TypeId { get; set; }
}
public enum ReportTypes
{
NotSpecified,
Monthly, Yearly
}
Vi kan registrera sessionsbaserat modellbindemedel för den här modellen i Global.asax i Application_Start- metoden:
protected void Application_Start()
{
.........
// Model binders.
// Remember to specy unique SessionKey
ModelBinders.Binders.Add(typeof(ReportInfo),
new SessionDataModelBinder<ReportInfo>("ReportInfo"));
}
Steg tre: använd det!
Nu kan vi dra nytta av detta modellbindemedel helt enkelt genom att lägga till parameter till våra åtgärder :
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();
}
}
Förhindra bindning på PostModel
Med tanke på en (post) modell:
public class User
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
Med en vy som så:
@using (Html.BeginForm()) {
@Html.EditorFor(model => model.FirstName)
<input type="submit" value="Save" />
}
För att förhindra en skadlig användare från att tilldela IsAdmin kan du använda attributet Bind
i åtgärden:
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
Filuppladdning
Modell:
public class SampleViewModel
{
public HttpPostedFileBase file {get;set;}
}
Se:
@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>
}
Verkan:
[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);
}
Validera datumfält manuellt med dynamiska format med hjälp av modellbindemedel
Om olika användare behöver olika datetime-format kan du behöva para din inkommande datumsträng till aktuellt datum enligt formatet. I detta fall kan detta utdrag hjälpa dig.
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);
}