May 25, 2012 06:47 by
Scott
This blog post shows how to validate models containing complex types such as Objects and Lists in ASP.NET MVC 3.
In a first step we modify the properties of the model ( Models/ValidationModel.cs) and add some complex types:
public class ValidUserNameAttribue : ValidationAttribute
{
public override bool IsValid(object value)
{
return (value != null && value.ToString() == "Bob");
}
}
public class User
{
[Required]
[StringLength(8, MinimumLength = 3)]
[ValidUserNameAttribue(ErrorMessage = "User name != 'Bob'")]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(8, MinimumLength = 3)]
[Display(Name = "Display name")]
public string DisplayName { get; set; }
}
public class ValidationModel
{
public User User { get; set; }
public List Users { get; set; }
}
In a second step we modify the form ( Views\Home\Partial\_Form.cshtml) to add input element for the new model properties:
@model MVC3_Ajax_Form_jQuery_Validation.Models.ValidationModel
@DateTime.Now: Partial/_Form.cshtml rendered
< hr/>
@using (Html.BeginForm("Form", "Home"))
{
<h1><em>User</em> object</h1>
<p>
@Html.LabelFor(m => m.User.UserName):
@Html.EditorFor(m => m.User.UserName)
@Html.ValidationMessageFor(m => m.User.UserName)
</p>
<p>
@Html.LabelFor(m => m.User.DisplayName):
@Html.EditorFor(m => m.User.DisplayName)
@Html.ValidationMessageFor(m => m.User.DisplayName)
</p>
<h1>List of <em>User</em> objects</h1>
for (var i = 0; i <= 1; i++)
{
<h2>User @i</h2>
<p>
@Html.LabelFor(m => m.Users[i].UserName):
@Html.EditorFor(m => m.Users[i].UserName)
@Html.ValidationMessageFor(m => m.Users[i].UserName)
</p>
<p>
@Html.LabelFor(m => m.Users[i].DisplayName):
@Html.EditorFor(m => m.Users[i].DisplayName)
@Html.ValidationMessageFor(m => m.Users[i].DisplayName)
</p>
}
<input type="submit" value="Submit" />
}
In a last step we adapt the “success-view” ( Views\Home\Partial\_Success.cshtml) that is shown after the data have been successfully validated on the server side:
@model MVC3_Ajax_Form_jQuery_Validation.Models.ValidationModel
< p><strong>Model is valid :)</strong></p>
< p>
Model.User.Username: '@Model.User.UserName'<br />
Model.User.DisplayName: '@Model.User.DisplayName'<br />
Model.Users[0].Username: '@Model.Users[0].UserName'<br />
Model.Users[0].DisplayName: '@Model.Users[0].DisplayName'<br />
Model.Users[1].Username: '@Model.Users[1].UserName'<br />
Model.Users[1].DisplayName: '@Model.Users[1].DisplayName'
</ p>
As you can see in the source code above, there is no magic; model binding and validation of complex objects and lists work out of the box in ASP.NET MVC 3.
March 22, 2012 06:56 by
Scott
In this blog post I will show how to set up custom error pages in ASP.NET MVC 3 applications to create user-friendly error messages instead of the (yellow) IIS default error pages for both “normal” (non-AJAX) requests and jQuery AJAX requests.
In this showcase we will implement custom error pages to handle the HTTP error codes 404 (“Not Found”) and 500 (“Internal server error”) which I think are the most common errors that could occur in web applications. In a first step we will set up the custom error pages to handle errors occurring in “normal” non-AJAX requests and in a second step we add a little JavaScript jQuery code that handles jQuery AJAX errors.
We start with a new (empty) ASP.NET MVC 3 project and activate custom errors in the Web.config by adding the following lines under <system.web>:
<customErrors mode="On" defaultRedirect="/Error">
<error redirect="/Error/NotFound" statusCode="404"/>
<error redirect="/Error/InternalServerError" statusCode="500"/>
</customErrors>
Note: You can set mode=”Off” to disable custom errors which could be helpful while developing or debugging. Setting mode=”RemoteOnly” activates custom errors only for remote clients, i.e. disables custom errors when accessing via http://localhost/[...]. In this example setting mode=”On” is fine since we want to test our custom errors. You can find more information about the <customErrors> element here.
In a next step we remove the following line in Global.asax.cs file:
filters.Add(new HandleErrorAttribute());
and add a new ErrorController (Controllers/ErrorController.cs):
public class ErrorController : Controller
{
public ActionResult Index()
{
return InternalServerError();
}
public ActionResult NotFound()
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View("NotFound");
}
public ActionResult InternalServerError()
{
Response.TrySkipIisCustomErrors = true;
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return View("InternalServerError");
}
}
In a last step we add the ErrorController‘s views (Views/Error/NotFound.cshtml and Views/Error/InternalServerError.cshtml) that defines the (error) pages the end user will see in case of an error. The views include a partial view defined in Views/Shared/Error/NotFoundInfo.cshtml respectively Views/Shared/Error/InternalServerErrorInfo.cshtml that contains the concrete error messages. As we will see below using these partial views enables us to reuse the same error messages to handle AJAX errors.
Views/Error/NotFound.cshtml:
@{
ViewBag.Title = "Not found";
}
@{
Html.RenderPartial("Error/NotFoundInfo");
}
Views/Shared/Error/NotFoundInfo.cshtml:
The URL you have requested was not found.
Views/Error/InternalServerError.cshtml:
@{
ViewBag.Title = "Internal server error";
}
@{
Html.RenderPartial("Error/InternalServerErrorInfo");
}
Views/Shared/Error/InternalServerErrorInfo.cshtml:
An internal Server error occured.
To handle errors occurring in (jQuery) AJAX calls we will use jQuery UI to show a dialog containing the error messages. In order to include jQuery UI we need to add two lines to Views/Shared/_Layout.cshtml:
<link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
Moreover we add the following jQuery JavaScript code (defining the global AJAX error handling) and the Razor snippet (defining the dialog containers) to Views/Shared/_Layout.cshtml:
< script type="text/javascript">
$(function () {
// Initialize dialogs ...
var dialogOptions = {
autoOpen: false,
draggable: false,
modal: true,
resizable: false,
title: "Error",
closeOnEscape: false,
open: function () { $(".ui-dialog-titlebar-close").hide(); }, // Hide close button
buttons: [{
text: "Close",
click: function () { $(this).dialog("close"); }
}]
};
$("#InternalServerErrorDialog").dialog(dialogOptions);
$("#NotFoundInfoDialog").dialog(dialogOptions);
// Set up AJAX error handling ...
$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError)
{
if (jqXHR.status == 404) {
$("#NotFoundInfoDialog").dialog("open");
} else if (jqXHR.status == 500) {
$("#InternalServerErrorDialog").dialog("open");
} else {
alert("Something unexpected happend :( ...");
}
});
});
</ script>
<div id="NotFoundInfoDialog">
@{ Html.RenderPartial("Error/NotFoundInfo"); }
</div>
<div id="InternalServerErrorDialog">
@{ Html.RenderPartial("Error/InternalServerErrorInfo"); }
</div>
As you can see in the Razor snippet above we reuse the error texts defined in the partial views saved in Views/Shared/Error/.
To test our custom errors we define the HomeController (Controllers/HomeController.cs) as follows:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Error500()
{
throw new Exception();
}
}
and the corresponding view Views/Home/Index.cshtml:
@{
ViewBag.Title = "ViewPage1";
}
<script type="text/javascript">
$function () {
$("a.ajax").click(function (event) {
event.preventDefault();
$.ajax({
url: $(this).attr('href'),
});
});
});
</script>
<ul>
<li>@Html.ActionLink("Error 404 (Not Found)", "Error404")</li>
<li>@Html.ActionLink("Error 404 (Not Found) [AJAX]", "Error404", new { },
new { Class = "ajax" })</li>
<li>@Html.ActionLink("Error 500 (Internal Server Error)", "Error500")</li>
<li>@Html.ActionLink("Error 500 (Internal Server Error) [AJAX]", "Error500", new { }, new { Class = "ajax" })</li>
</ul>
To test the custom errors you can launch the project and click one of the four links defined in the view above. The “AJAX links” should open a dialog containing the error message and the “non-AJAX” links should redirect to a new page showing the same error message.
Summarized this blog post shows how to set up custom errors that handle errors occurring in both AJAX requests and “non-AJAX” requests. Depending on the project, one could customize the example code shown above to handle other HTTP errors as well or to show more customized error messages or dialogs.
March 15, 2012 08:11 by
Scott
Description
Normally in a MVC3 application we have generally 3 sections.
Model, Controller, View.
Now in a real-world project in our Application there may be many modules.
So separating each of the modules in our MVC3 application is highly recommendable.
In ASP.Net MVC3 we have features called "area" where we can separate into different modules.
For e.g. ADMIN, CUSTOMER, PRODUCT etc.
Now I will show you an example of how to create the "AREA" in MVC3.
Step 1:
Now first create an ASP.Net MVC3 application.
Step 2:
Now right-click the project and "add" a new area with the name "Admin"; that means in our project there are an "admin" module. See the following picture:
Step 3:
After adding the "admin" area you will see that a separate "Areas" folder will be created. Under this folder controller, model and views, shared folders will be generated.
Now under the "Controller" folder add a new controller named "admin controller".
Now from the "admincontroller" create a new view named "index" from the "index" method.
After that copy the "_layout.cshtml" from the main project and paste it under the "Areas" Shared folder.
Now the whole structure will look like the following once.
Step 4:
Now add a new action link for "Admin" under the _layout.cshtml" like the following picture:
<li>@Html.ActionLink("Admin", "Index", "Admin")</li>
So the admin link will be created and when we click the "admin" link it will redirect to the "admin" part of our project which is under the "Areas".
Step 5:
Now go to the "AdminAreaRegistration" file (just the following picture) and modify the existing code like below:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Admin", action = "Index", id = UrlParameter.Optional }
);
I have added here only Controller Name "Admin"( controller = "Admin") extra under the new {} section.
Now run the application it will look like the following picture:
See here "Admin" link has come.
Now after clicking the admin link it will show like the following picture:
See the url routing. It calls our "Admin " Areas part.
So by this way we can group our project modules into our ASP.Net MVC3 application.
Conclusion
So in this article we have learned what is the purpose of use of "Area" features in ASP.Net MVC3 and how to implement them.