モデルをパラメーターとして受け取るMVCコントローラーアクションがあります。モデルが有効でない場合は JSON を返し、それ以外の場合はファイルを返します。
public class MainModel
{
public DownloadModel Downlaod {get;set;}
}
[ValidateAjaxRequest]
public ActionResult DownloadFile(DownloadVM model)
{
// get file using model
return File(byte[],"contenttype")
}
ValidateAjaxRequest
それがajaxリクエストであるかどうかを確認し、そうである場合はActionFilterAttribute
Jsonを使用してすべてのエラーを返すことに注意してください
public class ValidateAjaxRequestAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
return;
var modelState = filterContext.Controller.ViewData.ModelState;
if (!modelState.IsValid)
{
var errors = new List<MyStatus>();
//loop through all errors here and build errors list
filterContext.Result = new JsonResult()
{
Data = errors
};
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
}
}
次に、JavaScriptでjQueryを使用してデータを投稿し、エラーを処理しています。
$.ajax({
type: "POST",
data: $('#formid').serialize(),
url: '/Home/DownloadFile'
})
.done(function (response, textStatus, jqXHR)
{
// how do i show file here as popup?
}
.fail(function (jqXHR, textStatus, errorThrown) {
//show returned errors from the server
})
典型的なフォーム投稿の代わりに ajax 投稿を使用する理由は、投稿したくない同じビューに他のコントロールがあり、ModelState が有効でない場合はビュー全体を更新したくないためです。以下のようなことはしたくない
public ActionResult DownloadFile(MainModel model)
{
if(ModelState.IsValid)
{
// get file using model.Download
return File(byte[],"contenttype")
}
return View("Index",model)
// here i have to post entire MainModel and also re-render
// entire View if ModelState is not valid.
// i do not like this approach so im using ajax to post only
// what i needed and without needing to re-render view
}