I was wondering what the best implementation for a global error (doesn't have to be errors, can also be success messages) handler would be? Let me break it down for you with an example:
User tries to delete a record
Deletion fails and an error is logged
User redirects to another page
Display error message for user (using a HtmlHelper or something, don't want it to be a specific error page)
I'm just curious what you guys think. I've been considering TempData, ViewData and Session but they all have their pros and cons.
TIA!
UPDATE:
I'll show an example what I exactly mean, maybe I wasn't clear enough.
This is an example of a method that adds a message when user deletes a record.
If user succeeds, user redirects to another page
public ActionResult DeleteRecord(Record recordToDelete)
{
// If user succeeds deleting the record
if (_service.DeleteRecord(recordToDelete)
{
// Add success message
MessageHandler.AddMessage(Status.SUCCESS, "A message to user");
// And redirect to list view
return RedirectToAction("RecordsList");
}
else
{
// Else return records details view
return View("RecordDetails", recordToDelete);
}
}
And in the view "RecordsList", it would be kinda cool to show all messages (both error and success messages) in a HtmlHelper or something.
<%= Html.RenderAllMessages %>
This can be achieved in many ways, I'm just curious what you guys would do.
UPDATE 2:
I have created a custom error (message) handler. You can see the code if you scroll down.
Just for fun, I created my own custom error (message) handler that works pretty much as TempData, but with the small difference that this handler is accessible all over the application.
I'm not going to explain every single step of code, but to sum it all up, I used IHttpModule to fire a method for every request and Session to save data. Below is the code, feel free to edit or give suggestions for improvements.
Web.config (Define module)
<httpModules>
<add name="ErrorManagerModule" type="ErrorManagerNamespace.ErrorManager"/>
</httpModules>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorManagerModule" type="ErrorManagerNamespace.ErrorManager"/>
</modules>
</system.webServer>
ErrorManager.cs (Error manager handler code)
public class ErrorManager : IRequiresSessionState, IHttpModule
{
private const string SessionKey = "ERROR_MANAGER_SESSION_KEY";
public enum Type
{
None,
Warning,
Success,
Error
}
/*
*
* Public methods
*
*/
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(Initiliaze);
}
public static IList<ErrorModel> GetErrors(ErrorManager.Type type = Type.None)
{
// Get all errors from session
var errors = GetErrorData();
// Destroy Keep alive
// Decrease all errors request count
foreach (var error in errors.Where(o => type == ErrorManager.Type.None || o.ErrorType == type).ToList())
{
error.KeepAlive = false;
error.IsRead = true;
}
// Save errors to session
SaveErrorData(errors);
//return errors;
return errors.Where(o => type == ErrorManager.Type.None || o.ErrorType == type).ToList();
}
public static void Add(ErrorModel error)
{
// Get all errors from session
var errors = GetErrorData();
var result = errors.Where(o => o.Key.Equals(error.Key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
// Add error to collection
error.IsRead = false;
// Error with key is already associated
// Remove old error from collection
if (result != null)
errors.Remove(result);
// Add new to collection
// Save errors to session
errors.Add(error);
SaveErrorData(errors);
}
public static void Add(string key, object value, ErrorManager.Type type = Type.None, bool keepAlive = false)
{
// Create new error
Add(new ErrorModel()
{
IsRead = false,
Key = key,
Value = value,
KeepAlive = keepAlive,
ErrorType = type
});
}
public static void Remove(string key)
{
// Get all errors from session
var errors = GetErrorData();
var result = errors.Where(o => o.Key.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
// Error with key is in collection
// Remove old error
if (result != null)
errors.Remove(result);
// Save errors to session
SaveErrorData(errors);
}
public static void Clear()
{
// Clear all errors
HttpContext.Current.Session.Remove(SessionKey);
}
/*
*
* Private methods
*
*/
private void Initiliaze(object o, EventArgs e)
{
// Get context
var context = ((HttpApplication)o).Context;
// If session is ready
if (context.Handler is IRequiresSessionState ||
context.Handler is IReadOnlySessionState)
{
// Load all errors from session
LoadErrorData();
}
}
private static void LoadErrorData()
{
// Get all errors from session
var errors = GetErrorData().Where(o => !o.IsRead).ToList();
// If KeepAlive is set to false
// Mark error as read
foreach (var error in errors)
{
if (error.KeepAlive == false)
error.IsRead = true;
}
// Save errors to session
SaveErrorData(errors);
}
private static void SaveErrorData(IList<ErrorModel> errors)
{
// Make sure to remove any old errors
HttpContext.Current.Session.Remove(SessionKey);
HttpContext.Current.Session.Add(SessionKey, errors);
}
private static IList<ErrorModel> GetErrorData()
{
// Get all errors from session
return HttpContext.Current.Session[SessionKey]
as IList<ErrorModel> ??
new List<ErrorModel>();
}
/*
*
* Model
*
*/
public class ErrorModel
{
public string Key { get; set; }
public object Value { get; set; }
public bool KeepAlive { get; set; }
internal bool IsRead { get; set; }
public Type ErrorType { get; set; }
}
HtmlHelperExtension.cs (An extension method for rendering the errors)
public static class HtmlHelperExtension
{
public static string RenderMessages(this HtmlHelper obj, ErrorManager.Type type = ErrorManager.Type.None, object htmlAttributes = null)
{
var builder = new TagBuilder("ul");
var errors = ErrorManager.GetErrors(type);
// If there are no errors
// Return empty string
if (errors.Count == 0)
return string.Empty;
// Merge html attributes
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);
// Loop all errors
foreach (var error in errors)
{
builder.InnerHtml += String.Format("<li class=\"{0}\"><span>{1}</span></li>",
error.ErrorType.ToString().ToLower(),
error.Value as string);
}
return builder.ToString();
}
}
Usage for creating errors
// This will only be available for one request
ErrorManager.Add("Key", "An error message", ErrorManager.Type.Error);
// This will be available for multiple requests
// When error is read, it will be removed
ErrorManager.Add("Key", "An error message", ErrorManager.Type.Error, true);
// Remove an error
ErrorManager.Remove("AnotherKey");
// Clear all error
ErrorManager.Clear();
Usage for rendering errors
// This will render all errors
<%= Html.RenderMessages() %>
// This will just render all errors with type "Error"
<%= Html.RenderMessages(ErrorManager.Type.Error) %>
I'm confused by these steps:
Deletion fails and an error is logged
User redirects to another page
Why would you redirect the User when an error occurs? That doesnt make any sense, unless im misunderstanding something.
Generally, i follow these guidelines:
Error with form submission (e.g HTTP POST): check ModelState.IsValid and return the same View and render the error out with #Html.ValidationSummary()
Error with AJAX call: return JsonResult (like #Tomas says), and use basic client-side scripting to inspect the JSON and show the result
Error with domain/business: throw custom exceptions and let the controller catch them and add to ModelState as above
I prefer writing my server layer as an API emitting JSON - in ASP.NET MVC that's real simple - you just create a bunch of nested anonymous objects, and return Json(data);. The JSON object is then consumed by the client layer, which consists of html, css and javascript (I use jQuery a lot, but you might prefer other tools).
Since javascript is dynamic, it is then real easy to just have a property status on the data object, and the client side script can interpret that and display status or error messages as needed.
For example, consider the following action method:
public ActionResult ListStuff()
{
var stuff = Repo.GetStuff();
return Json(new { status = "OK", thestuff = stuff });
}
This will return JSON in the following format:
{ "status": "OK", "thestuf": [{ ... }, { ... }] }
where ... is a placeholder for the properties of stuff. Now, if I want error handling, I can just do
try
{
var stuff = Repo.GetStuff();
return Json(new { status = "OK", thestuff = stuff});
}
catch (Exception ex)
{
Log.Error(ex);
return Json(new { status = "Fail", reason = ex.Message });
}
Since javascript is dynamic, it doesn't matter that the two anonymous objects don't have the same properties. Based on the value of status, I'll only look for properties that are actually there.
This can be implemented even better if you create your own action result classes, which extend JsonResult and add the status property automatically. For example, you can create one for failed requests that takes an exception in the constructor and one for successful ones than take an anonymous object.
If all you're going to do is redirect the user to another page, then you can use any ActionMethod to do so and just redirect to it.
If you want a global error, such as a 500 or 403 or some other error, then the MVC 3 default template creates an _Error.cshtml page for you and registers the error handler in the global.asax.
If you want to catch specific errors, then you can register additional handlers in the same place and tell the system which Error page to use for that error.
Related
How to catch this error in the back-end C# code?
See the solution usedat the end of the question description.
It is due to integer overflow for a HTTP GET URL where the URL contains an integer parameter. Calls with a 32 bit integer sized value work (e.g., "1234" works and the API is called).
HTTP GET www.domain.net/api/product/{productIdInteger}
Where the JavaScript caller calls HTTPS
www.domain.net/api/product/222222222222222222222222222222222222
The response looks like:
{
"errors": {
"productIdInteger": [
"The value '222222222222222222222222222222222222' is not valid."
]
},
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-f6a8a724853f3f44ba9fc6468a42ad94-aca067537e73b645-00"
}
Startup.cs adds a middleware to catch exceptions at the WebAPI controller level.
This does not catch the URL parameter parsing error or a bad JSON in HTTP Body parsing error
Startup.cs has app.UseMiddleware();
MyErrorMiddleware has this invoke method and MyExceptionHandlerAsync is never called for this case.
public async Task Invoke(HttpContext context)
{
try
{
//This line gets called
await _next(context);
}
catch (Exception ex)
{
//The catch block is not executed if the integer passed to the controller method overflows a 32 bit integer
//The catch block is executed if an C# WebAPI controller method is called and the controller method throws an exception
await MyExceptionHandlerAsync(context, ex);
}
}
Here is what worked best for our application.
Create base class for controller methods (code below) having a Boolean errorFlag and a List ErrorList
Derive the return objects for controller methods off the base class in #1. Each of the return classes had the error properties plus the data returned from the controller method. The data property returned for the controller would be called "ReturnData" to make the front-end JavaScript easier.
Add the model validation error handler to Startup.cs. Return the same base class as in step #1
Add in the error handling middleware catch block, code to return the error using the same error class from step #1
This handles 1) business error from controller (not a thrown exception), 2) exception thrown from controller or a method it calls, no need to have a try/catch in the controller, 3) error passing data to the WebAPI controller method (model validation error)
public class ReturnBaseClass
{
public ReturnBaseClass() { errorList = new List<string>()}
public bool errorFlag {get;set;}
public List<string> errorList {get;set;}
}
//A controller return data could look like
public class CourseDataReturn : ReturnBaseClass
{
//Always called ReturnData to make front-end JavaScript easier
public CourseInfo ReturnData {get;set;}
}
//CourseInfo is the data returned from the data repository method called from the controller.
public class CourseInfo
{
public int CourseId {get;set;}
public string CourseName {get;set;}
}
We simplified our return HTTP status codes to OK, Bad Request, Internal Server Error for back-end WebAPI calls which are made. All other HTTP status codes would be a HTTP transport error (timeout, not found (URL is bad), ....
That got us past the REST mismatch of HTTP NotFound being either the URL is not found, such as domain name does not exist, and the database row being retrieved for a HTTP GET student by student ID not finding the student.
It is not an exception. It is ModelState not valid.
So you can catch this error by:
Step 1:
Add ConfigureApiBehaviorOptions in Program.cs file:
builder.Services.AddControllers().ConfigureApiBehaviorOptions(options => {
options.SuppressModelStateInvalidFilter = true;
});
Step 2:
Handle errors in top of Controller file:
if (!ModelState.IsValid) {
var errors = new List<string>();
foreach (var state in ModelState)
{
foreach (var error in state.Value.Errors)
{
errors.Add(error.ErrorMessage);
}
}
// Do something to handle error list
}
The code below is based on [https://briancaos.wordpress.com/2019/11/29/net-core-catch-model-binding-exceptions/]
We used a variation on this code in the solution
//In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
... //all of the configuration for services, etc...
//Add this at the very end of ConfigureServices()
//Handle errors - model bind error, bad parameter passed to WebAPI endpoint, ...
services.Configure<ApiBehaviorOptions>(options =>
options.InvalidmsResponseFactory = (ac =>
{
var ms = ac.ms;
//Properties related to errors: ms.IsValid, s.ErrorCount, ms.ValidationState
//var errors = ms.Where(x => x.Value.Errors.Count > 0);
var errs = new ValidationProblemDetails(ac.ms);
var errList = new List<string>();
//errList.Add($"Detail: {errs.Detail}");
errList.Add($"Title: {errs.Title}");
//errList.Add($"URL: {errs.Instance}");
//errList.Add($"HTTP Status Code: {errs.Status}");
//errList.Add($"Error Type: {errs.Type}");
List<string> mlist = new List<string>();
foreach (string k in errs.Errors.Keys)
{
//build one message line <KEY>: <ERROR MESSAGE 1>, <ERROR MESSAGE 2>, ...
string m = $"{k}: ";
string[] value = errs.Errors[k];
if (value.Length > 0)
{
m = $"{m}{string.Join(",", value)}";
}
mlist.Add(m);
}
string r = "";
if (msgList.Any())
{
r = string.Join(",", msgList);
}
return new BadRequestObjectResult(r);
}
));
I'm working on a kind of web-api. The parameters of my controller requests is automatically deserialized to dtos with Json.Net. I'm trying to bubble up the Json.Net deserializing errors to my ApiMiddleware.
var mvcBuilder = services.AddMvc().AddNewtonsoftJson(options =>
{
options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
options.SerializerSettings.Error = (sender, args) =>
{
var message = args.ErrorContext.Error.Message;
args.ErrorContext.Handled = false;
throw new ValidationException(message);
};
});
I supposed that the exception will buble up and my middleware will able to catch it and log the url of the request and deserializing error. But Json.Net catches all the inner throws by himself, so the only thing that comes to my middleware is "Current error context error is different to requested error”-exception.
I've explored about a dozen of questions somehow connected with my problem, but there was no solution.
So, does anybody know how to throw the ValidationException outside or make the original deserializing-exception delievered to my middleware? Thanks
After reading the ASP.NET Core source code, it seems that NewtonsoftJsonInputFormatter is designed to suppress JsonExceptions and convert it into model state errors. One workaround to this issue in ASP.NET Core 3.1 is to set MvcNewtonsoftJsonOptions.AllowInputFormatterExceptionMessages to false. This causes JsonExceptions to be captured on the model state, which you can then examine in an action filter and throw your own exceptions if you encounter one:
In Startup.cs:
services.AddControllers(c => { c.Filters.Add(new JsonErrorActionFilter()); })
.AddNewtonsoftJson(c => { c.AllowInputFormatterExceptionMessages = false; });
And the action filter could be something like this:
public class JsonErrorActionFilter : IActionFilter, IOrderedFilter
{
public void OnActionExecuted(ActionExecutedContext context) { }
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.Result == null && !context.ModelState.IsValid
&& HasJsonErrors(context.ModelState, out var jsonException))
{
throw new ValidationException(jsonException.Message);
}
}
private static bool HasJsonErrors(ModelStateDictionary modelState, out Exception jsonException)
{
foreach (var entry in modelState.Values)
{
foreach (var error in entry.Errors)
{
if (error.Exception is JsonException)
{
jsonException = error.Exception;
return true;
}
}
}
jsonException = null;
return false;
}
// Set to a large negative value so it runs earlier than ModelStateInvalidFilter
public int Order => -200000;
}
This is likely using MvcNewtonsoftJsonOptions.AllowInputFormatterExceptionMessages in a way that it's not intended to be used, so the solution will probably break if you upgrade to a different version of ASP.NET Core. But for now it may get the job done.
I want to add an ID (GUID) to the exception and:
Log it
Return it to the client json response
Where should I generate this log ID value and add it to the exception message that is logged. And where to change the following default response?
{
"targetUrl": null,
"result": null,
"success": false,
"error": {
"message": "An internal error occurred during your request!",
"details": "..."
},
"unAuthorizedRequest": false
}
I am using .NET Core version.
If you want to disable displaying the message for a particular AJAX call, add abpHandleError: false into the abp.ajax options.
Or you can disable the default behavior of the framework exception wrapper
public class PeopleController : AbpController
{
[HttpPost]
[WrapResult(WrapOnSuccess = false, WrapOnError = false)]
public JsonResult SavePerson(SavePersonModel person)
{
//TODO: save new person to database and return new person's id
return Json(new {PersonId = 42});
}
}
https://aspnetboilerplate.com/Pages/Documents/Javascript-API/AJAX?searchKey=wrap#asp-net-mvc-controllers
Another thing is; you can send exception details to the client by the below configuration
...
using Abp.Web.Configuration;
...
public override void PreInitialize()
{
Configuration.Modules.AbpWebCommon().SendAllExceptionsToClients = true;
}
...
https://aspnetboilerplate.com/Pages/Startup-Configuration#configuring-modules
Result Wrapping & Exception Handling:
ASP.NET Boilerplate does not wrap Web API actions by default if an action has successfully executed. It, however, handles and wraps exceptions. You can add the WrapResult/DontWrapResult attributes to actions and controllers for finer control. You can change this default behavior from the startup configuration (using Configuration.Modules.AbpWebApi()...). See the AJAX document for more info about result wrapping.
https://aspnetboilerplate.com/Pages/Documents/Web-API-Controllers?searchKey=wrap#result-wrapping-exception-handling
Wrapping Results
ASP.NET Boilerplate wraps the return values of dynamic Web API actions using an AjaxResponse object. See the ajax documentation for more information on wrapping. You can enable/disable wrapping per method or per application service. See this example application service:
public interface ITestAppService : IApplicationService
{
[DontWrapResult]
DoItOutput DoIt(DoItInput input);
}
https://aspnetboilerplate.com/Pages/Documents/Dynamic-Web-API?searchKey=wrap#wrapping-results
Lastly you can write your own ResultWrapperHandler...
public class CustomResultWrapperHandler : ResultWrapperHandler, ITransientDependency
{
//...
protected override void WrapResultIfNeeded(HttpRequestMessage request, HttpResponseMessage response)
{
//...
base.WrapResultIfNeeded(request, response);
}
}
if you want to get special message in some case you can use
throw new UserFriendlyException("your message");
the above code just effects on error message and doesn't show the details.
so its good option for production version.
I have a controller
[HttpGet]
[RoutePrefix("api/products/{productId}")]
public HttpResponseMessage Products(int productId,TypeEnum ptype=TypeEnum.Clothes)
{
if(!Enum.IsDefined(typeOf(TypeEnum),ptype))
//throw bad request exception
else
//continue processing
}
Myenum is declared as
public TypeEnum
{
Clothes,
Toys,
Electronics
}
Currently if,some garbage value is passed it is getting converted into default value.
What I want to do is if i call the controller as api/products/1 then the ptype should be assigned default value i.e clothes. If I call the controller as api/products/1?pType=somegarbagevalue then the controller should throw bad request exception. How can I achieve this?
Defining all your enum parameters as strings and then parsing them everywhere means you have to do this on every single action and you will need to come up with a consistent approach such that all parsing errors conform.
This is a parameter binding issue and should not be dealt with in the controller layer, it should be taken care of in the pipeline. One way to do this is to create a custom filter and add it to your config.
public class ModelStateValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = <your standardised error response>
}
}
}
And in your global.asax.cs
...
GlobalConfiguration.Configure(WebApiConfig.Register);
...
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
config.Filters.Add(new ModelStateValidationAttribute());
...
}
}
If you're having trouble with the model state, it's type is a ModelStateDictionary and you simply iterate over it and then it's Errors property contains all the model binding issues. e.g.
modelState = actionContext.ModelState;
modelState.ForEach(x =>
{
var state = x.Value;
if (state.Errors.Any())
{
foreach (var error in state.Errors)
{
<work your magic>
}
}
});
You have to do with string and use TryParse() to convert string to Enum value.
public HttpResponseMessage Products(int productId,string ptype="Clothes")
{
TypeEnum category = TypeEnum.Clothes;
if(!Enum.TryParse(ptype, true, out category))
//throw bad request exception if you want. but it is fine to pass-through as default Cloathes value.
else
//continue processing
}
It may look naive but the benefit of this approach is to allow ptype parameter to whatever string and to perform process without exception when ptype fails to bind the value.
This type of validation should be handled in pipeline not in controller.
public abstract class ETagMatchAttribute : ParameterBindingAttribute
{
private ETagMatch _match;
public ETagMatchAttribute(ETagMatch match)
{
_match = match;
}
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
if (parameter.ParameterType == typeof(ETag))
{
return new ETagParameterBinding(parameter, _match);
}
return parameter.BindAsError("Wrong parameter type");
}
}
something like this. refer to MSDN link for detailed explanation
I've built an MVC application that looks like this:
MVC Application - Application Layer - Business Layer - Repository - Data
I've read the code of many applications and discovered that no-one seems to pass exceptions in manner similar to how data is passed.
In other words, data is referenced through an interface and reused in different contexts. If an exception occurs in the Data layer, how would this get sent to the GUI or relevant layer? Should an Interface be used? What is the construct for handling exceptions in this application?
I haven't read the doco u linked but i have a simple case where i raise exceptions in response to failure to satisfy an invariant. Say I'm creating an instance and it's "invalid" for some reason, let's say bad user input. Rather than having my entity be in an invalid state (which is a no-no in DDD) and let the system "validate" it, it throws an exception when it's being created. The relevant "validation messages" (for the user) are extracted from the same Specification instance that was not satisfied and my derived exception contains the values the UI needs.
Example of an InvariantException:
public class InvariantException : MyAppException
{
public object FailingObject = null;
public ModelStateDictionary ModelState = new ModelStateDictionary();
public InvariantException() { }
public InvariantException(object failingObject, ModelStateDictionary messages)
{
this.FailingObject = failingObject;
this.ModelState = messages;
}
public InvariantException(object failingObject, ModelStateDictionary messages,
Exception innerException)
: base("refer to ModelState", innerException)
{
this.FailingObject = failingObject;
this.ModelState = messages;
}
}
Example of a Specification that returns the relevant "validation messages" for the user/UI:
public class PostFieldLengthSpecification : ISpecification<Post>
{
private const string TITLE_LENGTH_RANGE = "5-100";
private const string BODY_LENGTH_RANGE = "20-10000";
public bool IsSatisfiedBy(Post post)
{
return this.GetErrors(post).IsValid;
}
public ModelStateDictionary GetErrors(Post post)
{
ModelStateDictionary modelState = new ModelStateDictionary();
if (!post.Title.Trim().Length.Within(TITLE_LENGTH_RANGE))
modelState.AddModelError(StongTypeHelpers.GetPropertyName((Post p) => p.Title),
"Please make sure the title is between {0} characters in length".With(TITLE_LENGTH_RANGE));
if (!post.BodyMarkup.Trim().Length.Within(BODY_LENGTH_RANGE))
modelState.AddModelError(StongTypeHelpers.GetPropertyName((Post p) => p.BodyMarkup),
"Please make sure the post is between {0} characters in length".With(BODY_LENGTH_RANGE));
return modelState;
}
}
Example of how the Factory never let's the invalid instance be created, instead it throws an exception and deposits the messages for the UI:
public static Post GetNewPost(string title, string bodyMarkup, DateTime posted)
{
var post = new Post(0, title, bodyMarkup, posted, new List<Comment>());
var fieldLengthSpec = new PostFieldLengthSpecification();
if (fieldLengthSpec.IsSatisfiedBy(post))
return post;
else
throw new InvariantException(post, fieldLengthSpec.GetErrors(post));
}
Finally, an example of a custom model binder that is used to catch said exception and pass the "invalid object" back to the action with the error messages:
public class PostModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(Post))
{
try
{
// Create Post
return base.BindModel(controllerContext, bindingContext);
}
catch (InvariantException ie)
{
// If invalid, add errors from factory to ModelState
bindingContext.ModelState.AddNewErrors(ie.ModelState);
bindingContext.ModelState.AddValuesFor<Post>(bindingContext.ValueProvider);
return ie.FailingObject;
}
}
Hope this helps.