How to return ModelState errors to Kendo grid in M

2019-02-21 15:49发布

I haven't been able to find an example of Kendo + MVC Web API where post/update methods return validation errors. It doesn't look like there is a Kendo extension that could make the following code work.

public HttpResponseMessage Post([ModelBinder(typeof(Prototype.WebApi.ModelBinders.DataSourceRequestModelBinder))][DataSourceRequest] DataSourceRequest request, User user)
    {
        if (this.ModelState.IsValid)
        {               
           //save               
        }
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState.ToDataSourceResult());            
    }

becaue ModelState in this context is System.Web.Http.ModelBinding.ModelStateDictionary and Kendo extensions expect System.Web.Mvc.ModelStateDictionary.

So what is the best way to return ModelState errors from Web API to Kendo?

1条回答
太酷不给撩
2楼-- · 2019-02-21 16:36

This works fantastic for us, though we never see ModelState errors and usually omit that part...

Kendo Grid

@model SysMaintViewModel
@(Html.Kendo().Grid<BuildingModel>()
    .Name("BuildingsGrid")
    .Columns(columns =>
    [Stuff Omitted]
    .DataSource(dataSource => dataSource
        .Ajax()
>>>     .Events(e => e.Error("error_handler"))
        .Model(model =>
        {
            model.Id(m => m.Id);
            model.Field(m => m.ProjectId).DefaultValue(Model.ProjectId);
            model.Field(m => m.IsActive).DefaultValue(true);
        })
        .Create(create => create.Action("CreateBuilding", "SysMaint"))
        .Read(read => read.Action("ReadBuildings", "SysMaint", Model))
        .Update(update => update.Action("UpdateBuilding", "SysMaint"))
        .Destroy(destroy => destroy.Action("DestroyBuilding", "SysMaint"))
    )
)

Controller

[HttpPost]
public JsonResult UpdateBuilding([DataSourceRequest]DataSourceRequest request, BuildingModel modelIn)
{
    var building = new BuildingModel();
    if (ModelState.IsValid)
    {
        try
        {
            building = _presentationService.UpdateBuilding(modelIn);
        }
        catch (Exception e)
        {
            ModelState.AddModelError(string.Empty, e.Message);
        }
    }
    else
    {
        var errMsg = ModelState.Values
            .Where(x => x.Errors.Count >= 1)
            .Aggregate("Model State Errors: ", (current, err) => current + err.Errors.Select(x => x.ErrorMessage));
        ModelState.AddModelError(string.Empty, errMsg);
    }
    var buildings = (new List<BuildingModel> {building}).ToDataSourceResult(request, ModelState);
    return Json(buildings, JsonRequestBehavior.AllowGet);
}

UPDATED Controller

We have found this flow to work a bit better and it adds error logging to Elmah (generic example)...

[HttpPost]
public JsonResult Update([DataSourceRequest]DataSourceRequest request, MyObjectModel modelIn)
{
    try
    {
        if (ModelState.IsValid)
        {
            var myObject = _presentationService.Update(modelIn, User.Identity.Name);
            var myObjectList = new List<MyObjectModel> { myObject };
            return Json(myObjectList.ToDataSourceResult(request, ModelState), JsonRequestBehavior.AllowGet);
        }
        else
        {
            var myObjectList = new List<MyObjectModel> { modelIn };
            return Json(myObjectList.ToDataSourceResult(request, ModelState), JsonRequestBehavior.AllowGet);
        }
    }
    catch (Exception e)
    {
        Elmah.ErrorSignal.FromCurrentContext().Raise(e);
        ModelState.AddModelError(string.Empty, e.Message);
        var myObjectList = new List<MyObjectModel> { modelIn };
        return Json(myObjectList.ToDataSourceResult(request, ModelState), JsonRequestBehavior.AllowGet);
    }
}

Common JavaScript and Kendo Window

@(Html.Kendo().Window()
    .Name("alertWindow")
    .Title("Status Message from Server")
    .Draggable()
    .Resizable()
    .Width(400)
    .Height(200)
    .Modal(true)
    .Visible(false)
)
function showAlertWindow(message) {
    var alertWindow = $('#alertWindow').data('kendoWindow');
    alertWindow.content(message);
    alertWindow.refresh();
    alertWindow.center();
    alertWindow.open();
}
function error_handler(e) {
    if (e.errors) {
        var message = "Errors:\n";
        $.each(e.errors, function (key, value) {
            if ('errors' in value) {
                $.each(value.errors, function () {
                    message += this + "\n";
                });
            }
        });
        showAlertWindow(message);
    }
}

Bonus

Our BaseModel also has an ErrorMessage parameter that we put other types of errors into that checks on page load if the same alert window should be opened for anything else.

$(document).ready(function () {
    if ("@Model.ErrorMessage" != "") {
        showAlertWindow("@Model.ErrorMessage");
    }
});

This has a very nice presentation when an error is thrown - keeps our in-house users from freaking out. I hope this helps you out.

查看更多
登录 后发表回答