jQuery AJAX (and the shorthands)

Using AJAX (asynchronous JavaScript and XML) for your ASP.NET MVC application is made relatively easy using the jQuery .ajax method or one of the shorthand methods available. I found a nice introduction on jQuery Fundamentals which explains the concepts i used as the basis for my own experiments with MVC5 and AJAX.

$.ajax: basic usage

The $.ajax method takes a configuration object specifying things like the url, the http-verb to use, the format, and the callback functions for the succes and error situations. A straightforward way to render this is like this:

@section Scripts {
    <script type="text/javascript">

        function addFav()
        {
            $.ajax({
                url: '@Url.Action("AddFavorite", "Test", new { id = Model.Id })',
                type: 'POST',
                dataType: 'json',
                success: function (resp) {
                    $('#notificationBox').html(resp);
                },
                error: function (jqXHR, status, err) {
                    errMsg = jQuery.parseJSON(jqXHR.responseText);
                    console.log('Oops: ', status, errMsg);
                }
            });
        }

        $(function () {
            $('#favMe').on('click', addFav);
        });

    </script>
}

Somewhere on the page, there is an element with id ‘favMe’ and another element with id ‘notificationBox’. This code will fire the addFav method when the user clicks the element with id ‘favME’. When the ajax call succeeds the message that is returned from the server is written to the ‘notificationBox’ element.

The server side code looks like this:

[HttpPost]
public JsonResult AddFavorite(int id)
{
         if (!User.Identity.IsAuthenticated)
         {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                return Json("You must be logged in to add a favorite!");
         }

         var fav = new Favorite(){UserName = User.Identity.Name, Id = id};

         // ... validate id and persist changes ...

         return Json("Added to favorites", JsonRequestBehavior.AllowGet);
}

Returning a http statuscode like 400 (Bad Request) or 500 (Internal Server Error) will effect in the error callback being called on the client side. Just throwing an exception from the action method will send an internal server error back to the client. By specifying the http statuscode yourself you have more control over the data being returned to the client. One note though: a statuscode of 401 (unauthorized) does not result in the error callback being called. Instead, the success callback is called.

Shorthand Methods

jQuery has a number of shorthand methods for the $.ajax method that can result in less javaScript code:

jQuery.get() loads data from the server using a HTTP GET request.
jQuery.post() loads data from the server using a HTTP POST request. The above code could also be written using the post method:
function addFav() {
     $.post('@Url.Action("AddFavorite", "Test", new { id = Model.Id })',
                function (resp) {
                    $('#notificationBox').html(resp);
                }
     );
}
jQuery.getJSON() loads Json-encoded data from the server using a HTTP GET request.
jQuery.getScript() loads javaScript from the server using a HTTP GET request and execute it.

The shorthand methods are all in the form jQuery.methodname (or $.methodname). There is also a shorthand that you can apply on an element to load new data:

.load() Load data from the server and place the returned HTML into the matched element. This could be used in combination with a controller method that returns a partial view.
$('#data').load('@Url.Action("SomeData", "Test", new {id = Model.id})');

JSONP and CORS

For security reasons many browsers block requests to other domains. For this reason jQuery offers support for the JSONP (‘JSon with Padding’ or ‘Json with Prefix’) protocol.

Another option is to use CORS (Cross-origin resource sharing), but this is not supported by older browsers. It also requires some work on the server side: either by adding a header programatically:

Response.AppendHeader("Access-Control-Allow-Origin", "*");

or, by adding some configuration to the web.config:

<system.webServer>

    <httpProtocol>;
      <customHeaders>
        <clear>
          <add name='Access-Control-Allow-Origin' value='*' />;
      </customHeaders>
    </httpProtocol>

..

When using JSONP or CORS to post to another domain you should be very careful as this introduces a security risk to the application.

Tracing in MVC5

While studying for the Microsoft 70-486 certification i noticed there are two ways you can use tracing in your controller methods. The first one is by using the Trace class:

Trace.TraceInformation("View called for id = {0}", id);

When you have configured a TextWriterTracelListener this will write a line to the logfile containing the name of the executing process (in my case ‘iisexpress’, the switchvalue and the line itself.

The other way is to call a method on the TraceSource class. This will give you the opportunity to supply a name for the logger and create a dedicated configuration. The code for using a TraceSource looks like this:

TraceSource trace = new TraceSource(this.GetType().Name);
trace.TraceInformation("Index action called");

For completeness, here is the configuration as well:

<system.diagnostics>
    <sources>
      <source name="ArtworkController"             switchType="System.Diagnostics.SourceSwitch"             switchValue="Information">
        		<listeners>
          <clear/>
          <add name="textListener" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="textListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="D:\Projects\Logs\trace.log" traceOutputOptions="Timestamp"/>
    </sharedListeners>
    <trace>
      		<listeners>
        <add name="textListener" />
      </listeners>
    </trace>
  </system.diagnostics>

The TextWriterTraceListener that comes out of the box in .NET does not give much options to format the text. In the above configuration i added a timestamp to the trace output. The TextWriterTraceListener did not ad – as i was expecting – the timestamp on the same line, but added another line. For this reason i prefer to use a more mature logging framework like log4net.

When you need a more structured setup, you can also use the DelimitedListTraceListener. With this you can create a csv file with the trace output.

Tracing page information

Note that the old style web forms tracing is also available. You have to setup some things in the web.config to make it work:

<system.web>
    <trace enabled="true" mostRecent="true" pageOutput="false"/>
</system.web>

When this is configured you can click through the application and then point the url to http://server/sitename/trace.axd and then a page will be shown containg limited information about the last requests. In a webforms application you could use Trace.Write and the data would be written to the trace.axd result. However, in MVC this will not happen and you will need to use the tracing mechanism as described above.