ASP.NET MVC: Internal Server Error (500) on Action Method Returning Json Result


The ASP.NET MVC framework allows you to easily return Json from an action method. This makes jQuery Ajax calls very easy to implement, as shown below.

The JavaScript

$(.button).getJSON('/Home/GetJsonData', 
                   { id = 34 },
                   function(data) {
                      // Do something with it
                   }
);

The Action Method

public ActionResult GetJsonData(int id)
{
     Person person = this.personService.GetPerson(id);     
     return Json(person);
}

There is only one problem with the above action method. If you attempt to run it, the call to the ActionMethod will result in a Internal Server Error (Error 500). The reason is that by default data can only be retrieved using a POST operation if your action method returns a Json result. To make this work with a GET request, all you need to use is the overloaded Json() method shown below.

public ActionResult GetJsonData(int id)
{
     Person person = this.personService.GetPerson(id);     
     return Json(person, JsonRequestBehavior.AllowGet);
}

ASP.NET MVC: Ajax Dialog Form Using jQuery UI


In one of my recent projects I needed to display some information, allow the user to edit it utilizing a dialog window, post the updated information and reload it for the user using Ajax. I needed to perform the named operations for multiple models so I set out to create some generic code that could be reused over and over. Below is a picture of what I am trying to accomplish.

The project used for the post below can be downloaded here.

First, the model. For this example, we will use a simple model that contains some profile information for a user.

public class Profile
{
    [Required]
    public string Name { get; set; }

    [Required]
    [StringLength(10, MinimumLength=3)]
    [Display(Name="Nick name")]
    public string NickName { get; set; }

    [Required]        
    public string Email { get; set; }

    [Required]
    public int Age { get; set; }
}

Second, we need to create three action methods. One will be used to display the profile information, another to display the form for editing the profile, and lastly another that will be used to save the edited profile object. The first two should always return a PartialView as each of these action methods will be called using Ajax and their result will be loaded into div elements; the first into a div used to display the saved profile and the second into the edit dialog. The third action method will return a PartialView if the ModelState is invalid so that the errors can be displayed to the user and a Json result indicating the save was successful if all went well. (Note that in this example I am just storing the profile information in Session but obviously this would be stored in a database or some other data store.)

public ActionResult Profile()
{
    Profile profile = new Profile();

    // Retrieve the perviously saved Profile
    if (Session["Profile"] != null)
        profile = Session["Profile"] as Profile;

    return PartialView(profile);
}

public ActionResult EditProfile()
{
    Profile profile = new Profile();

    // Retrieve the perviously saved Profile
    if (Session["Profile"] != null)
        profile = Session["Profile"] as Profile;

    return PartialView(profile);
}

[HttpPost]
public ActionResult EditProfile(Profile profile)
{
    // If the ModelState is invalid then return
    // a PartialView passing in the Profile object
    // with the ModelState errors
    if (!ModelState.IsValid)
        return PartialView("EditProfile", profile);

    // Store the Profile object and return
    // a Json result indicating the Profile 
    // has been saved
    Session["Profile"] = profile;
    return Json(new { success = true });            
}

Next we need to create the two partial views that correspond to the first two action methods we created above. In this example, the partial view for the EditProfile action is pretty much just the stock view created by the MVC frameowork except I have removed the input element to submit the form as we will use the buttons on the jQuery UI dialog to submit it.

The second partial view, the one that displays the saved Profile object again in this example is the stock view with a few added elements.

@using DialogFormExample.MvcHelpers

@model DialogFormExample.Models.Profile

<fieldset>
    <legend>Contact Info</legend>
    
    <div class="display-field">
        Name: @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-field">
        Nick name: @Html.DisplayFor(model => model.NickName)
    </div>
    
    <div class="display-field">
        Email: @Html.DisplayFor(model => model.Email)
    </div>
    
    <div class="display-field">
        Age: @Html.DisplayFor(model => model.Age)
    </div>

    <div class="right">
        @Html.DialogFormLink("Edit", Url.Action("EditProfile"), "Edit Profile", "ProfileContainer", Url.Action("Profile"))
    </div>
</fieldset>

The first portion of the partial view just displays the form elements required for editing the model. This is just the stock Edit view modified only slightly. The new code starts at line 25. Here I created an extension method for HtmlHelper named DialogFormLink that will create an anchor tag loaded with all the needed information to make the dialog form work. Here is the code for the extension method. You can read the comments to get an understanding of the parameters it requires.

/// <summary>
/// Creates a link that will open a jQuery UI dialog form.
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="linkText">The inner text of the anchor element</param>
/// <param name="dialogContentUrl">The url that will return the content to be loaded into the dialog window</param>
/// <param name="dialogTitle">The title to be displayed in the dialog window</param>
/// <param name="updateTargetId">The id of the div that should be updated after the form submission</param>
/// <param name="updateUrl">The url that will return the content to be loaded into the traget div</param>
/// <returns></returns>
public static MvcHtmlString DialogFormLink(this HtmlHelper htmlHelper, string linkText, string dialogContentUrl,
    string dialogId, string dialogTitle, string updateTargetId, string updateUrl)
{
    TagBuilder builder = new TagBuilder("a");
    builder.SetInnerText(linkText);
    builder.Attributes.Add("href", dialogContentUrl);            
    builder.Attributes.Add("data-dialog-title", dialogTitle);
    builder.Attributes.Add("data-update-target-id", updateTargetId);
    builder.Attributes.Add("data-update-url", updateUrl);

    // Add a css class named dialogLink that will be
    // used to identify the anchor tag and to wire up
    // the jQuery functions
    builder.AddCssClass("dialogLink");

    return new MvcHtmlString(builder.ToString());
}   

The above extension method that builds our anchor tag utilizes the HTML5 data attributes to store information such as the title of the dialog window, the url that will return the content of the dialog window, the id of the div to update after the form is submitted, and the url that will update the target div.

Now we need to add the container div to hold the Profile information in the Index view.

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>

<div id="ProfileContainer">
    @{ Html.RenderAction("Profile"); }
</div>

Lastly, I have listed the scripts and css files that need to be linked below in the _Layout page for everything to work correctly.

<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>   
    <script src="@Url.Content("~/Scripts/DialogForm.js")" type="text/javascript"></script>    
</head>

The last script reference above is to a script I wrote called DialogForm.js. This script (shown below) will make everything work.


$(function () {

    // Don't allow browser caching of forms
    $.ajaxSetup({ cache: false });

    // Wire up the click event of any current or future dialog links
    $('.dialogLink').live('click', function () {
        var element = $(this);

        // Retrieve values from the HTML5 data attributes of the link        
        var dialogTitle = element.attr('data-dialog-title');
        var updateTargetId = '#' + element.attr('data-update-target-id');
        var updateUrl = element.attr('data-update-url');

        // Generate a unique id for the dialog div
        var dialogId = 'uniqueName-' + Math.floor(Math.random() * 1000)
        var dialogDiv = "<div id='" + dialogId + "'></div>";

        // Load the form into the dialog div
        $(dialogDiv).load(this.href, function () {
            $(this).dialog({
                modal: true,
                resizable: false,
                title: dialogTitle,
                buttons: {
                    "Save": function () {
                        // Manually submit the form                        
                        var form = $('form', this);
                        $(form).submit();
                    },
                    "Cancel": function () { $(this).dialog('close'); }
                }
            });

            // Enable client side validation
            $.validator.unobtrusive.parse(this);

            // Setup the ajax submit logic
            wireUpForm(this, updateTargetId, updateUrl);
        });
        return false;
    });
});

function wireUpForm(dialog, updateTargetId, updateUrl) {
    $('form', dialog).submit(function () {

        // Do not submit if the form
        // does not pass client side validation
        if (!$(this).valid())
            return false;

        // Client side validation passed, submit the form
        // using the jQuery.ajax form
        $.ajax({
            url: this.action,
            type: this.method,
            data: $(this).serialize(),
            success: function (result) {
                // Check whether the post was successful
                if (result.success) {                    
                    // Close the dialog 
                    $(dialog).dialog('close');

                    // Reload the updated data in the target div
                    $(updateTargetId).load(updateUrl);
                } else {
                    // Reload the dialog to show model errors                    
                    $(dialog).html(result);

                    // Enable client side validation
                    $.validator.unobtrusive.parse(dialog);

                    // Setup the ajax submit logic
                    wireUpForm(dialog, updateTargetId, updateUrl);
                }
            }
        });
        return false;
    });
}

Conclusion

I know that was a lot to take in but after you do the setup once, all you need to do is make one call to Html.DialogFormLink and everything will be taken care of for you. Hope this helps!

ASP.NET AJAX ModalPopup to Confirm Delete


As mentioned in my last post I have been working with the ASP.NET AJAX Toolkit lately and more specifically the ModalPopupExtender control. On one of the pages I have a DataGridView with a delete ImageButton and I wanted to use the ModalPopup control to confirm that the user really wanted to delete the row from the DataGridView. This question has been asked many times on many different forums but the most common response seems to be, “That is completely overkill. Just use the confirm() javascript method”. While this may be a true statement, the confirm() method has some drawbacks one of which is that you can’t customize the window to the look and feel of your website.

Confirming an action is a very common scenario, one that will occur on many pages and even multiple times on one page. Such a situation obviously calls for a custom control and that is exactly what I did. After creating the custom control, which will be explained shortly, adding a confirmation box is accomplished by inserting the following markup into your page:

<enso:ConfirmBox runat="server" 
     Title="Confirm Delete" 
     Message="Are you sure you want to delete this product key?" 
     TargetControlId="btnDeleteProductKey"  />

This one simple line will create a confirmation box that can be completely customized to fit the look and feel of your site as shown below.

Now for the explanation.

To create a new user control:

  1. In the Solution Explorer of Visual Studio right click the project name, select Add, and click New Item…
  2. Select Web User Control
  3. Enter ConfirmBox.ascx in the Name textbox
  4. Copy and paste the following code into the ConfirmBox.ascx window.
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ConfirmBox.ascx.cs" Inherits="TestWebApp.ConfirmBox" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<script runat="server">
    public string Title { set { this.lblTitle.Text = value; } }
    public string Message { set { this.lblMessage.Text = value; } }
    public string TargetControlId { set { this.popupConfirmBox.TargetControlID = this.btnConfirm.TargetControlID = value; } }
    public int Width { set { this.panelConfirmBox.Width = Unit.Pixel(value); } }

    protected void Page_Load(object sender, EventArgs e)
    {
        this.btnPopupClose.OnClientClick = "$find('" + popupConfirmBox.ClientID + "').hide();";
    }
</script>
  
<asp:Panel ID="panelConfirmBox" runat="server" CssClass="modalPopup" Width="400px" Style="display:none;">
    <h5><asp:Label ID="lblTitle" runat="server" Text="[Title]"></asp:Label></h5>
    <div class="closeButton">
        <asp:LinkButton ID="btnPopupClose" runat="server" >x</asp:LinkButton>
    </div>    
    <div class="clearBoth"></div>
    
    <br />  
    <div class="modalMessage">
        <asp:Label ID="lblMessage" runat="server" Text="[Message]"></asp:Label>
    </div>
    <br />
    
    <div class="modalButtons">
        <asp:Button ID="btnYes" runat="server" Text="Yes" /> &nbsp; <asp:Button ID="btnNo" runat="server" Text="No" />
    </div>
</asp:Panel>                    
<asp:ModalPopupExtender Id="popupConfirmBox" runat="server" PopupControlID="panelConfirmBox" BackgroundCssClass="modalBackground" CancelControlID="btnNo" OkControlId="btnYes" ></asp:ModalPopupExtender>
<asp:ConfirmButtonExtender ID="btnConfirm" runat="server" DisplayModalPopupID="popupConfirmBox"> </asp:ConfirmButtonExtender>

You will need to change the Inherits property in the first line of the file to match the name of your application. Additionally, you will need to make sure you already have a reference to the AjaxControlToolkit.dll in your project.

Property Definitions:

  • Title – Sets the title of the confirmation box
  • Message – Sets the message of the confirmation box
  • TargetControlId – Sets the id of the control that should display the confirmation box when clicked
  • Width – Sets the width confirmation box

The only non-appearance property is the TargetControlId which as in all ASP.NET AJAX Toolkit controls is the id of the control that is to be extended. In our case, setting this property will set the TargetControlId property of both the ModalPopupExtender and the ConfirmButtonExtender. For these to work in conjunction, they must both have the same value for the TargetControlId property.

If you read my previous post, ASP.NET AJAX Toolkit ModalPopup Tips, I mentioned a few tricks I learned while using the ModalPopupExtender; one of which was how to add multiple cancel buttons. This can be accomplished with a bit of client-side javascript placed in the OnClientClick event of a LinkButton. In the Page_Load event of the control we are adding this javascript to the ‘x’ in the top right corner of the window. Note that we are accessing the ClientID property of the ModualPopupExtender as when the page is rendered, the control will no longer have an Id of ‘popupConfirmationBox’ but rather something like ”ctl00_contentMain_dataGridViewProductKeys_ctl02_ctl01_popupConfirmBox’.

There is nothing fancy to the configuration of the ModalPopupExtender. We simply set the panel that will appear when the TargetControlId is clicked and the cancel and ok control ids.

Next we introduce another ASP.NET AJAX Toolkit control called the ConfirmButtonExtender. As the name indicates, it is used to confirm an action taken by the user. The plain vanilla version of this control simply uses the javascript function confirm() as discussed earlier in this post. But, the developers were nice enough to allow us to specify a ModalPopup control to be displayed instead. We simply set the DisplayModalPopupID property to the Id of our ModalPopupExtender to let the control know to display the ModalPopup instead.

Before we can start using this control on any page we desire, we need to make a simple modification to the web.config to ensure the control is visible throughout the site. Open your web.config and find the pages section which is a child of the system.web node. Add something similar to the last line that appears in the controls section below. Note that the tagPrefix and tagName can be anything you desire but just make them something you can remember as they will be used to identify your control.

<system.web>
    <pages>
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="enso" src="~/ConfirmBox.ascx" tagName="ConfirmBox"  />
      </controls>
    </pages>
</system.web>

Save the web.config file and build your project to make the control visible while using intellisense. Now navigate to the page on which you want to place the ConfirmBox control. In my case I wanted to add it to a DataGridView delete button. To do this I added a TemplateField to the DataGridView using the following markup.

<asp:TemplateField ShowHeader="false">
     <ItemTemplate>
          <asp:ImageButton ID="btnDeleteProductKey" runat="server" CommandName="Delete" ImageUrl="~/Styles/images/X.png" />
          <enso:ConfirmBox runat="server" Title="Confirm Delete" Message="Are you sure you want to delete this product key?" TargetControlId="btnDeleteProductKey"  />
     </ItemTemplate>
</asp:TemplateField>

Lastly you will need to make sure you have a ScriptManager on the page somewhere and a link to the stylesheet used to format your confirmation box. Click here for the stylesheet and here for the image used in my implementation.

Simply add the markup for the new control you created, specify the Title, Message, and TargetControlId and you are done! You have now implemented a confirmation box that you can placee anywhere on any page without having to duplicate code over and over!

ASP.NET AJAX Toolkit ModalPopup Tips


Today I was working with the ModalPopup control provided by Microsoft in the ASP.NET AJAX Toolkit. This control allows you to create a popup box that removes the users ability to interact with the underlying page until the box is closed. Below is a screenshot from my implementation which was used in a customer managment web application used to create product keys.

Joe Stagner provides a great introductory video tutorial on this control here which got me off to a great start but I soon ran into my first problem.

If you setup the control exactly as described in the video and run your application in IE8, the background opacity is completely ignored and the entire window changes to an opaque gray. Unfortunately, IE8 does not support the opacity css property even though all other browsers do. After many complaints, Microsoft provided a work around that allows you to set the opacity of the page using a proprietary property named ‘-ms-filter’. In order to get the correct opacity in all browsers (IE8, 7, 6, Chrome, FireFox, etc), your CSS class must be in the following order:

.modalBackground
{        
    background-color: Gray;    
    -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";   /* IE 8 Compatibility */
    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);       /* IE 7 Compatibility */
    opacity: 0.5;    /* Everyone else */
}

My second issue was that when you click the OK button in the ModalPopup the control is designed to execute a javascript function but I needed to execute a server-side function in the code-behind file. To fix this, all you need to do is simply remove the OKControlID property from the ModalPopupExtender control. After doing this, the code-behind click event will be executed when the user clicks the OK button.

<asp:ModalPopupExtender 
     BehaviorID="popupAddProductKeys" 
     runat="server" 
     TargetControlID="btnAddProductKeys" 
     PopupControlID="panelAddProductKeys" 
     BackgroundCssClass="modalBackground"
     CancelControlID="btnProductKeysCancel">

Unfortunately, fixing this second issue caused another. In all non-IE browsers, clicking the OK button would not close the popup window. To fix this, two things need be done. First, set the BehaviorID property in the ModalPopupExtender control. Second, in the Button control, include the following javascript in the OnClientClick event.

<asp:Button 
     ID="btnProductKeysOk" 
     runat="server" 
     Text="OK" 
     OnClientClick="$find('popupAddProductKeys').hide()" 
     onclick="btnProductKeysOk_Click" />

The included javascript will find the ModalPopup control and then call its hide function and thus hide it from view. This trick can be used to add multiple close buttons on the popup. For instance, I added a LinkButton with the text of ‘x’ in the to right corner to allow the user another way to close the popup. To close the popup when clicked, simply place the same javascript as shown above in the OnClientClick event.

<asp:LinkButton 
     ID="btnPopupClose" 
     runat="server" 
     OnClientClick="$find('popupAddProductKeys').hide()">x</asp:LinkButton>
Follow

Get every new post delivered to your Inbox.

Join 67 other followers