asp.net-mvc
モデルバインディング
サーチ…
前書き
モデルバインディングは、一般にGETリクエストのクエリ文字列内またはPOST本体内のHTTPパラメータを取得し、コントローラアクションを必要とせずにオブジェクト指向の方法で検証および使用できるオブジェクトに適用するプロセスですHTTPパラメータを取得する方法に関する詳細な知識が必要です。
言い換えれば、モデルバインディングは、MVCのアクションが値型であるかオブジェクトであるかにかかわらず、いずれかのパラメータを持つことを可能にするものです。
備考
アクションでインスタンスを作成しようとすると、バインド・モデル・プロセスはさまざまな場所のデータを検索します。
- フォームデータ
- ルートデータ
- クエリ文字列
- ファイルカスタム(例:クッキー)
ルート値バインド
URLがhttps://stackoverflow.com/questions/1558902
だった場合、 {controller=Home}/{action=Index}/{id?}
ような既定のルーティングがあるとしhttps://stackoverflow.com/questions/1558902
これはQuestionControllerに行き、値1558902はインデックスアクションのidパラメータにマップされます。
public ActionResult Index(int? id){
//id would be bound to id of the route
}
クエリ文字列バインディング
ルートバインディングを拡張するには、 https://stackoverflow.com/questions/1558902?sort=desc
descのようなURLがありhttps://stackoverflow.com/questions/1558902?sort=desc
{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"
}
オブジェクトへのバインド
多くの場合、asp.net-mvcのviewmodelクラスで作業していて、これらのプロパティにバインドしたいことがあります。これは、個々のパラメータへのマッピングに似ています。
単純なビューモデル呼び出しのPostViewModelをこのようにしたとします
public class PostViewModel{
public int Id {get;set;}
public int SnappyTitle {get;set;}
}
次に、IdとSnappyTitleの値をhttpリクエストのフォームから投稿した後、モデル自体がアクションパラメータであれば、そのモデルにマップされます。
public ActionResult UpdatePost(PostViewModel viewModel){
//viewModel.Id would have our posted value
}
パラメータ名とプロパティ名では大文字と小文字は区別されません。また、可能な限り値をキャストします。私は具体的な事例についてより多くの例を残しています
Ajaxバインディング
これらはPOSTメソッドを使用してHTTP要求に入るフォーム値です。 (jQueryのPOST要求を含む)。
次のようなajax投稿をしたとします
$.ajax({
type: 'POST',
url: window.updatePost,
data: { id: 21, title: 'snappy title' },
//kept short for clarity
});
ここでは、jsonのidとtitleの2つの値が一致するアクションにバインドされます。
public JsonResult UpdatePost(int id, string title) {
...
}
ジェネリック、セッションベースのモデルバインディング
時には、モデル全体を保存し、アクションやコントローラに渡して転送する必要がある場合もあります。このタイプの要求に対してセッションを良好に解決してモデルを格納する。これをMVCの強力なモデルバインディング機能と組み合わせると、エレガントなやり方が得られます。 3つの簡単なステップでジェネリック・セッション・ベースのモデル・バインディングを作成できます。
ステップ1:モデルバインダーを作成する
モデルバインダー自体を作成します。個人的には、 / Infrastructure / ModelBindersフォルダにSessionDataModelBinderクラスを作成しました。
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;
}
}
ステップ2:バインダーを登録する
次のようなモデルがあるとします。
public class ReportInfo
{
public int ReportId { get; set; }
public ReportTypes TypeId { get; set; }
}
public enum ReportTypes
{
NotSpecified,
Monthly, Yearly
}
Application_StartメソッドのGlobal.asaxで、このモデルのセッションベースモデルバインダーを登録できます。
protected void Application_Start()
{
.........
// Model binders.
// Remember to specy unique SessionKey
ModelBinders.Binders.Add(typeof(ReportInfo),
new SessionDataModelBinder<ReportInfo>("ReportInfo"));
}
ステップ3:それを使用してください!
今度は、アクションにパラメータを追加するだけで、このモデルバインダーのメリットを得ることができます 。
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();
}
}
PostModelへのバインディングを防止する
(ポスト)モデルを考える:
public class User
{
public string FirstName { get; set; }
public bool IsAdmin { get; set; }
}
次のように表示されます。
@using (Html.BeginForm()) {
@Html.EditorFor(model => model.FirstName)
<input type="submit" value="Save" />
}
悪意のあるユーザーがIsAdminを割り当てるのを防ぐために、アクションでBind
属性を使用できます。
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}
ファイルアップロード
モデル:
public class SampleViewModel
{
public HttpPostedFileBase file {get;set;}
}
表示:
@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>
}
アクション:
[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);
}
モデルバインダーを使用して動的フォーマットで日付フィールドを手動で検証する
異なるユーザが異なる日時フォーマットを必要とする場合は、受信日付文字列をフォーマットに従って実際の日付に解析する必要があります。この場合、このスニペットが役に立ちます。
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);
}