Showing posts with label Responsive Web Design. Show all posts
Showing posts with label Responsive Web Design. Show all posts

Tuesday, September 10, 2013

Getting Started with Mobility, Part 9: Mobile Web with Responsive Web Design

In this series of posts, we're looking at how to get started as a mobile developer. In Part 1, we provided an overview of the mobile landscape, and discussed various types of client app development (native, hybrid, mobile web) and supporting back-end services. In Parts 2-5, we examined native app development for iOS, Android, Windows Phone, and Windows 8. In Parts 6-8 we examined three hybrid platforms, Xamarin, Titanium, and Icenium. Here in Part 9, we will look at Mobile Web.



Mobile Web: The Good, The Bad, and The Ugly
Mobile Web is about writing an HTML5 web application that will run acceptably on mobile devices in a browser, rather than executing a native app on the device--and it is controversial. Mobile web is often looked down upon, especially after Facebook's Mark Zucker declared that betting on HTML5 was a mistake last year. Using mobile has been likened to putting the engine of a Smart Car into a Ferrari. I think it helps to make a distinction between developing a web site to also render well on mobile devices--surely a good idea that everyone can get behind--vs. deciding to create a mobile web solution in place of a true mobile app; it's the latter case that's controversial.

Let's try to take an even-handed look at mobile web. First of all, mobile web has some good things going for it, and that includes lower cost of development and easier to find developer skills. You create a single web solution, using web skills that you can readily find affordable developers for, that support a large variety of mobile devices. Compare that to developing an assortment of mobile app projects for each target platform, each in a different language, and you can appreciate the savings.

Mobile web is a bad choice when you have requirements a browser-based solution can't satisfy. That includes access to the camera and other hardware sensors on your phone; and access to the mobile operating system for such things as contacts and photos and videos. HTML5 does give you some access to your phone's sensors, such as Geolocation (GPS) support; and some access to your phone's operating system, such as Web Storage. But the bottom line is, there's far more denied to you than made available to you. And we should point out, the most innovative mobile apps tend to take advantage of the phone's hardware and operating system.

And then there's the ugly: mobile web implementations can be unfulfilling to use, for cosmetic or performance reasons. From a user experience standpoint, it takes extra work to make a browser-based web app shine on a mobile device, including detecting the device you're running on and honoring its user interface design philosophy. Overly-simplistic mobile web solutions stand out like a sore thumb because they aren't honoring your phone's conventions. Browser-based solutions can also perform quite poorly compared to native apps.

Mobile web apps are also not delivered through your device's App Store. However, it is possible on many mobile platforms, once you pull up a web app in a browser, to save it to your home screen so that it has the appearance of an installed app.

Having said all that, when is a mobile web approach worth considering? I would say, in these cases:
  • When native development is not an option (perhaps you only have web developers available, or are cost-constrained).
  • When you don't need access to your device hardware or its operating system beyond what HTML5 can give you.
  • When your app is simple, and you don't have robust performance requirements.
  • When you don't need to be in the mobile App Stores.
  • When you want to provide a web experience for desktop computers that also shows acceptably on mobile devices.
  • When you need a reasonable fallback for mobile devices that you won't be creating targeted native apps for.
With all of that out of the way, read on if you still think mobile web might be for you.


Responsive Web Design
A key technique in mobile web is Responsive Web Design, which is all about detecting some of the characteristics of the device you are running on (especially screen dimensions) and adapting layout intelligently. Your layout follows a fluid grid model. You tend to use a lot of percentages and proportional style rules in RWD; for example, you might establish a left panel as 30% of available width and a main panel as 70% of available width. By using proportional measurements (which you can also apply to font selection and images), your app can be very tolerant of device size and orientation differences.

Just making your layout proportional is not enough, however: on tablets and phones you'll need to arrange things differently at times, reduce or leave out some content, and make different sizing decisions. To the rescue comes CSS Media Queries, a feature in CSS3 that allows you to have conditional style rules that apply only when certain criteria are satisfied. For example, the CSS media query below only applies when running on a phone-sized screen. Using CSS Media Queries, you can have a base set of CSS rules and then one or more sections of conditional rules that kick into action for some categories of devices.

/* Smartphones (portrait and landscape) ----------- */
@media only screen and (min-device-width : 320px) and (max-device-width : 480px) {
    /* CSS styles for phones go here */

    ...
}


You can implement your own responsive web design, or leverage frameworks that do much of the work for you. If you're doing it on your own, I recommend devouring the book Responsive Web Design by Ethan Marcotte and taking it to heart. Below we examine some of the responsive frameworks that are available.


Twitter Bootstrap
Twitter Bootstrap is a popular open-source framework that automates responsive web design using a grid system. It's intended for web site development but has good support for mobile devices.

Bootstrap uses classes to define rows, columns, and spanning as shown in the sample HTML fragment below.

    <div class="row">
      <div class="span4">
        <div class="row-fluid">
          <div class="4">...</div>
          <div class="4">...</div>
          <div class="4">...</div>
        </div>
      </div>
      <div class="span8">...</div>
    </div>


Apps written in Bootstrap behave well on phones, tablets, and desktop-sized screens. For example, this sample renders quite differently on different size devices.

Rendering on an iPhone
 
Rendering on an iPad
 
Rendering on a desktop
 
jQuery Mobile
jQuery Mobile is a popular mobile web framework, described as "a unified, HTML5-based user interface for all popular mobile device platforms, built on the rock-solid jQuery and jQuery UI foundation." As its name implies, jQuery Mobile is oriented toward mobile devices with touch interfaces.

Here's an example of HTML markup that uses jQuery Mobile. jQuery Mobile relies heavily on data-xxxx attributes in HTML elements.

<!DOCTYPE html>
<html>
    <head>
       <title>Page Title</title>
       <meta name="viewport" content="width=device-width, initial-scale=1" />
       <link type="text/css" href="http://code.jquery.com/mobile/latest/jquery.mobile.min.css" rel="stylesheet" />
    </head>

    <body>
        <div data-role="page" id="first" data-theme="a">
            <div data-role="header">
                <h1>Page Title1</h1>
            </div><!-- /header -->

            <div data-role="content">
                <p>Page content goes here.</p>
                <a href="#second" data-transition="flip">Go to second page</a>
            </div><!-- /content -->

            <div data-role="footer">
                <h4>Page Footer1</h4>
            </div><!-- /footer -->
        </div><!-- /page -->

        <div data-role="page" id="second" data-theme="b">
            <div data-role="header" data-add-back-btn="true">
                <h1>Page Title2</h1>
            </div><!-- /header -->

            <div data-role="content">
                <p>Page content goes here.</p>
            </div><!-- /content -->

            <div data-role="footer">
                <h4>Page Footer2</h4>
            </div><!-- /footer -->
        </div><!-- /page -->
        <!-- JavaScript placed at the end of the document so the pages load faster. -->
        <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
        <script type="text/javascript" src="http://code.jquery.com/mobile/latest/jquery.mobile.min.js"></script>
    </body>
</html>


Let's look at an example. The Dodge mobile website, m.dodge.com, was made using jQuery Mobile, and looks like as follows on phone, tablet, and desktop size screens:


Rendering on an iPhone
 
 
Rendering on an iPad

Rendering on a desktop

There are certainly other frameworks out there, so if you're researching you might also want to check out Kendo UI Mobile and Sencha Touch, among others. Some of these frameworks are open source and free, others require a commercial license. Generally you'll find very good developer resources, online demos, and supporting tools for these frameworks.


Summary: Mobile Web
Mobile Web is often looked upon as a second-class citizen in the mobile development world, a fallback for those who can't develop true native apps. However, that's harsh: while mobile web isn't always the right approach, it certainly has its place in the mobile landscape. It especially makes sense when you have a web site that you want to also be consumable from mobile devices.

Next: Getting Started with Mobility, Part 10: A Mobile Back-end in the Cloud with Windows Azure Mobile Services

Saturday, April 14, 2012

Outside-the-Box Pizza, Part 3: Mobility & Responsive Web Design

In this series, we’re looking at Outside-the-Box Pizza, a web-mobile-social-cloud demo, from a variety of perspectives that include design, technologies, and techniques. The online demo is at http://outsidetheboxpizza.com. Here in Part 3, we’ll be looking at how the app supports mobile devices, primarily through the technique of Responsive Web Design. We’ve seen the desktop browser experience, but how does Outside the Box Pizza render on smartphones and tablets? We’ll look at four things:
·         Responsive Web Design and CSS3 Media Queries
·         Font Size and Responsive Text
·         Running Full-Screen on the iPad
·         Sticky Footers

The Goal: Broad Reach
We want our web applications to have broad reach, to work on as many devices as possible. Below you can see some examples of Outside the Box Pizza on various devices. It renders acceptably on smartphones (iPhone, Android, Windows Phone 7), tablets (iPad, Windows 8), and desktop browsers (Chrome, Firefox, Safari, Internet Explorer). It also handles orientation changes on smartphones and tablets.
Outside-the-Box Pizza on various Phones and Tablets

Responsive Web Design & CSS3 Media Queries
The key technique to support a wide variety of modern devices well is Responsive Web Design, the brainchild of Ethan Marcotte. His book on the subject is highly recommended. Responsive Web Design is about adapting to the device we find ourself running on and not making assumption in advance. The most articulated aspect of RWD is adaptive layout for different size and orientation devices, but the principal can be extended to other areas. Let’s first look at device dimensions.
We can use the CSS3 media queries feature to have conditional CSS styling that only applies to devices meeting certain criteria. For example, this is how we express conditional styling in our CSS that should apply to smartphones, which generally have a width of 320 to 480 pixels:
/********************
*                   *
*   Mobile Styles   *
*                   *
********************/

@media only screen and (max-width: 480px) {
    header .float-left, header .float-right {
        float: none;
    }

    header .site-title {
        margin: 10px;
        text-align: center;
    }
    
    .selectGroup li 
    {
        display: inline-block; width: 120px 
    }
    
    h1 { font-size: 1.4em; }
    h2 { display: none; }
    
    .logo { font-size: 1.75em }
    .logo i { display: none }
    .text i { display: none }
    .text b { display: none }

    span b { display: none }
    span i { display: none }
    button b { display: none }
    article { font-size: 0.8em }
    article i { display: none }
    article B { display: none }

    ...


In our case the default CSS is for the desktop browser and our conditional CSS adapts to smaller mobile device dimensions, but I should point out that an alternative way to go is to have your default styles target phones and use conditional styling for larger sizes. Today we have pretty widespread support for HTML5 and CSS3 media queries in our mobile browsers, but when that was less common doing what I just described was important so that the default rendering of the site on “dumb phones” would still be acceptable. Supporting dumb phones wasn’t a priority in this application.
None of the styles in these conditional sections are new; that is, all of the style rules have been previously defined in the base CSS. Rather, what we’re doing here is overriding many of our styles for the small device. There are several reasons to override styles for smaller size screens:
1.       Spacing. We may need to change margins and padding to reduce white space.
2.       Sizes. We may need to change widths, or heights.
3.       Type. We may need to change font characteristics such as size or font family.
4.       Hiding. We may need to hide elements for which we don’t have room to show.
5.       Layout. We may choose to arrange elements differently.
You can reduce the amount of work you need to do here by thinking in percentages as RW advocates. The more we use percentages in our CSS styling rather than fixed units, the less need there is for conditional styles.

Layout Flow
We can solve a lot of problems simply by choosing our layout flow wisely. For example, Outside-the-Box Pizza has an orders page in which many choices are listed for pizza shape, sauce, meat toppings, and veggie toppings. These are all expressed as list items, which are styled such that the browser will intelligently wrap as many across as possible. Notice how this renders on various screen sizes:
Orders Page on Various Mobile Devices
Also notice the toppings icons are designed to be easily touched as well as clicked. When a topping is active, we mark the item as checked and use styling to show its bounding circle in green rather than gray (the “circles” are actually rectangular borders with a generous border radius).
Order Topping Items Designed to be Touch-friendly
Here’s the style for the order list items and how the list items are defined:

.topping { font-family: Arial, Helvetica; font-size: 1.0em; color: Black }
.topping input { display: none; vertical-align: middle; margin-right: 4px; }
.topping span { font-family: Arial, Helvetica; font-size: 1.2em; color: Black }
.topping label { display: inline; font-family: Arial, Helvetica; font-size: 1.2em; color: Black; vertical-align: middle; }

.topping:checked { background-color: #C00000 }
.topping:checked+label { background-color: #C00000 }

.topping img { display: inline; vertical-align: middle; margin-right: 4px; height: 48px;
               border: 4px solid Silver;
               -webkit-border-radius: 32px;
               -moz-border-radius: 32px;
               border-radius: 32px;   
}


<div class="selectGroup">
<div class="selectGroupTitle">Meat Toppings</div>
<ul>
<li class="topping"><label for="topPepperoni"><img src="~/Images/topping_pepperoni.png"  alt="image"/><input id="topPepperoni" name="jqdemo" value="value1" type="checkbox"/>pepperoni</label></li>
<li class="topping"><label for="topSausage"><img src="~/Images/topping_sausage.png"  alt="image"/><input id="topSausage" name="jqdemo" value="value1" type="checkbox"/>sausage</label></li>
<li class="topping"><label for="topHam"><img src="~/Images/topping_ham.png"  alt="image"/><input id="topHam" name="jqdemo" value="value1" type="checkbox"/>ham</label></li>
<li class="topping"><label for="topBacon"><img src="~/Images/topping_bacon.png"  alt="image"/><input id="topBacon" name="jqdemo" value="value1" type="checkbox"/>bacon</label></li>
<li class="topping"><label for="topBeef"><img src="~/Images/topping_beef.png"  alt="image"/><input id="topBeef" name="jqdemo" value="value1" type="checkbox"/>beef</label></li>
<li class="topping"><label for="topChicken"><img src="~/Images/topping_chicken.png"  alt="image"/><input id="topChicken" name="jqdemo" value="value1" type="checkbox"/>chicken</label></li>
<li class="topping"><label for="topTurkey"><img src="~/Images/topping_turkey.png"  alt="image"/><input id="topTurkey" name="jqdemo" value="value1" type="checkbox"/>turkey</label></li>
<li class="topping"><label for="topElk"><img src="~/Images/topping_elk.png"  alt="image"/><input id="topElk" name="jqdemo" value="value1" type="checkbox"/>elk</label></li>
<li class="topping"><label for="topSardines"><img src="~/Images/topping_sardine.png"  alt="image"/><input id="topSardines" name="jqdemo" value="value1" type="checkbox"/>sardines</label></li>
<li class="topping"><label for="topEgg"><img src="~/Images/topping_egg.png"  alt="image"/><input id="topEgg" name="jqdemo" value="value1" type="checkbox"/>egg</label></li>
</ul>
</div>

<div class="selectGroup">
<div class="selectGroupTitle">Veggie Toppings</div>
<ul>
<li class="topping selectedTopping"><label for="topCheese"><img src="~/Images/topping_cheese.png"  alt="image"/><input id="topCheese" name="jqdemo" value="value1" type="checkbox" checked="checked"/>cheese</label></li>
<li class="topping"><label for="topTomato"><img src="~/Images/topping_tomato.png"  alt="image"/><input id="topTomato" name="jqdemo" value="value1" type="checkbox"/>tomatoes</label></li>
<li class="topping"><label for="topPineapple"><img src="~/Images/topping_pineapple.png"  alt="image"/><input id="topPineapple" name="jqdemo" value="value1" type="checkbox"/>pineapple</label></li>
<li class="topping"><label for="topOnions"><img src="~/Images/topping_onion.png"  alt="image"/><input id="topOnions" name="jqdemo" value="value1" type="checkbox"/>onions</label></li>
<li class="topping"><label for="topPeppers"><img src="~/Images/topping_pepper.png"  alt="image"/><input id="topPeppers" name="jqdemo" value="value1" type="checkbox"/>peppers</label></li>
<li class="topping"><label for="topSprouts"><img src="~/Images/topping_sprouts.png"  alt="image"/><input id="topSprouts" name="jqdemo" value="value1" type="checkbox"/>sprouts</label></li>
<li class="topping"><label for="topArtichoke"><img src="~/Images/topping_artichoke.png"  alt="image"/><input id="topArtichoke" name="jqdemo" value="value1" type="checkbox"/>artichoke</label></li>
<li class="topping"><label for="topGorgonzola"><img src="~/Images/topping_gorgonzola.png" alt="image" /><input id="topGorgonzola" name="jqdemo" value="value1" type="checkbox"/>gorgonzo</label></li>
<li class="topping"><label for="topBroccoli"><img src="~/Images/topping_broccoli.png" alt="image" /><input id="topBroccoli" name="jqdemo" value="value1" type="checkbox"/>broccoli</label></li>
<li class="topping"><label for="topPotato"><img src="~/Images/topping_potato.png" alt="image" /><input id="topPotato" name="jqdemo" value="value1" type="checkbox"/>m. potato</label></li>
</ul>
</div>

 
Font Size and Responsive Text
The main thing RWD has to say about text is to set font sizes based on “em” units rather than fixed unit sizes like pixels. In typography, an “em” is the width of the capital letter M in a typeface. Since we want to preserve a user’s right to control the default font size in their browser (for accessibility reasons, for example), doing all of our type setting relative to the “em” is the best approach. Thus our type settings for headings, text, button captions, etc. are all based on the em. Again, we may choose to use different proportions in our conditional styles for some devices.
h1 {
    font-size: 1.8em;
}

h2 {
    font-size: 1.5em;
}

h3 {
    font-size: 1.2em;
}

Another thing we can do to apply the philosophy of RWD to text is to responsive text, which has to do with right-sizing the amount of text content we display. For example, the navigation buttons and top-of-page text on a desktop browser look like this:
Desktop Button Captions and Page Text
…whereas on a phone they look like this, with shorter button captions and shorter page text. The button caption shortening prevents the buttons from wrapping to the next line on many phones. The text shortening prevents the text from taking up too much of the screen so that the content below can be seen without scrolling.

Phone Button Captions and Page Text
Implementing responsive text is a simple matter of choosing some text emphasis style (like <b> or <i>) and defining it to be visible for large screens and hidden on small screens. This is how the button captions and page text are defined in the HTML:
        <nav>
            <div class="content-wrapper">
                <button id="buttonSpecialOffers" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/';"><b>Special&nbsp;</b>Offers</button>
                <button id="buttonOrderPizza" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Order';">Order<b>&nbsp;Pizza</b></button>
                <button id="buttonTweet" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Share';">Tweet<b>za&nbsp;Pizza</b></button>
                <button id="buttonPizzaSightings" class="buttonUnselected" #nclick="javasc#ipt:window.location.href = '/Cool';">Cool<b>&nbsp;Pizzas</b></button>
            </div>
        </nav>


All it takes to apply this simply technique with finesse is to think through where you use longer and shorter text and ensure both versions communicate your intent well. Here’s another example of responsive page text:

<span class="text">Ordered an unusual pizza for an unusual occasion? Tweet it and you may win a discount<i> on your next order. We regularly award prizes for most unusual pizza, most unusual occasion, and even most unusual customer<i>!<span>


Full Screen on the iPad
When you access a web application on the iPad, this is how the app will look in the Safari Mobile browser by default. While there’s nothing wrong with the web display, we’d really like the app to have more the look of a native app, preferably running full screen without the browser’s tabs or address bar being visible.
Outside the Box Pizza on iPad - Default Appearance
We can achieve this on the iPad by 1) adding the HTML meta element below to our markup and 2) getting the user to add the web site to their home screen.

<meta name="apple-mobile-web-app-capable" content="yes" />
With the above accomplished, the “app” now has an app icon on the home screen and when invoked runs full-screen. The entire experience is now much closer to that of a native app.
Outside the Box Pizza on iPad - Full Screen
Responsive Images
One other area you should be looking at is sizing your images appropriate for your target device. You don’t want to send a large, hi-resolution image to a phone that is incapable of showing it that way: it’s a waste of bandwidth and slows down you app’s loading and rendering time.
We haven’t put responsive images into place yet in Outside the Box Pizza, but we will be doing so (and will update this post when that happens).

Sticky Footers
A sticky footer will smartly snap to the bottom of the display, which looks a lot more app-like than simply appearing at the end of the content. Sticky footers are best done in CSS (rather than JavaScript): they flow more smoothly and are less-taxing. A well-done sticky footer will also detect and handle long scrolling pages and in that case simply put the footer at the end of the content. Here’s a technique for a sticky footer in CSS you can use (source: http://ryanfait.com/sticky-footer/).
Here’s the relevant CSS for Outside-the-Box Pizza: 
html {
    margin: 0;
    padding: 0;
}

html, body 
{
    height: 100%;
}

#wrap 
{
    min-height: 100%;
    height: auto !important;
    height: 100%;
    margin: 0 auto -3.0em; /* Set footer height. */
}
footer, .push {
    height: 3.0em; /* Set footer height. */
}


Here’s what the rules do:
·         The first rule performs a reset of margin and padding for everything in the document.
·         The second rule sets the height to 100% for the html and body elements.
·         The #wrap rule defines a wrapper style, which will enclose everything in the body except the footer.
·         The .footer / .push rule makes space for the footer.
Notice the height in the #wrap rule is -1 x the height specified in the footer / .push rule. You can use any height you wish, but they must match.
In the HTML markup, we enclose the entire contents of your body in a wrapper div, except the footer – wrap that in a div with the footer class. The wrapper div should include a div with class push at the bottom which is empty. That’s all there is to it.
<div id="wrap">
        <header>
            <div id="banner" class="content-wrapper">
                <div id="logo" class="logo">
                    <div>outside&nbsp;the&nbsp;box&nbsp;<b>pizza</b>&nbsp;<img src="http://outsidetheboxpizza.blob.core.windows.net/images/icon.png" alt="icon" /></div>
<div><i>pizza&nbsp;as&nbsp;individual&nbsp;as&nbsp;you&nbsp;are.</i></div>
                </div>
            </div>
        </header>

        <nav>
            <div class="content-wrapper">
                <button id="buttonSpecialOffers" class="buttonUnselected" onclick="javascript:window.location.href = '/';"><b>Special&nbsp;</b>Offers</button>
                <button id="buttonOrderPizza" class="buttonUnselected" onclick="javascript:window.location.href = '/Order';">Order<b>&nbsp;Pizza</b></button>
                <button id="buttonTweet" class="buttonUnselected" onclick="javascript:window.location.href = '/Share';">Tweet<b>za&nbsp;Pizza</b></button>
                <button id="buttonPizzaSightings" class="buttonUnselected" onclick="javascript:window.location.href = '/Cool';">Cool<b>&nbsp;Pizzas</b></button>
            </div>
        </nav>

        <div id="body">
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>

        <div class="push"><!--Sticky Footer Push--></div>
    </div>

    <footer>
        <div class="content-wrapper" style="line-height: 3.0em">
            <div class="logo">
            <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a style="color: White; font-size: 0.75em" href="/Activity" >Sales</a></span>
            <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a id="StoreLink" style="color: White; font-size: 0.75em" href="/Store/Orders/@ViewBag.StoreId">Store</a></span>
            <span style="float: left; color: White; font-size: 0.65em; margin-right: 8px;"><a id="DeliveryLink" style="color: White; font-size: 0.75em" href="/Store/Driver/@ViewBag.StoreId">Driver</a></span>
            <span style="float: right; color: White; font-size: 0.65em; margin-right: 10px"><a style="color: White; text-decoration: none; font-size: 0.75em" href="/Home/About">About</a></span>
        </div>
        </div>
    </footer>


Summary
Outside-the-Box Pizza leverages responsive web design to adapt its layout to many kinds and sizes of device in order to extend its reach. It renders well on desktop browsers, tablets, and smartphones. Beyond layout, the principle of responsive web design is also seen in the use of responsive text and, in the future, responsive images. Using sticky footers makes the web application seem more like a native app, as does going full-screen if the platform/browser provides a means of achieving that.


Next: Part 4: Social Integration with Twitter

Tuesday, December 27, 2011

Mobile & Global with HTML5, MVC & Windows Azure, Step 7: Globally-Deployed

In this series of posts we’re progressively demonstrating a mobile and global sample, Responsive Tours. The source code for all 7 steps is on CodePlex at http://responsivetours.codeplex.com.

Here in Step 7 of 7 we’re going to deploy the solution to a Windows Azure data center. In this step we will:

• Create hosted services in multiple data centers
• Configure traffic management
• Update the Access Control Service to support the new data centers
• Enable Content Delivery Network for image blobs
• Deploy the solution to multiple Windows Azure data centers around the world
• Set up automated traffic management
• Set up a friendly DNS for the solution

Creating Hosted Services in Multiple Data Centers
In Step 6 we deployed the solution as a Windows Azure Compute hosted service in a single Windows Azure data center (we chose the South Central US location, which is in Texas). Now imagine we are serving a worldwide audience, and that we’d like a global presence. We can achieve that by creating additional hosted services in other parts of the world.

There are currently 6 Windows Azure data centers to choose from: 2 in North America, 2 in Europe, and 2 in Asia. We’ll plan on running our solution in two additional places: Western Europe (Amsterdam) and East Asia (Hong Kong).
To streamline the work we need to do in this step, it’ll work out best if we create the hosted services now in the Windows Azure portal (noting their names and production URLs) but do not deploy our solution to them just yet. We need to note the production URLs for our hosted services. In our example, they are responsive-us.cloudapp.net, responsive-europe.cloudapp.net, and responsive-asia.cloudapp.net. Your names will be different, as they must be unique.

A decision to make at this point is whether to create three separate editions of your web site project. If you will be customizing content based on location (for example, using different languages or content for each locale) or creating a separate database for each data center (which would be necessary in a real setting for performance), you may want to split out the web projects separately or do the equivalent with clever build configuration. In our sample code we have simply made three copies of the project (named /us, /europe, and /asia), one for each data center.
Configuring Traffic Management
A nice feature of Windows Azure is the Traffic Manager service (currently in Community Technology Preview), which will allow us to have a single .com address for our deployment even though it will exist in 3 data centers on 3 continents.

To set up the traffic manager, we use the Windows Azure portal, choosing a unique name prefix for the Traffic Manager. In our example that name is responsive, making the Traffic Manager endpoint http://responsive.ctp.trafficmgr.com. This will ultimately be the endpoint we can use to access Responsive Tours regardless of location. We then enroll each of our three hosted services (US, Europe, Asia). Of course the hosted services aren’t deployed yet, but we’re doing this now because we’ll need the Traffic Manager URL for configuring security.

We get to choose a policy in our Traffic Manager configuration (performance, failover, or round-robin).  The best choice for this scenario would be Performance-based routing based on location, but we’re going to use Round Robin in our example since it is a demo, to prove that as you access the site at different times you will in fact be routed to different data centers. In our HTML code, we’ve changed the footer text of each site to indicate which location it is in so we will have an easy way to detect that when we visit the site.
Updating Access Control Service to Support the New Hosted Services

The Access Control Service we are using for authentication needs to be configured to allow the additional data centers. You might think we need to identify each hosted service as an additional Relying Party, but actually all we have to do is identify the Traffic Manager endpoint as an RP.

One other change we need to make is in the Windows Identity Foundation configuration in the Web.config file of our web project(s). In the wsFederation element, we need to set the realm to the Traffic Manager endpoint.

<federatedAuthentication>
  <wsFederation passiveRedirectEnabled="true" issuer="https://[MY-ACS-NAMESPACE].accesscontrol.windows.net/v2/wsfederation" realm="http://[MY-TRAFFIC-NAME].ctp.trafficmgr.com/" requireHttps="false" />
  <cookieHandler requireSsl="false" />
</federatedAuthentication>


Enabling Content Delivery Network for Image Blobs

Our promotional images are residing in Blob Storage in the South Central US Data Center. We can enable efficient worldwide access through the Windows Azure Content Delivery Network. The CDN will use a worldwide network of 24 edge cache servers to serve images with high performance based on locale.

Configuring the CDN for our storage account in the Windows Azure portal provides us with a special CDN endpoint for our images. We change our view pages to use the new endpoint.

<!-- begin - homepage promos -->
<div class="home_promo_container">
 <div class="home_promo">
  <div class="home_promo_content" style="background-image:url(http://responsive.blob.core.windows.net/images/@(ViewBag.Promos["1"].ImageURL));">
   <h2 data-bind="text: PromoTitle1"></h2>
   <p  data-bind="text: PromoText1"/>
   <a class="button" href="#">Learn more &raquo;</a>
  </div>
 </div>
 <div class="home_promo">
  <div class="home_promo_content" style="background-image:url(http://responsive.blob.core.windows.net/images/@(ViewBag.Promos["2"].ImageURL));">
   <h2 data-bind="text: PromoTitle2"></h2>
   <p  data-bind="text: PromoText2"/>
   <a class="button" href="#">Learn more &raquo;</a>
  </div>
 </div>
 <div class="home_promo">
  <div class="home_promo_content" style="background-image:url(http://responsive.blob.core.windows.net/images/@(ViewBag.Promos["3"].ImageURL));">
   <h2 data-bind="text: PromoTitle3"></h2>
   <p  data-bind="text: PromoText3"/>
   <a class="button" href="#">Learn more &raquo;</a>
  </div>
 </div>
 <div class="clear_both"></div>

Deploying the Solution Worldwide

Now it’s time to deploy our hosted services to the US, European, and Asian data centers. As in Step 6, we can use Visual Studio to package and publish the solutions. When we’re done, we should see 3 deployments ready in the Windows Azure portal.

Once we’ve deployed all 3 hosted services, we’re in a position to serve a worldwide audience.

Testing the Global Site

Now we’re ready to see our mobile and global solution work in its global deployment. All we have to do is visit our Traffic Manager endpoint with a desktop browser, tablet, or phone. In our example the endpoint is http://responsive.ctp.trafficmgr.com. The site comes up just as we expect it to, and as we do this from multiple sessions and devices we can see that the location (shown in the page footer) varies between South Central US, Western Europe, and East Asia. Note, you may get an occasional hiccup because we are not routing traffic based on location.


Friendly DNS
One last thing we can do is give our web site a friendly DNS name. We’ll do this by purchasing the domain responsive-tours.com from GoDaddy. Now you can access the global site at http://responsive-tours.com. There are a number of ways we can forward the domain, we’ll deliberately choose here to do simple forwarding--so you can see the URL change when you visit the site  to show the Windows Azure Traffic Manager is at work.

Summary
In Step 7 we enabled the CDN for edge caching of blob images, deployed the solution globally to 3 continents, and used the Windows Azure Traffic Manager to manage traffic. Our site now has the following functionality:

• Embodies responsive web design and runs on desktops, tablets, and phones.
• Uses HTML5 and open standards on the web client
• Uses the Microsoft web platform on the web server.
• Provides server-side dynamic content (promotional items)
• Provides client-side dynamic content (Bing Maps)
• Is set up for Windows Azure Compute
• Can authenticate against web identities
• Is hosted in Windows Azure Compute
• Stores images in Windows Azure Blob Storage
• Stores promotional content in SQL Azure Database
• Uses Content Delivery Network for worldwide image caching
• Manages global traffic across 3 data centers on 3 continents

In this series we’ve seen the power that comes from combining HTML5, open standards, mobile devices, and responsive web design on the front end with the Microsoft web platform and Windows Azure cloud computing on the back end: true “mobile and global” web/cloud solutions that truly run anywhere and everywhere.

Mobile & Global with HTML5, MVC & Windows Azure, Step 6: Cloud-Deployed

In this series of posts we’re progressively demonstrating a mobile and global sample, Responsive Tours. The source code for all 7 steps is on CodePlex at http://responsivetours.codeplex.com.

Here in Step 6 of 7 we’re going to deploy the solution to a Windows Azure data center. In this step we will:
• Migrate the local SQL Server database to a SQL Azure database in the cloud
• Migrate the promotional item image files to Blob Storage
• Update how we handle session cookies to be compatible with Windows Azure
• Package the application and publish it to the Windows Azure Compute Service
• Configure the Access Control Service for the hosted service

Migrating the Database to SQL Azure
Since Step 3 we’ve been using dynamic content for promotional items, driven by a Promotions table in a SQL Server database. Now we’ll need to move that over to a SQL Azure database in the cloud. To do that, we use the Windows Azure portal to create a virtual database server in the cloud. This involves selecting a data center, specifying an admin username/password, and setting up firewall rules.


Once the database server is created we need to create a Tours database. We can go with the smallest size (1GB) since we’re only storing a small amount of data in this sample.


From this point, working with the database is much like working with SQL Server. We have the choice of using familiar tools like SQL Server Management Studio or managing design and data through the SQL Azure portal. We need to design the Promotions table and migrate the promotional item records we previously created in a local database.



There’s one last item to attend to on the database. With our database now in SQL Azure, we must change the connection string in the web project’s Web.config to reference the cloud database.

  <connectionStrings>
    <add name="Tours" connectionString="Data Source=[MY-SQL-AZURE-SERVER].database.windows.net;Initial Catalog=Tours;UID=[MY-SQL-AZURE-USER]@[MY-SQL-AZURE-SERVER];PWD=[MY-SQL-AZURE-PASSWORD];" />
  </connectionStrings>
 
Migrating Images to Blob Storage

Up till now our promotional images have been part of the web project. To make them truly dynamic like the rest of the promotional content we should be able to change them out quickly and easily. We can achieve this by relocating the image files to Windows Azure Blob Storage, where they can be accessed as Internet URLs if we set appropriate permissions.

We first need to create a Windows Azure storage account in the Windows Azure portal. We’ll need to capture the storage account’s unique name and a storage key.



With the storage account created, we can create a container (allowing public read access) and upload our promotional images to it. Use a tool like Cerebrata Cloud Storage Studio or Azure Storage Explorer for this.


With the images in a new location we must change the promotional item markup on our HTML pages to match. We use the blob container’s URL format, [STORAGE-ACCOUNT-NAME].blob.windows.net/[CONTAINER-NAME]/[BLOB-NAME].
<!-- begin - homepage promos -->
<div class="home_promo_container">
 <div class="home_promo">
  <div class="home_promo_content" style="background-image:url(http://responsive.blob.core.windows.net/images/@(ViewBag.Promos["1"].ImageURL));">
   <h2 data-bind="text: PromoTitle1"></h2>
   <p  data-bind="text: PromoText1"/>
   <a class="button" href="#">Learn more &raquo;</a>
  </div>
 </div>
 <div class="home_promo">
  <div class="home_promo_content" style="background-image:url(http://responsive.blob.core.windows.net/images/@(ViewBag.Promos["2"].ImageURL));">
   <h2 data-bind="text: PromoTitle2"></h2>
   <p  data-bind="text: PromoText2"/>
   <a class="button" href="#">Learn more &raquo;</a>
  </div>
 </div>
 <div class="home_promo">
  <div class="home_promo_content" style="background-image:url(http://responsive.blob.core.windows.net/images/@(ViewBag.Promos["3"].ImageURL));">
   <h2 data-bind="text: PromoTitle3"></h2>
   <p  data-bind="text: PromoText3"/>
   <a class="button" href="#">Learn more &raquo;</a>
  </div>
 </div>
 <div class="clear_both"></div>

Updating Session Cookie Handling for Windows Azure
By default, our WIF-enabled ASP.NET MVC3 application is encrypting session cookies using the Data Protection API (DPAPI). DPAPI is not compatible with Windows Azure, so we need to add some code to encrypt cookies with RSA using a certificate. We do this in global.asax.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.IdentityModel.Tokens;
using Microsoft.IdentityModel.Web;
using Microsoft.IdentityModel.Web.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace html5_mvc_razor
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        /// Retrieves the address that was used in the browser for accessing 
        /// the web application, and injects it as WREPLY parameter in the
        /// request to the STS 
        /// </summary>
        void WSFederationAuthenticationModule_RedirectingToIdentityProvider(object sender, RedirectingToIdentityProviderEventArgs e)
        {
            //
            // In the Windows Azure environment, build a wreply parameter for  the SignIn request
            // that reflects the real address of the application.
            //
            HttpRequest request = HttpContext.Current.Request;
            Uri requestUrl = request.Url;
            StringBuilder wreply = new StringBuilder();

            wreply.Append(requestUrl.Scheme);     // e.g. "http" or "https"
            wreply.Append("://");
            wreply.Append(request.Headers["Host"] ?? requestUrl.Authority);
            wreply.Append(request.ApplicationPath);

            if (!request.ApplicationPath.EndsWith("/"))
                wreply.Append("/");
            e.SignInRequestMessage.Reply = wreply.ToString();
        }


        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

            routes.MapRoute(
                "Map", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Map", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            FederatedAuthentication.ServiceConfigurationCreated += OnServiceConfigurationCreated;
        }

        void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
        {
            //
            // Use the <serviceCertificate> to protect the cookies that are
            // sent to the client.
            //
            List<CookieTransform> sessionTransforms =
              new List<CookieTransform>(new CookieTransform[] {
                new DeflateCookieTransform(), 
                new RsaEncryptionCookieTransform(e.ServiceConfiguration.ServiceCertificate),
                new RsaSignatureCookieTransform(e.ServiceConfiguration.ServiceCertificate) });
            SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
            e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
        }

    }
}


If we were using SSL for this site we'd use the SSL certificate. Since we're not, we upload our own certificate to the Windows Azure portal for this purpose and specify it in our Web.config.

  <microsoft.identityModel>
    <service>
      <audienceUris>
        <add value="http://MY-SERVICE-NAME.cloudapp.net/" />
      </audienceUris>
      <federatedAuthentication>
        <wsFederation passiveRedirectEnabled="true" issuer="https://[MY-ACS-NAMESPACE].accesscontrol.windows.net/v2/wsfederation" realm="http://MY-SERVICE-NAME.net/" requireHttps="false" />
        <cookieHandler requireSsl="false" />
      </federatedAuthentication>
      <applicationService>
        <claimTypeRequired>
          <!--Following are the claims offered by STS 'https://[MY-ACS-NAMESPACE].accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
          <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
          <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
          <!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
          <!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
        </claimTypeRequired>
      </applicationService>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="ED98B07917624AEE89EDDC086589A6149ADE6859" name="https://[MY-ACS-NAMESPACE].accesscontrol.windows.net/" />
        </trustedIssuers>
      </issuerNameRegistry>
      <serviceCertificate>
        <certificateReference storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="308EFDEE6453FFF68C402E5ECEEE5B8BB9EAA619"/>
      </serviceCertificate>
      <certificateValidation certificateValidationMode="None" />
    </service>
  </microsoft.identityModel>


Publishing the Site to Windows Azure Compute
Back in Step 4 we set the site up for hosting in Windows Azure Compute but up till now we’ve only been running it locally in the Windows Azure Simulation Environment. Now we want to deploy the solution to a Windows Azure Data Center.

We first create a Hosted Service in the Windows Azure management portal. In our example we chose the South Central US data center and call the service responsive, which means its production URL will be http://responsive.cloudapp.net. Your name will be different as they must be unique.

After setting the number of VM instances in the Windows Azure project’s .cscfg file (to 2, the minimum for high availability), we are almost ready to deploy but we need to make one change to the project first relating to security.

Our impending change in deployment location will cause ACS authentication to fail unless we change our web project's configuration settings (which we'll do now) and ACS configuration (which we'll do later in this post). In the web project's Web.config file, change the realm attribute in the wsFederation element to reflect the new production URL.

<federatedAuthentication>
  <wsFederation passiveRedirectEnabled="true" issuer="https://[MY-ACS-NAMESPACE].accesscontrol.windows.net/v2/wsfederation" realm="http://MY-SERVICE-NAME.net/" requireHttps="false" />
  <cookieHandler requireSsl="false" />
</federatedAuthentication>


We can now proceed to package and deploy our solution, which can done directly from Visual Studio. We do this in Solution Explorer by right-clicking the Windows Azure project (ResponsiveSite.Azure) and selecting Publish. A wizard then guides us through the publishing action, which packages up the solution and its configuration, uploads both to the cloud, allocates VM instances, and deploys an image to them that includes our web site.

The Publish action can take 10-20 minutes, and when complete we’ll see the hosted service showing a status of Ready in the Windows Azure management portal.


Configuring Access Control Service for the Hosted Service
In Step 5 we configured the Windows Azure Access Control Service to allow our local development endpoint as a Relying Party. Now we need add another RP, our hosted service endpoint (http://responsive.cloudapp.net in our example) using the Windows Azure portal.

Running the Solution in the Cloud
All that remains now is try things out. Our hosted service in the cloud is accessed at a production URL based on the unique name we chose when the hosted service was created – http://[SERVICE-NAME].cloudapp.net. Our example deployment is at http://responsive.cloudapp.net. When we access this URL, we see the hosted service respond, now using a database in the cloud for promotional content and promotional images served up from blob storage. As in the past we first have to sign in with a web identity. In short, the site looks and acts like it has in previous steps except that it is now running in a Windows Azure data center accessible on the Internet.
Summary
In Step 6 we moved to the cloud, by hosting our web site in Windows Azure Compute, our promotional images in Blob Storage, and our promotional content in SQL Azure Database. Our site now has the following functionality:
• Embodies responsive web design and runs on desktops, tablets, and phones.
• Uses HTML5 and open standards on the web client
• Uses the Microsoft web platform on the web server.
• Provides server-side dynamic content (promotional items)
• Provides client-side dynamic content (Bing Maps)
• Is set up for Windows Azure Compute
• Can authenticate against web identities
• Is hosted in Windows Azure Compute
• Stores images in Windows Azure Blob Storage
• Stores promotional content in SQL Azure Database
We've come pretty far, but there's more we can do. In the next step, we'll deploy the application globally to multiple Windows Azure data centers around the world.