Using ASP.NET Razor syntax with HTML5 and JQuery

In this tutorial you will learn how ASP.NET Web Pages with Razor syntax can be used to build a RESTful web service that is capable of delivering weather forecast data to a website using JQuery and the HTML5 Canvas element. We will be using WebMatrix, a lightweight tool for web development to write code and manage the website throughout this tutorial.

Getting Started

The Current Weather Site

The REST service that you will create will be built on top of the Current Weather WebMatrix site that was built in the tutorial Consuming and Storing Data from a REST service with Razor. In that tutorial we learned how the integrated database features of WebMatrix could be used to retrieve and cache weather service data. Some of the code you will be looking at in this tutorial will directly reference the weather forecast features developed for the Current Weather site, so if you would like to familiarize yourself with how the site works, feel free to review that tutorial before continuing.

What You’ll Need

In addition to WebMatrix and the Razor syntax, you will be working with some standard web technologies. If you have a basic understanding of HTML5, CSS, JSON and JavaScript you will be set.

A RESTful Service

The first thing we want to do is review the Razor methods that the Current Weather site uses to retrieve forecast data. The functions we want to use are located in a Razor function block in the App_Code folder of the site. Function blocks are called using the following signature:

FileName.FunctionName([DataType arg], [DataType arg]…);

The filename that the Current Weather site uses is Weather.cshtml and the function is GetForecastByZipcode(string zip). So to call this function using Razor you would write:

@{
    var temp = Weather.GetForecastByZipcode("92805");
}

as you can see, we simply pass any valid zip code to the function in order to receive the forecast data for that area. the object that is returned is a simple container that holds the temperature data that is retrieved from a remote weather service.

public class temperature
{
    public string zip { get; set; }
    public string latitude { get; set; }
    public string longitude { get; set; }
    public int maxtemp { get; set; }
    public int mintemp { get; set; }
    public string forecast { get; set; }
}

each property of the temperature object can be easily accessed through the ‘temp’ variable. for example, the following razor code will write the forecast description into an html paragraph:

<p>@temp.forecast</p>

so now that now you understand how weather forecast data is accessed, let’s take a look at how the data stored in the temperature object can be converted to a format that is consumable by other applications.

What is REST

a restful web service is simply a service that is implemented using http as a protocol. that means that requests are made from client to server using a valid uri as a locator. a typical restful api will respond to requests with a standards compliant data format like json or xml. for this tutorial you will be creating a very simple service layer on top of the getforecastbyzipcode method that we discussed in the last section.

Using Razor to Deliver RESTful Content

in order to create a restful web service layer on top of the current weather site, we will need to add some code that accepts a client supplied zip code and returns data in a standardized format. so let’s start by creating an empty razor page to hold the required code. for this tutorial we are using a page called jsonservice.cshtml, but you are free to name it anything you like. this page will only contain a small razor code block, so any default html can safely be removed.

now that we have a place to hold the service code, let’s discuss how we can add support for accepting zip codes. since a restful web service is implemented over http, the simplest means of passing http compliant data into a url is with query string parameters. so let’s add a code block the page and extract a query parameter from the request object called ‘zip’.

@{
    var zip = request.querystring["zip"];
}

now whenever a request is made to the following url:

http://localhost/CurrentWeather/JsonService.cshtml?zip=92805

the razor variable, zip, will contain the requested zip code. you will also probably want to make sure that if the zip parameter is missing that the page handles it appropriately. so let’s add a little more code to make sure that the request is properly formatted.

@{
    var zip = request.querystring["zip"];
    
    if(zip == null){
        response.write("Missing Zip Parameter");
        return;
    }
    
    if(zip != string.empty){
        //zip code exists
    }else{
        response.write("Invalid Zip Code");
    }
}

if either the zip parameter is missing a value or not even included in the url, the client that issued the request will receive a simple message indicating what went wrong.

alright, so now that we have a zip code supplied by a client request, we can retrieve the weather forecast data using the same function call to getforecastbyzipcode we looked at earlier. however we still need a convert the data held in the temperature object to a format that any client can consume. to do this we will be using the javascript object notation (json) protocol. json is a text-based open standard that has become the leading interchange format for sharing data in a web environment. the json standard uses a concise key/value pair format that is perfect for parsing data into a readable format. although the json specification is well documented, the code required to convert a razor object into json would be pretty lengthy to write yourself. thankfully, the webmatrix environment comes preinstalled with a json helper that simplifies the process of encoding and decoding objects into a few simple functions.

let’s take look at how the webmatrix json helper can be used to encode and decode the temperature object returned by the getforecastbyzipcode function by adding some test output to the service page.

if(zip != string.empty){
        //zip code exists
        var temp = weather.getforecastbyzipcode("92805");
        response.write("<p>" + temp.forecast + "</p>");
        
        var json = json.encode(temp);
        response.write("<p>" + json + "</p>");
        
        var temp2 = json.decode(json);
        response.write("<p>" + temp2.forecast + "</p>");
        
        json.write(temp, response.output);
    }else{
        response.write("Invalid Zip Code");
    }

if you were to run this code in a browser from within the current weather site you would see the following page output:

partly sunny
{"Zip":"92805","Latitude":"33.8263","Longitude":"-117.911","MaxTemp":66,"MinTemp":50,"Forecast":"Partly Sunny"}
partly sunny
{"Zip":"92805","Latitude":"33.8263","Longitude":"-117.911","MaxTemp":66,"MinTemp":50,"Forecast":"Partly Sunny"}

the json helper contains two methods, encode and decode, which are used to convert the temperature object in and out of a json formatted string. notice that the encoded output has converted the properties of the temperature object to keys and associated the property data to each key as a string value. when the json string is decoded, the properties of the object can once again be accessed as they were prior to encoding. since we only need to encode and write directly to the response stream, the process can be simplified through the use of the json.write method as demonstrated in the last line of code.

A Quick Test

your final output of the rest service should look something like this:

@{
    var zip = request.querystring["zip"];
    
    if(zip == null){
        response.write("Missing Zip Parameter");
        return;
    }
    
    if(zip != string.empty){
        var temp = weather.getforecastbyzipcode(zip);
        json.write(temp, response.output);
    }else{
        response.write("Invalid Zip Code");
    }
}

now whenever a valid zip code is sent to the service, the requesting client will receive a json formatted string that can be decoded by a native json parser.

A Web Client with HTML5

after years of waiting, the html5 standard is finally starting to find its way into all the modern browsers. this opens the path to rich client experiences without the need for additional browser plugins. the canvas element provides access to a powerful api that is capable of rendering the type of vector based graphics and animations that you would expect to see in flash or silverlight. in this section, we will look at how the canvas api and ajax can be combined to dynamically communicate with the current weather site using the rest service we just created.

Using JQuery and AJAX

to allow the canvas element to receive updates without having to do a full page load, we will use ajax to send zip code requests to the rest service in the background. we will be using the jquery javascript library to simplify our interaction with ajax. if you are not familiar with jquery, feel free to take a look at the tutorials and documentation available at jquery.com. the jquery library provides an amazing array of cross-browser compatible features that remove a lot of the complexity involved in client side programming. let’s take a quick look at how we can use the ajax features in jquery to call the rest service page without requiring a page reload. for demonstration purposes, let’s add the following code to a new razor page called jqueryweather.cshtml. this page should be added under the same path as the jsonservice.cshtml file we created in the previous section.

<script src="http://ajax.microsoft.com/ajax/jQuery/jquery-1.5.js" type="text/javascript"></script> 
<script type="text/javascript">
            var index = 0;
            var zipcodes = new array();
            zipcodes[0] = "02125";
            zipcodes[1] = "60611";
            zipcodes[2] = "90001";
            zipcodes[3] = "33122";
            zipcodes[4] = "70112";
            zipcodes[5] = "10012";
            zipcodes[6] = "98101";
            
            $(document).ready(function(){
                updateweather();
            });
            
            function updateweather(){
                $.getjson('/JsonService',{ zip:zipcodes[index] }, function(data) {
                    $("#json").html("<ol>" + 
                            "<li>Location: " + zipcodes[index] + "</li>" +
                            "<li>Forecast: " + data.forecast + "</li>" +
                            "<li>High: " + data.maxtemp + "</li>" +
                            "<li>Low: " + data.mintemp + "</li>" +
                        "</ol>");
                        
                    if(index > 5) index = 0;
                    else index++;
                    
                    settimeout("UpdateWeather()", 3000);
                });
            }
        </script>
    <div id="json"></div>

in this example, we have created an array of zip codes that are used to request weather data from the rest service page. as soon as the dom has loaded the updateweather function is called. the updateweather function performs the ajax request and sets a timer that will call the function again after three seconds have passed. the getjson function is a shortcut to jquery’s ajax api that removes some of the additional parameters required to work json data. in short, getjson is given the rest service url, a zip code selected from the array incrementally, and a function that will execute as soon as data is retrieved from the service. the jquery ajax api will assemble the url and zip code value into the valid query string required by the service. the data is returned as a json object derived from the properties and values of the temperature object that is used by the razor server code. notice that when the data is written to the div html element, that the individual temperature values are accessed using the exact same property names that we created in razor. if you were to run this code, it would iterate through the zip code array every three seconds and dynamically update the web page after each response.

Building an Interface with the Canvas Element

alright, now let’s look at how we can take advantage of the canvas element to create a nice, polished user interface to consume this data.

img1

the picture above is a screen capture of the vector graphics rendered by two canvas elements on a single web page. the buttons and other static content are rendered on a canvas element that acts as a bottom layer. the weather icon and forecast information is rendered on a separate canvas element that is positioned on top of the bottom layer. when a city is selected, the top layer is reset and updated weather data is written to the canvas. the following html will be added to the default.cshtml page that was created with the site.

<html lang="en">
    <head>
        <script src="http://ajax.microsoft.com/ajax/jQuery/jquery-1.5.js" type="text/javascript"></script>
        <script src="/Content/Scripts/JScript.js" type="text/javascript"></script>
        <style type="text/css">
            body{
                margin: 0;
            }
            canvas{
                position:absolute;
                left:0px;
                top:0px;
            }
            #canvasStage{
                background: #000;
                z-index:1;
            }
            #canvasText{
                z-index:2;
            }
        </style>
        <meta charset="utf-8" />
        <title>Current Weather</title>
    </head>
    <body>
        <div id="canvasContainer">
            <canvas width="640" height="480" id="canvasStage"></canvas>
            <canvas width="640" height="480" id="canvasText"></canvas>
        </div>
        <div id="weather"></div>
    </body>
</html>

as you can see in the code above, both canvas elements are set at the same height and width. then using css, they are positioned directly on top of each other using the z-index property. in order to render content to a canvas element, you will need to interact with it through the dom using javascript. notice that there is a separate script tag that references a javascript file called jscript.js. let’s take a look at that next.

var canvas;
var canvasText;

$(document).ready(function(){
    canvas = document.getElementById('canvasStage');
    canvasText = document.getElementById('canvasText');
    if(canvas.getContext){
        drawBackground();
        createButtons();
        checkWeather(button1);
    }else{
 $("#canvasContainer").html("Your browser does not appear to support the HTML5 Canvas element"); 
    }
    
    $('canvas').click(function(e){
        buttonClick((e.clientX-canvas.offsetLeft), (e.clientY-canvas.offsetTop));
    });
});

once again, we wait until the dom has completely loaded before performing any other operations. then we create instances of the two canvas elements that are used to render the graphics and weather data. there is a simple browser check in place so that browsers that are not able to display canvas content will display a warning message. if the browser is supported, the content is then rendered. the function drawbackground is responsible for setting up the background on the canvasstage element. once the background is rendered, the createbuttons function will use hard coded city information to create an array of button objects. the button object is just a custom javascript object that is used to hold the name and zip code of the individual cities that are displayed. finally, a click event is registered to all canvas elements on the page. by comparing the mouse coordinates at the time of the event with the location and size of the selected button, the buttonclick function can determine which, if any, of the listed cities were clicked.

function buttonClick(x,y){
    for (var i = 0; i < buttons.length; i++){
        var button = buttons[i];
        if(x > button.X && x < (button.X + button.Width)){
            if(y > button.Y && y < (button.Y + button.Height)){
                checkWeather(button);
                break;
            }
        }
    }
}

when a valid click is detected, the button object is passed to the checkweather function and used to display the name of the city and request weather data from the jsonservice.

function checkWeather(button){
    wait(button.Text);
    
    $.getJSON('/JsonService',{ zip:button.Location }, function(data) {
        updateLocation(data, button);
    })
}

the caching logic used by the current weather website is designed to only make requests from the remote weather service when the local data for a selected location has expired. when this happens there can be a delay while the site is waiting for fresh data. the wait function renders a loading screen using the name of the selected location. this function is called before every request so that if the request takes more than a few milliseconds the user is presented with some immediate feedback. the following picture shows the loading screen that is displayed when the weather for the boston area has to be refreshed.

img2

the wait function demonstrates how javascript is used to interact with api exposed by the canvas element. to interface with the api a reference to the primary context of the canvas element must be requested. the wait function uses the 2d context of the top layer canvastext element to write the loading message that you see in the picture above. in the case that the canvastext element already has content rendered to it, the entire content area is cleared before writing the loading message.

function wait(city){
    var ctx = canvasText.getContext('2d');
    ctx.clearRect(0,0,640,480);
    ctx.fillStyle    = '#fff';
    ctx.font         = '12px sans-serif';
    ctx.fillText  ('Loading ' + city + '...', 240, 100);
}

when the jsonservice request has completed the callback function passes the response data and requesting button object to the updatelocation function.

function updateLocation(weather, button){
    var ctx = canvasText.getContext('2d');
    ctx.clearRect(0,0,640,480);
    ctx.save();
    
    ctx.fillStyle    = '#fff';
    ctx.font         = 'bold 20px sans-serif';
    ctx.fillText  (weather.Forecast, 240, 160);
    
    ctx.fillStyle = 'rgba(205,205,205,.7)';
    ctx.font         = 'bold 16px sans-serif';
    ctx.fillText  (weather.MaxTemp, 240, 185);
    
    ctx.font         = 'bold 22px sans-serif';
    ctx.fillText  (button.Text, 240, 100);
    
    ctx.font         = '14px sans-serif';
    ctx.fillText  (new Date().toDateString(), 245, 125);
    
    var sun = /(sun|sunny|sunshine|clear)/i;
    var cloud = /(cloud|fog|haze|overcast|blustery)/i;
    var snow = /(snow|flurries|ice)/i;
    var rain = /(rain|shower|drizzle)/i;
    var storm = /(storm|thunder|lightning)/i;
    
    if(weather.Forecast.search(sun) >=0){
        drawSun(ctx,60,120);
    }else if(weather.Forecast.search(cloud)>=0){
        drawCloud(ctx,40,150);
    }else if(weather.Forecast.search(snow)>=0){
        drawCloud(ctx,40,150);
    }else if(weather.Forecast.search(rain)>=0){
        drawRain(ctx,40,200);
    }else if(weather.Forecast.search(storm)>=0){
        drawThunder(ctx,20,20);
    }
    ctx.restore();
}

the updatelocation function once again clears the canvastext area, removing the loading message from the context. the weather data that was returned from the jsonservice is then rendered to the context. the user interface that we are using only includes a few weather icons for this tutorial, so to decide which icon to render a very basic conditional lookup is used to extract common weather terms from the forecast and assign an icon. the code required to generate the visual elements can get pretty lengthy and repetitive, so in this tutorial we will only provide a brief overview of the approach that was used. to see the full source for the full weather widget, take a look at the javascript file included in the demonstration site.

the weather icon rendering functions that are called from the updatelocation function, accept a context object along with the desired coordinates for icon positioning as parameters. in the following example you can see how the weather icon for thunder is rendered.

function drawThunder(ctx, x,y){
    ctx.translate(x,y);
    ctx.beginPath();
    ctx.moveTo(185.15397,137.33667);
    ctx.lineTo(133.81479,136.96566);
    ctx.lineTo(124.17756,159.20675);
    ctx.lineTo(147.34501,159.02141);
    ctx.lineTo(127.51367,187.37837);
    ctx.lineTo(146.2327,186.08124);
    ctx.lineTo(130.66444,217.95941);
    ctx.lineTo(185.89568,168.28854);
    ctx.lineTo(165.87861,168.1032);
    ctx.lineWidth=2;
    ctx.fillStyle="white";
    ctx.fill();
}

the lineto function is used to draw a continuous line in the shape of a lightning bolt. when the shape is complete it is filled with the color white. you can see what it looks like when rendered to the page in the following picture:

img3

Summary

in this tutorial you saw how webmatrix can be used to create a restful content delivery service that can be consumed by a variety of user interfaces. you learned how webmatrix can be used to build sophisticated client pages using the latest standards like html5 and jquery.

webmatrix was created from the ground up to be a simple, all-inclusive web development tool that is easy to use but still powerful enough to deliver the advanced features used by websites today.

Additional Resources

learn jquery http://www.jquery.com/

download webmatrix http://www.microsoft.com/web/webmatrix/

learn more http://www.microsoft.com/web/category/learn

more tutorials http://www.asp.net/webmatrix

learn more http://www.microsoft.com/web/post/Web-Development-101-using-WebMatrix

twitter http://twitter.com/mswebplatform

for any questions or comments about this tutorial please contact the aeshen team at http://www.twitter.com/aeshen

You can discuss this article using the adjacent Facebook talkback.

For technical questions please visit our discussion forums, where we have a vibrant community of developers like you, as well as Microsoft engineers who are ready to answer your questions!