Showing posts with label MVC. Show all posts
Showing posts with label MVC. Show all posts

Tuesday, May 13, 2014

MVC Exception Handling

This is a followup post to my original post about simple .NET exception handling.

The ASP.NET MVC Framework contains an attribute named HandleErrorAttribute.  You can decorate a controller or an action simply by typing <HandleError> above the controller or action definition.  If an unhandled exception occurs anywhere in the scope decorated with HandleErrorAttribute, MVC will display a view named "Error" to the user automatically.  Typically, this view is generated for you when you create a new MVC application and is placed in the Shared folder.  The HandleErrorAttribute will only work if you have customErrors turned on in your web.config.  HandleErrorAttribute works great but it doesn't log.  In order to add this functionality to the attribute you need to create a new class that inherits from the HandleErrorAttribute like this:

    Public Class HandleErrorAndLogAttribute
        Inherits HandleErrorAttribute

        Public Overrides Sub OnException(filterContext As ExceptionContext)

            If Not filterContext.ExceptionHandled Then

                Dim MyException As Exception = filterContext.Exception
                
                ...

                'Logging code goes here

                ...
 
                MyBase.OnException(filterContext)

            End If

            'filterContext.Result = New ViewResult With {.ViewName = "Error"}

        End Sub

    End Class

Now, if you want to use your new attribute, you would just type <HandleErrorAndLog>.  You can globally apply this attribute to everything in your application by editing the FilterConfig.vb file like this:

Public Module FilterConfig
    Public Sub RegisterGlobalFilters(ByVal filters As GlobalFilterCollection)
        'filters.Add(New HandleErrorAttribute())
        filters.Add(New HandleErrorAndLogAttribute())
    End Sub
End Module

Apparently, there are certain exceptions that this attribute does not handle such as HTTP exceptions that occur outside of the MVC context.  To ensure these errors are handled, you should also log errors in the MvcApplication_Error event of the Global.asax.vb file.  Be sure to set the defaultRedirect attribute of the customErrors node in the web.config file to the Error View.  You will need to create an action method that will display the Error view when it is redirected in this way..

Tuesday, March 25, 2014

Add a Bootstrap jQuery widget to an MVC view.

Bootstrap is an awesome open source UI framework that uses both CSS and jQuery to provide a great user experience.  Besides straight up CSS, they offer some "widgets" that offer some functionality that jQuery UI does not provide.  The process for getting a widget on your view is almost exactly the same as using jQuery UI.

You have to render both the jQuery and Bootstrap bundles in your layout view.  If you don't have a Bootstrap bundle, you just need Bootstrap.js.  Also ensure Bootstrap.css is in your CSS bundle.

This markup will render a button that will produce the popover widget when clicked.

<button type="button" id="Example" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="top" data-content="Phone:  555-5555">
   Popover on top
</button>

Then, add a jQuery file with the following:

$(document).ready(function () {
    $("#Example").popover();
});

Thursday, March 20, 2014

ASP.NET Identity - Using Roles

Roles allow you to restrict parts of your application to specific roles.  These roles are stored in the AspNetRoles table that is created by ASP.NET Identity.  You can create roles programatically or just insert them directly into the table using SQL.  If you're using SQL, you can just use an INSERT statement with a GUID and a name.  Programmatically, you would do the following:

Dim MyRoleStore As New RoleStore(Of IdentityRole)
Dim MyRoleManager As New RoleManager(Of IdentityRole)(MyRoleStore)

MyRoleManager.Create(New IdentityRole("Support Staff"))

IdentityRole is the Entity Framework object that ultimately represents (mapped to) a row in the AspNetRoles table.

Once you have some roles defined, you can associate a role to a user like this:

Dim Result As IdentityResult = _
     Await UserManager.AddToRoleAsync(MyUser.Id, "Support Staff")

Finally in order to restrict parts of your application, you simple add an Authorize filter to the ActionMethod you want restricted like this:

<Authorize(Roles:="Support Staff")>
Function MyActionMethod() As ActionResult

    ...

    Return View()

End Function

Finally, if the user is not authorized, they will be redirected to a specific page, usually the login page.  ASP.NET Identity is not forms authentication so this redirect is not in the web.config.  In an MVC app, it is in the Startup.Auth class ConfigureAuth method:

app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
.AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
.LoginPath = New PathString("/Account/Login")})

Tuesday, March 18, 2014

ASP.NET Identity Basics

ASP.NET Identity is a system for handling authentication and authorization.  It was released with .NET 4.5.1 that shipped with Visual Studio 2013.  It replaces ASP.NET Membership.

Like Membership, it is a (sort of) simple way to implement forms authentication in a .NET application.  Identity differs from Membership in that it can be unit tested, allows external providers (such as Facebook and Google), and it works for all .NET technologies.

It is surprisingly easy to configure a new MVC web application for forms authentication using Identity.  Simply add an ASP.NET Web Application project and choose MVC with Individual User Accounts.  Remember to set the Framework version of the new project to 4.5.1 or you will not have these options.

The resulting project will contain:
  1. An AccountController class that will handle all authentication and user profile functionality.
  2. A web.config with a connection string pointing at a yet-to-be-created LocalDB database.
  3. A set of Account views that handles logging in, registrations, and user profile management.
  4. An AccountViewModels file that contain view models the Account views use.
  5. An IdentityModel file that contain classes that derive from the built in Identity classes.
  6. A Startup.Auth file that contains code to configure authentication on start up.
All of this is nicely integrated with the default views and controllers that the ASP.NET template has included for a standard MVC application.  You can run it and start registering and logging in immediately with no additional configuration.

It is important to understand the users and passwords are stored in the LocalDB database referred to in the web.config file.  This database is created by ASP.NET when you register the first user.  When you want to go to production, you can just script this thing out and move it to a production SQL Server.

If you look at the AccountController code, you'll notice that a Microsoft.AspNet.Identity.UserManager class is called for much of the authentication functionality.  The UserManager class almost exclusively offers asynchronous methods.  Therefore, you have to follow the rules of calling asynchronous methods when using this class.

Thursday, November 7, 2013

Be aware of binding peculiarities when posting a form more than once using MVC

Occasionally, you will find yourself in a situation where you might need to post more than once.  For example, when the form doesn't pass validation, the user is returned to the form, and must post again once they have made corrections.

You may want to change some fields on the page during this first post, so that when the user returns, he sees something different.  Or, you may want to set a hidden field to indicate something.  You cannot do this if the model field is bound to an element on the view.  It is important to understand that the model is bound to the element and only element.  If you are going to return to the view, you can't break this binding even if you set the field to something else in the action method.

If you absolutely need to do this, you can use JavaScript/jQuery connected to the submit button to set the field on the page.

Monday, October 28, 2013

Ignore the route when adding a web service to an MVC project

You may want to add a web service to an MVC project in order to to implement AJAX call backs.  Remember that MVC is always trying to interpret URLs using it's routing configuration.  So, if the routing configuration encounters a URL referring to your web service, the framework won't know what to do with it.

You could just put it into another web project.  This works fine with IE 10, but Chrome will blow up.  I think it has something to do with cross domain configuration (and lack thereof).  Do yourself a favor, and stick it in the MVC project, and remember to add this to your RegisterRoutes method:

routes.IgnoreRoute("YourAjaxEnabledService.svc/DoWork")

Thursday, October 24, 2013

Use a custom editor template when using a partial view to edit data.

Let's say your view is being used to edit some data.  Part of the data being edited appears elsewhere in the app and you might want to reuse it.  For example, an address.  There may be multiple places in your application where you want to have the ability to edit an address.  You might be tempted to just make a partial view and then render it within the parent view by doing something like this:

@Code
    Html.RenderPartial("Address", Model.MyAddressViewModel)
End Code

The problem is that this doesn't bind back to the parent model when the parent form is submitted. Use the Editor helper instead:

@Html.EditorFor(function(m) m.MyAddressViewModel)

Now this will bind. However, the resulting html looks like crap. You can either decorate the crap out of your view model, or just make a custom editor template. All you have to do is make a new folder called EditorTemplates in the Shared folder of your MVC project.  Then put the partial view in this folder.  Make sure you name the view the same as the data type of the object your are creating the editor for.  For example, AddressViewModel.vbhtml.

Friday, October 18, 2013

MVC Buttons, Links, and Actions

With any web page, you need to execute code on the server in response to user actions.  With traditional web forms, this was accomplished with event handlers generally tied to buttons, and links.  With MVC, we have this concept of an "action method".  So, the question becomes, how do we kick off action methods?  There are 3 big ways to do this:
  1. The user enters a url and the routing system kicks off the action method associated with the route.  The parameters of the action method are in the route itself, or exist as query parameters.  Technically this is a GET request.
  2. The user clicks a link defined by the Html.ActionLink method.  ActionLink will generate a url that contains the route necessary to execute the specified action method on the specified controller with the provided parameters.  Technically this is a GET request.
  3. Within a form element, the user clicks on a button with the "type" attribute set to "submit".  The action method associated with the form is executed.  Parameters are provided through model binding.  Technically this is a POST request.
One and Two are really doing the same thing.  So, we are either doing a GET with a link, or a POST with a button.  When do we use a GET link, and when do we use a POST button?

Standard practice is to use a GET link if we are not changing anything and POST button when are changing something.

What is model binding?  First of all model binding only occurs during a POST.  It's not going to happen if you are kicking off an action method through a link (such as creating one with Html.ActionLink).  Any parameters for the action method associated with the form will be automatically provided based on matching up the names of the ids on the page with the names of the parameters.  So if you have a hidden field with an id of "Price", and you have a parameter of your action method named "Price", this will automatically be sent to the action method when you POST.

Some people are fond of everything being a button, even stuff that shouldn't be a POST.  For example, a "Cancel" button.  One option is...

Create a button and make it behave like a link. Setting the type attribute to "button" will prevent the button from doing a POST

<button id="cancelButton" type="button">Cancel</button>

Then in your jQuery file,

    $("#cancelButton").click(function () {
        window.location = '../ActionName/' + $("#inputParameterId").val()
    });

I don't particularly care for this solution.  Feels like a hack to me.

Some might want everything to be a link, even though a "Save" button should be a POST.  We could...

Create a link and make it behave like a submit button.

<a id="LinkSave" href="#">Link Save</a>

Then, in your jQuery file,

    $("#LinkSave").click(function () {
        $("#myForm").submit();
    });

AND, to get this to work, your form has to have an ID, so you have to do some extra stuff with your Html.BeginForm helper...

@Using Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, New With {.id = "myForm"})

Besides being ugly and a hack, this only works in IE. So I don't like that either.

Your best bet is to leave POSTing to buttons and GETting to links.  If you apply css in a smart way, you can style them to look the same.  For example:

                        <div class="actionButtons">
                            <input name="Save" type="submit" value="Save" />
                            @Html.ActionLink("Cancel", "ApplicationSummary", New With {.id = Model.Nutshell.ApplicationId})
                        </div>

The CSS looks like this:
.actionButtons A, .actionButtons INPUT {
    font-size: 10pt; 
    color: White; 
    margin: .5em; 
    font-weight: normal;
    text-decoration: none; 
    padding: .15em 1.5em .2em 1.5em;
    background-color: #353535; 
    border: 1px solid black;
}

So, any anchor or input tags inside a container with the "actionButtons" class will have this style.

One final note.  What if you need more than one POST button on the same page?  You could put two submit buttons within the same form and differentiate between the two in the action method using something like:

            If Request("Save") IsNot Nothing Then
                'Save Logic Here
            ElseIf Request("Verify") IsNot Nothing Then
                'Verify Logic Here
            End If

I don't like that because it's hackish and messy. Just put two or more forms on the page, and have each form target a different action method for each submit button.

UPDATE:  OK, so having two forms isn't always practical.  What if you need to submit the same data, but in two different ways depending on what button is clicked?  Go ahead, and put to submit buttons on your form.  Make sure the name attribute is identical. Let's name them both "TheViewButton".  The value attribute will obviously be different.  Your action method will look like this:

<HttpPost>
Function TheViewName(viewModel As TheViewModel, TheViewButton As String) As ActionResult
   If TheViewButton = "Save" Then
      'Save Logic
   Else
      'Other Logic
   End If
End Function

I like this solution a bit better.

Wednesday, September 25, 2013

Using a jQuery UI widget in on an MVC view

First, you need to add references to the appropriate jQuery libraries. The recommended way to do this is to use NuGet. Right click your project, and click on Manage NuGet Packages. Search online and install jQuery and jQuery UI. This will add the .js files as well as .css files that are used by the widgets. Bundles will be created in your BundleConfig file and these bundles will be rendered in your default layout file. Essentially, rendering a bundle is equivalent to using a <script> tag to reference the .js files. If you use another layout file, you must copy these statements over.

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryui")

This example will add the functionality of the datepicker widget to a text box. First create a textbox and make sure it has the id attribute:

@Html.TextBoxFor(Function(x) x.ProposedStartDate, New With {.id = "ProposedStartDate"})

I recommend a separate jQuery file for each page that you add jQuery functionality. Create the file, and include a link to this file in your view:

<script type="text/javascript" src="~/Scripts/ViewjQuery/SampleView.js"></script>

In your jQuery files, associate your textbox with the widget by using the following code:

$(document).ready(function () {

    $("#ProposedStartDate").datepicker();

});

Finally, you're not going to like the look of the datepicker, so go to Theme Roller and download a jQuery theme .css file or two. Then reference this css file in the head tag of your layout file thusly:

<link href="~/Content/themes/lightness/jquery-ui-1.10.3.custom.css" rel="stylesheet" type="text/css" />

Finally, you will be satisfied with the look of your datepicker, but you may want to size it down a bit. This has to be done through the font-size css style of a particular class. I don't recommend changing the theme .css that you downloaded. Rather, I would just create a new .css file for these types of tweaks and load it after you load the theme roller css. Put this in there:

.ui-datepicker { font-size:9pt !important}