Rules to Better MVC - 29 Rules
Since 1990, SSW has supported the developer community by publishing all our best practices and rules for everyone to see.
If you still need help, visit ASP.NET MVC Web Application Development and book in a consultant.
Help your users by setting the default field when your MVC Website loads.
By selecting a default field for your users when a page loads you can improve the usability of your website by reducing the amount of steps needed to perform a task.
Here is a way to do this with MVC 3 and Razor:
1. Add a div with a class around the field you want to set focus on
<div class="focus"> @Html.EditorFor(model => model.FirstName) @Html.ValidationMessageFor(model => model.FirstName) </div>
-
Then use jQuery to select the class and set focus:
$(function() { $('.focus :input').focus(); });
-
Understanding the Enterprise MVC request process is crucial for ensuring smooth operations, efficient handling of requests, and alignment with organizational goals. It enables timely delivery and keeps everyone well-informed throughout the process.
UpdateModel will throw an exception if validation of the model fails. Instead of managing an exception, you should use TryUpdateModel as it adds the error to the ModelState dictionary. This lets you check the ModelState.IsValid property and decide how to handle the issue from there.
This is an important distinction to be made because if we had used UpdateModel then our if (ModelState.IsValid) would not be hit in the event of a failure to bind.
public ActionResult Create() { Entity myEntity = new Entity(); UpdateModel(myEntity); }
Figure: Bad example – UpdateModel may throw an exception and the ModelState dictionary won’t be updated
public ActionResult Create() { Entity myEntity = new Entity(); TryUpdateModel(myEntity); if (ModelState.IsValid) { // ... } }
Figure: Good example – TryUpdateModel will gracefully handle validation and will add to the ModelState dictionary so we can check for validity
Model binding in the ASP.NET MVC framework is simple. Your action methods need data, and the incoming HTTP request carries the data you need. The catch is that the data is embedded into POST-ed form values, and possibly the URL itself. Enter the DefaultModelBinder, which can magically convert form values and route data into objects.
Model binders allow your controller code to remain cleanly separated from the dirtiness of interrogating the request and its associated environment.
public ActionResult Create(FormCollection values) { Recipe recipe = new Recipe(); recipe.Name = values["Name"]; // ... return View(); }
Figure: Bad example – Manually reading form values and assigning them to properties is tedious boiler-plate code!
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(Recipe newRecipe) { // ... return View(); }
Figure: Good example – Using MVC’s model binding allows you to work with an automatically-populated object instead
Any sensitive data that is sent over the wire must be protected using a secure transport such as HTTPS. MVC (version 2, Preview 2 or higher) allows you to specify that HTTPS is required for an action. It’s important that the GET method is secure as well as the POST method to avoid people sending sensitive form data over the wire.
public ActionResult Register() { return View(); }
Figure: Bad example – The Register method isn’t secure
[RequireHttps] public ActionResult Login() { return View(); }
Figure: Good example – The Login method is protected by HTTPS
MVC provides a ViewData collection in which you can store miscellaneous pieces of information to pass to the View. It’s also accessible it as a Dynamic object by using the ViewBag. However, you should avoid using ViewData or ViewBag because it isn’t type-safe and relies on Magic Strings.
public ActionResult Index() { ViewData["Message"] = "Some Message"; return View(); } <h1><%: ViewData["Message"] &></h1>
Figure: Bad example – ViewData being used to pass information to the View isn’t type-safe
public ActionResult Index() { var model = new IndexViewModel(); model.Message = "Some Message"; return View(); } <h1><%: Model.Message %></h1>
Figure: Good example – Using a ViewModel is a safer way to pass data
Returning a view that is named differently to the action confuses the MVC process and can make the code difficult to maintain.
In cases where data is posted, if you don't do a redirect and the user hits the refresh/reload button in the browser, the data can be is submitted more than once. This can lead to duplicate data being stored in your database.
Redirecting after posted data has been processed is called the Post-Redirect-Get (or PRG) pattern.
[HttpPost] public ActionResult Create(CreateModel model) { // ... save to DB, then: ViewBag.Message = "Successfully created " + model.Name; return View("Success"); }
Figure: Bad example – Returning a different view is misleading and potentially dangerous
[HttpPost] public ActionResult Create(CreateModel model) { // ... save to DB, then: return RedirectToAction("Success", new { message = "Successfully created " + model.Name }); } public ActionResult Success(string message) { ViewBag.Message = message; return View(); }
Figure: Good example – Using the PRG pattern to avoid duplicate data being posted
Adding code to the Application_Start method in the Global.asax file is the easiest and most straight-forward approach for executing startup logic, however, this code should be encapsulated in static methods outside the Global.asax file. Doing this helps provide cleaner code and encourages proper adherence to the Single Responsibility principle.
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
Figure: Bad example – Logic is implemented in the Application_Start method which breaks the Single Responsibility Principle
ASP.NET MVC provides the AuthorizeAttribute which ensures there is a logged in user before it will execute an action. You can also provide parameters to restrict actions or controllers to only be accessible to certain roles or users. This is a better solution than checking whether a logged-in user exists in code as the authorization itself doesn’t need to be repeated.
public ActionResult Delete(string tagName) { if (!Request.RequestContext.HttpContext.User.IsInRole("CanDeleteTags")) { return new System.Web.Mvc.HttpUnauthorizedResult(); } // delete view return View(); }
Figure: Bad example – Checking for an appropriate role in code leads to repetition
[Authorize(Roles = "CanDeleteTags")] public ActionResult Delete(string tagName) { // ...delete tag return View(); }
Figure: Good example – Using the AuthorizeAttribute to check for appropriate roles
Repeated sections of User Interface should be encapsulated in either Html Helpers or Partial Views to avoid repetition.
<div class="featured"> @if (ViewData.ContainsKey("FeaturedProduct")) { <span class="ProductName">@ViewBag.FeaturedProduct.Name</span> <span class="ProductPrice">@ViewBag.FeaturedProduct.Price</span> } </div>
Figure: Bad example – The above code could be encapsulated into a Partial View for reuse
public static class DateExtensions { public static MvcHtmlString GetTodayDate(this System.Web.Mvc.HtmlHelper helper) { return new MvcHtmlString(DateTime.Now.ToShortDateString()); } } @Html.GetTodayDate()
Figure: Good example – Using an HTML Helper extension method for reusable code
@Html.Partial("_FeaturedProduct")
Figure: Good example – Using a Partial View for reusable sections of UI
Hard-coding URLs in your View can cause problems if your routes or page names need to change. Instead, you should always use the Url and Html helpers to refer to different pages in your MVC application.
<a href="/Rule/Create">Create a Rule</a>
Figure: Bad example – Hard-coded URLs may lead to broken links if routes change
@Html.ActionLink("Create a Rule", "Create", "Rule")
Figure: Good example – Use the Url or Html helpers to provide links
ASP.NET provides a great way to compress and package multiple script files or multiple css files. Bundling multiple files together results in fewer requests from the client and smaller payloads which leads to much faster render times.
Rather than link to each script or css file individually, use bundling to group many together and get the advantages of minification and versioning out of the box.
<link rel="stylesheet" href="~/Content/themes/base/jquery.ui.core.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.resizable.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.selectable.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.accordion.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.autocomplete.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.button.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.dialog.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.slider.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.tabs.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.datepicker.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.progressbar.css" /> <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.theme.css" />
Figure: Bad example – each reference will be downloaded separately and won’t be compressed
Configuration: public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new StyleBundle("~/Content/themes/base/css").Include( "~/Content/themes/base/jquery.ui.core.css", "~/Content/themes/base/jquery.ui.resizable.css", "~/Content/themes/base/jquery.ui.selectable.css", "~/Content/themes/base/jquery.ui.accordion.css", "~/Content/themes/base/jquery.ui.autocomplete.css", "~/Content/themes/base/jquery.ui.button.css", "~/Content/themes/base/jquery.ui.dialog.css", "~/Content/themes/base/jquery.ui.slider.css", "~/Content/themes/base/jquery.ui.tabs.css", "~/Content/themes/base/jquery.ui.datepicker.css", "~/Content/themes/base/jquery.ui.progressbar.css", "~/Content/themes/base/jquery.ui.theme.css")); } View: @Styles.Render("~/Content/themes/base/css")
Figure: Good example – Define a bundle and render it in the view for maximum performance
To prevent cross-site request forgery (XSRF), you should use Html.AntiForgeryToken. On the action which takes the post request, place the ValidateAntiForgeryToken attribute to enable the request to validate. Doing this ensures that the post is a direct response to the page that was given to this user so only verified posts will be processed.
@using (Html.BeginForm()) { @Html.ValidationSummary(true) <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <p> <input type="submit" value="Create" /> </p> }
Figure: Bad example – The page is potentially vulnerable to XSRF attacks. Any post will be accepted by the server
View: @using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true) <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <p> <input type="submit" value="Create"/> </p> } Controller: [ValidateAntiForgeryToken] public ActionResult Create(CreateModel model) { // save data }
Figure: Good example – The page is no longer vulnerable to XSRF attacks
Figure: Watch the URL working as a navigation aid
MVC gives us great URLs, but you need to help users navigate via the URL. If the user changes a URL, and the route parameters no longer match, you should correct them with a redirect.
public ActionResult Edit(string employeename, int id) { var model = _repository.GetEmployee(id); // check for a parameter match and redirect if incorrect if (string.IsNullOrEmpty(employeename) || employeename != model.EmployeeName) { return RedirectToAction( "Edit", new { employeename = model.EmployeeName, id }); } return View(model); }
Figure: Good example - The comment says it all Wordpress and Stack Overflow have URL formats that do this very well
Figure: Good example - If the "settimeout-or-setinterval" part of th eURL changes, the page will redirect to the correct location
See more about Thin Controllers, Fat Models, and Dumb Views.
Thin Controllers
You need to think of a controller as more of a coordinator than a controller.It is responsible for calling the business layer and passing from the business layer to the view.
It is also responsible for process flow.
public ActionResult Details(decimal todaysWeather) { var todaysWeatherInFarhenheit = ((9.0 / 5.0) \* todaysWeather) + 32; return View(todaysWeatherInFarhenheit); }
Figure: Business logic is mixed up within the controller making it fatter than it should be public
ActionResult Index() { var todaysWeather = weatherDB.Today.ToList(); return View(todaysWeather); }
Figure: The controller is coordinating between the business layer and the view
public ActionResult Details(int? id) { if (!id.HasValue) return RedirectToAction("Index"); return View(); }
Figure: The controller is co-ordinating process flow (directing traffic)
Dumb Views
Views shouldn't have any flow logic, application logic or business rules.The only logic you should have in the view is in relation to the displaying of data.
The view should never go out and get information from somewhere else.
@{ var theMonth = DateTime.Now.Month; } <p>The numeric value of the current month: @theMonth</p>; @{ var outsidetempinfahrenheit = ((9.0 / 5.0) \* model.outsideTemp) + 32; var weatherMessage = "Hello, it is " + outsidetempinfahrenheit + " degrees."; } <p>Today's weather: @weatherMessage</p>; Figure: Business logic is mixed in with the view @{ var theMonth = DateTime.Now.Month; } <p>The numeric value of the current month: @theMonth</p>; @{ var weatherMessage = "Hello, it is " + model.outsideTemp + " degrees."; } <p>Today's weather: @weatherMessage</p>
Figure: The logic is related to the displaying of data only
Fat Model
So where do we put all the logic? The answer of course is in the model, hence the name fat model.
NuGet allows you to search for, install, upgrade, configure and remove 3rd party libraries from Visual Studio projects in a consistent and easy to use manner.
NuGet makes it easy to manage 3rd party libraries in your projects by keeping track of the library and the files needed to make it work with the concept of a package.
The package contains all the information needed for the 3rd party library to work with your project including any dependencies it may require.
The concept of a package makes it very easy to upgrade and remove the libraries in the future with a single click.
Now all you need to do when you want to remove or upgrade the package is go back to the NuGet package manager.
ASP.NET MVC makes good use of NuGet for managing its dependencies, however these dependencies can easily get out of date.
When you begin a new MVC project, there are often many dependencies that already have updated versions.
You should immediately update your NuGet dependencies in the NuGet Package Manager after starting an MVC project. You should also frequently check for new updates during development so you’re not working with out of date versions.
Figure: Even after starting a brand new project, NuGet shows a lot of required updates!Glimpse allow you to easily perform diagnostics on your MVC application at runtime.As an ASP.NET developer (including ASP.NET MVC), you should use it all the time.
Glimpse lets you find useful information like:
- Routing information
- Profiling
- Request information
- Parameters passed into actions
- Model inspector
The new version of Glimpse now also gives you a Heads Up Display (HUD) showing you important information all the time. While developing, it's a good idea to keep Glimpse open so you can see any issues as soon they come up.
For more information on what the HUD provides, see Damian Brady's blog post.
Glimpse is available on NuGet, so it’s a simple matter to get it up and running on your application. You can find out more from their website.
Securing Glimpse for production use
Glimpse is very powerful but there are some considerations to be addressed before using it on Production.
- 1. Security: Enabling Glimpse can reveal many details about your server – including full database connection details. Glimpse also publishes a full list of all the actions your MVC site can perform so you should thoroughly test the security on all restricted actions before you consider enabling Glimpse.
- 2. Performance: Running Glimpse involves sending debug data with every request. This can impact site performance
Even with these considerations, Glimpse can provide some unique insights into production server performance so it’s worth spending the time to correctly configure Glimpse for production use.
Glimpse on Production Level 1: Developers Only
Install Glimpse on production so that only internal developers can enable it.This is achieved by:
- Limiting access to an ip address range.
<glimpse enabled="true"> <ipAddresses> <add address="127.0.0.1" /> <add addressRange="192.168.1.1/24" /> <add address="::1" /> </ipAddresses> </glimpse>
Figure: Glimpse is now limited to localhost and the 192.168.1.x network
- Using role-based authentication.
If your site has role-based authentication, you can secure Glimpse usage by editing web.config to control access to the Glimpse.axd location.
<location path="glimpse.axd"> <system.web> <authorization> <allow roles="Developers" /> <deny users="\*" /> </authorization> </system.web> </location>
Figure: Glimpse is restricted to the Developers group
Glimpse on Production Level 2: Public by invitation only
If an end-user reports a problem on your website it can be useful to temporarily enable Glimpse for that user. Glimpse also has remote features allowing developers to see the user’s Glimpse data.
- Create a new authentication role such as "PublicGlimpseUsers"
- Edit web.config to control access to Glimpse.axd
<location path="glimpse.axd"> <system.web> <authorization> <allow roles="Developers, PublicGlimpseUsers" /> <deny users="\*" /> </authorization> </system.web> </location>
Figure: Glimpse.axd is now restricted to Developers and PublicGlimpseUsers
- Disable the “config” section of Glimpse so that site connection strings are not published.
<pluginBlacklist> <add plugin="Glimpse.Core.Plugin.Config" /> </pluginBlacklist>
Figure: How to disable the Config tab
Injecting your dependency gives you:
- Loosely coupled classes
- Increased code reusing
- Maintainable code
- Testable methods
- All dependencies are specified in one place
- Class dependencies are clearly visible in the constructor
The classes in each layer can depend on layers toward the center. It emphasizes the use of interfaces for the business logic and repository layers.
The repository layer corresponds to the Data Access Layer in an n-Tier architecture. An n-Tier architecture has at its base the database.
The core of the onion architecture is the Domain Model, and all dependencies are injected. This leads to more maintainable applications since it emphasizes the separation of concerns.
The Onion Architecture is a software design approach that promotes a clear separation of concerns by organizing code into distinct layers. Each layer has a specific purpose, forming concentric circles around the core business logic, much like layers of an onion. This architecture encourages flexibility, testability, and resilience, ensuring that changes in outer layers have minimal impact on the core logic.
Download Onion Architecture PDF
Application Core (the grey stuff)
This should be the big meaty part of the application where the domain logic resides.
Domain Model
In the very centre, we see the Domain Model, which represents the state and behaviour combination that models truth for the organization and is only coupled to itself.
Repository Interfaces
The first layer around the Domain Model is typically where we find interfaces that provide object saving and retrieving behaviour.The object saving behaviour is not in the application core, however, because it typically involves a database. Only the interface is in the application core. The actual implementation is a dependency which is injected.
Business Logic Interfaces
Business logic is also exposed via interfaces to provide decoupling of business logic.Examples of where this is useful include substituting a FacebookNotificationService for an EmailNotificationService or a FedExShippingCalculator for a DHLShippingCalculator
Clients (the red stuff)
The outer layer is reserved for things that change often. E.g. UI and the other applications that consume the Application Core.This includes the MVC project.Any interface dependencies in factories, services, repositories, etc, are injected into the domain by the controller.This means any constructor-injected interfaces in domain classes are resolved automatically by the IoC container.
Dependencies
Dependencies are implementations of interfaces defined in Repository and Business Logic Interfaces and Domain.These classes are specific implementations and can be coupled to a particular method of data access, or specific service technology.e.g. this is where the EF DbContext is implemented, as well as things like logging, email sending, etc.
These dependencies are injected into the application core.
Because the Application core only relies on abstractions of the dependencies, it is easy to update them.
The Onion Architecture relies heavily on the Dependency Inversion principle and other SOLID principles. (Note: Onion Architecture has been replaced by Clean Architecture)
References
- Blog | Peeling Back the Onion Architecture
- Stack Overflow Questions | What type of architecture is this called?
Use SSW Data Onion to Generate your Code
To help make this process pain free, we've developed the SSW Data Onion to get you going and take away the boilerplate code you would normally need to write. Check out this cool video to see how it works:
Further Reading: Do You Use a Dependency Injection Centric Architecture?
You should build a responsive UI using jQuery and a Web API.
Bad Example – Posting the whole form in a submit requires the whole page to be posted to the server Figure: Good Example - Using jQuery to call the Web API provides a great user experience. The whole page does not need to be posted to the serverEfficient programmers do not re-invent the wheel. That's why we use the best Web UI libraries.
Twitter Bootstrap is a NuGet Package that provides a jump-start for HTML based projects. It includes the HTML, CSS and JavaScript framework used by Twitter, to build the Twitter site.
Building your site on top of bootstrap makes it much easier to have your website look great on devices of all sizes, across many different browsers.
Read our Rules to Better UI (Bootstrap).
Documentation
Tailwind
Alternatively, TailwindCSS is also acceptable. The difference between the Tailwind and Bootstrap is a matter of how comfortable you are with CSS.
Out of the box, Tailwind is lightweight and will get the job done simply; you can build a website without ever having to look at CSS.
Bootstrap requires theme customization, but it’s more robust and solid once done. Read more about these differences.
Did you know you can improve the speed of your MVC app by using a built in feature called bundling and minification.
Bundling allows you to:
- Specify the JavaScript files you want to include in your app and the order in which they are loaded
- Put them into one JavaScript file reducing calls to the server.
The next part of the process is minification. This means that all the whitespace is removed from the JavaScript files and long variables names are shortened where possible to decrease the size of the package.All this adds up to a faster MVC app and a better user experience.
Layout.cshtml
<script type="text/javascript" src="/SoftwareDevelopment/RulesToBetterMVC/Pages/@Url.Content("></script><script type="text/javascript" src="/SoftwareDevelopment/RulesToBetterMVC/Pages/@Url.Content("></script><script type="text/javascript" src="/SoftwareDevelopment/RulesToBetterMVC/Pages/@Url.Content("></script><script type="text/javascript" src="/SoftwareDevelopment/RulesToBetterMVC/Pages/@Url.Content("></script><script type="text/javascript" src="/SoftwareDevelopment/RulesToBetterMVC/Pages/@Url.Content("></script><script type="text/javascript" src="/SoftwareDevelopment/RulesToBetterMVC/Pages/@Url.Content("></script>Figure: Scripts are specified in the viewBundleConfig.cs
public static void RegisterBundles(BundleCollection bundles){bundles.Add(new ScriptBundle("~/bundles/SSW").Include("~/Scripts/2011.3.1115/jquery-1.6.4.min.js","~/Scripts/jquery-ui-1.8.16.min.js","~/Scripts/jquery.formatCurrency-1.4.0.min.js","~/Scripts/date.js","~/Scripts/jquery.watermark.min.js","~/Scripts/jquery.cross-slide.min.js"));}
Layout.cshtml
@Scripts.Render("~/bundles/ssw") Figure: A bundle is created in the bundle config and then referenced in the view
Before starting a software project and evaluating a new technology, it is important to know what the best practices are. The easiest way to get up and running is by looking at a sample application. Below is a list of sample applications that we’ve curated and given our seal of approval.
Northwind Schema
SQL Server
SQL Server and Azure SQL Database
.NET Core
- SSW Clean Architecture Solution Template An example REST API build with .NET 7 following the principles of Clean Architecture.
- SSW Northwind Traders A reference application built using Clean Architecture, Angular 8, EF Core 7, ASP.NET Core 7, Duende Identity Server 6.
- eShopOnWeb Sample ASP.NET Core 6.0 reference application, powered by Microsoft, demonstrating a layered application architecture with monolithic deployment model. Download the eBook PDF from docs folder.
- eShopOnContainers Cross-platform .NET sample microservices and container based application that runs on Linux Windows and macOS. Powered by .NET 7, Docker Containers and Azure Kubernetes Services. Supports Visual Studio, VS for Mac and CLI based environments with Docker CLI, dotnet CLI, VS Code or any other code editor.
- ContosoUniversity This application takes the traditional Contoso University sample applications (of which there have been many), and try to adapt it to how our "normal" ASP.NET applications are built.
Blazor
- Awesome Blazor Browser A Blazor example app that links to many other useful Blazor examples
- Blazor Workshop A Blazor workshop showing how to build fast food website
UI - Angular
- Tour of Heroes Default Angular sample app as part of the documentation
- ngrx Example App Example application utilizing @ngrx libraries, showcasing common patterns and best practices
UI - React
- Intro to React (Tic-tac-toe)
Introductory React tutorial that builds a simple Tic-tac-toe game - Intro to Redux (Counter example)
Introductory React Redux tutorial that builds a simple counter app
Making reports on websites printable can be difficult. While there are CSS media and rules to help make pages printable, there are always issues with page breaks, browser quirks and tables.
The best and most accurate print solution is to use SQL Server Reporting Services (SSRS). You can use SQL Server Reporting Services in MVC even though its only supported by WebForms.
It's great to include SQL Server Reporting Services (SSRS) reports in your web application, which can be done with the Microsoft ReportViewer web control...however this only applies to ASP.NET WebForms.
With an iframe and a little bit of code, your reports can also be viewed in your ASP.NET MVC application.
In your MVC project, add a new item of type WebForm.
Then add the ReportViewer control to the WebForm.
In the View you want to display the report in, add an iframe pointing to your WebForm.
Tie them together, by getting your report parameters from the MVC page and appending them to the query string of the iframe URL.
(The below example uses JavaScript to execute this part from user input)
Now you have your SSRS report in your MVC application.
When using Web-API the method above is difficult and time-consuming!
The easy solution is to render the report within the API and return it to the user as a pdf. For an example of how to implement the functionality, read the following series of articles on 'Integrating SSRS Web-API and AngularJS'.
Protecting your site from automated attack is easy with reCAPTCHA.
Learn how to by clicking this link and improve your site security.
Figure: Good Example - reCAPTCHA is protecting a site from automated attack
Figure: Bad Example - Older v1 reCAPTCHA.
Figure: Bad Example - No protection, a robot can register tens or thousands of users...
NuGet packages can quickly get out of date and you may miss some important updates and/or features. Therefore, it is important to keep them up-to-date by updating on a regular basis. This can be done via the Package Manager UI or via the Package Manager Console.
**WARNING**
Some package updates may require extra care, such as packages containing content files or updated client script libraries. For example, the jQuery NuGet package update may break the UI of your web application due to some breaking changes introduced in a later version of the library (e.g. upgrading from v 1.10 to 2.0).
The impact of such upgrades can be greatly minimized by introducing Selenium or Coded UI tests into your solution. Running Selenium or Coded UI tests after performing a NuGet package update, can help to quickly identify problematic areas in your UI, which may be affected by the update.
Validation is an important part of any data-driven web application. Client-Side validation provides fast user feedback and a better UI experience but cannot be relied on for data integrity - so client-side validation should always be backed by additional server-side validation.
With MVC Unobtrusive Validation, you can configure both client-side and server-side in one place.
Validation rules can be added to a model object via Data Annotations or using the Fluent Validation API.
Fluent Validation is available as a Nuget package. See Do you use Fluent Validation?
If you create a new MVC web application in VisualStudio 2013, unobtrusive validation will be enabled by default. Otherwise, it's simple to install from Nuget. To use it simply:
- Bind your razor views to model objects
- Use Html Helpers to render the form UI