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?
You can use SelectMany function c# to get error message from modelstate mvc. It will generate error message string it contains modelstate errors; we can return as json and display in html element. You have to define validations inside the class as per requirement.
Now all you need is a javascript function that is called this and prints the errors such as: function Grid_Error(e){ if(e. errors){ var message = "There are some errors:\n"; // Loop through the errors you defined in the controller $.
How to handle error in controller action method and pass error to the View? To pass error to the view we can use ModelState. AddModelError method (if the error is Model field specific) or simply ViewBag or ViewData can also be used.
In short, the ModelState is a collection of name and value pairs that are submitted to the server during a POST. It also contains error messages about each name-value pair, if any are found. ModelState is a property of a Controller instance, and can be accessed from any class that inherits from Microsoft. AspNetCore.
This works fantastic for us, though we never see ModelState errors and usually omit that part...
@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"))
)
)
[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);
}
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);
}
}
@(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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With