Scott Hanselman

Stop Doing Internet Wrong.

June 14, '13 Comments [151] Posted in Mobile | Musings
Sponsored By

Some days...some days it's frustrating to be on the web. We're compiling C++ into JavaScript and running Unreal in the browser but at the same time, here in 2013, we're still making the same mistakes. And by we, I mean, the set of web developers who aren't us, right Dear Reader? Because surely you're not doing any of these things. ;)

All of these are solvable problems. They aren't technically hard, or even technically interesting. I consider these "will-required" problems. You need the knowledge that it's wrong and the will to fix it. As users - and web developers - we need to complain to the right people and help fix it.

Redirecting a deep desktop link to a mobile home page

Google has decided that the practice of taking perfectly good deep links like foo.com/something/deep, detecting a mobile device, then redirecting to m.foo.com is user-hostile. In fact, the GoogleBot is going to declare these "faulty redirects" and ding sites in the search result ranking. Stated simply:

Avoiding irrelevant redirects is very easy: Simply redirect smartphone users from a desktop page to its equivalent smartphone-optimized page. If the content doesn't exist in a smartphone-friendly format, showing the desktop content is better than redirecting to an irrelevant page.

For example, if I want to go to the http://www.mcmenamins.com/Pubs page, but I do it on mobile, they ALWAYS redirect me to /mobile. Always. Even though I have a quad-processor pocket supercomputer with gigs of space I've still surfing a second-class internet.

image image

I don't want your crappy app

That means you Quora. I am in my browser, unless I'm going to the App Store, let's assume if I'm in the browser, I want to be on the web.

You suck Quora

Giant Interstitial Ads

I'm looking at you, Forbes.com. I GET IT. YOU HAVE ADS.

Interstitial Ads are Evil

Stay classy.

Labels for Input Forms

I hate seeing a checkbox and only being able to click on that exact checkbox.

<p>Which fruit would you like for lunch?</p>
<form>
<input type="radio" name="fruit" id="banana" />
<label for="banana">Banana</label>
<input type="radio" name="fruit" id="None" />
<label for="none">None</label>
</form>

It's so easy to just associate a label with an input. Please do  it, then we can all have something larger to click on.

Breaking Hyperlinks

We're still doing this. Haven't we learned that Cool URIs Don't Change? It was true in 1998 when that was written and it's true now. The web as we know it was created in 1990 and made truly open in 1993 and the link to the First Web Page (yes, Capital Letters) is still http://info.cern.ch/hypertext/WWW/TheProject.html. I love that they've done the work to keep that link alive.

There's just no excuse for this. With .htaccess files and web.config files, maintain a list of redirects and do your best to test them. Maintaining deep and complex links can be complex, but if you're companyname.com/about link dies because you switch from PHP to Rails, there's just no excuse for that. I'm your User and I have always typed /about. Don't' give me a To Do like "Update your bookmarks!" I didn't come here for a To-Do, I came her for your damn about page. YOU figure it out.

image

Click the Flag that represents your Language

I've often been asked to "select my language" from a list of country flags, and ended up clicking on the Union Jack to represent "English." I'm sure the actual English don't appreciate an American declaring they speak English. ;)

Nothing says pick a language like all the United Nations Flags

but I know I'm not the only one who realizes that a Flag is a lousy representation of a language, especially since your browser is announcing what languages you speak with every web request.

Accept:text/html
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8

There can be a whole list of languages in the Accept-Language header, in the order the user prefers them!  Use that data, it's there for you to use.

You know my Zip Code, why am I entering my State?

For folks living in the states, we're always asked to enter our postal code (ZIP code) and our city and state, even though there are dozens of great APIs and Databases that can give you that information.

Don't make me enter my state

The meta-point is this: If you can reliably determine something from the user (language, location, country, preference) without invading their privacy, do it! Save them a little time!

Resizing Giant Images with width and height attributes

Perhaps take a moment and remind your boss that the 6 megapixel photo that he or she took with their new Canon EOS is not a good background image for your corporate site...especially if it's a 4 megabyte JPGs.

Oh, that's OK, we can just <img src="bigassfile.jpg" width="100" height="100"> and that will make it smaller. No, that just downloads the giant file and then makes your browser do the work to resize it on the client.

Big ass picture

Resize first, and squish often. Also run all your PNGs through PNGGauntlet or PNGOut.

Serving pages from both www. and naked domains

If you've got example.com/something AND www.example.com/something both serving up the same content, consider "canonicalizing" your URLs. You can do this with rel="canonical" in your META tags, but that only hides the problems and makes the Googlebot happy. Instead, why not PICK ONE and serve a 301 redirect to the other? Did you know that there are rules built into IIS7 that will set this up for you? You can even remove your .aspx extension if that makes you happy. You can do it!

image

The same is true if you do the same thing for / and /default.html. Pick one if you can, and redirect the other.

<system.webServer>
<rewrite>
<rules>
<rule name="CanonicalHostNameRule1" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" matchType="Pattern" pattern="^hanselman\.com$" ignoreCase="true" negate="false" />
</conditions>
<action type="Redirect" url="http://www.hanselman.com/{R:1}" redirectType="Found" />
</rule>
<match url="blog/default.aspx" />
<action type="Redirect" url="blog/" redirectType="Found" />
</rule>
<rules>
<rewrite>
<system.webServer>

Others?

What are some great examples that you think Break The Internet...but that are easily fixed if we have the will?


Sponsor: Big thanks to RedGate for sponsoring the feed this week! Check out Deployment Manager – app deployment without the stress. Deploy .NET code & SQL Server databases in one simple processfrom a web-based UI. Works with local, remote and cloud servers. Try it free.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Making a switchable Desktop and Mobile site with ASP.NET MVC 4 and jQuery Mobile

October 1, '12 Comments [27] Posted in ASP.NET | ASP.NET MVC | Javascript | Mobile
Sponsored By

I really enjoy working on and thinking about mobile websites. There's something about making an experience great on a pocket supercomputer phone that is so much more satisfying than a desktop. I actually got this blog looking nice on mobile devices back in 2006 when nobody was mobile except doctors and, well, all of us techies here on the blogs.

I've talked about the importance of a good mobile site before in posts like Create a great mobile experience for your website today. Please. However, some folks had asked me if I'd do a post on how to do a combination Desktop and Mobile site using ASP.NET MVC similar to the examples I used in my talks in Russia on mobile earlier this year. (There's video of those ASP.NET mobile presentations available)

When you start Visual Studio 2012 and go File | New ASP.NET MVC 4 app, there's an Internet Application template and a Mobile template. One gets you a standard desktop site - although with responsive design elements so it works on small screens - and the other gets you a jQuery Mobile application meant primarily for phones and tablets. Let's make one that switches between both.

We will do a small site in ASP.NET MVC for the Desktop, do some quick DB access, add jQuery Mobile and a View Switcher switch back and forth. I'll be using the Electric Mobile Studio from Electric Plum to simulate an iPhone. You can get a 7 day trial or you can get the Lite version of the Electric Plum Mobile Simulator with WebMatrix 2.

Quick CRUD Example

First, a model for DVDs.

public class DVD
{
public int ID { get; set; }
public string Title { get; set; }
public int Year { get; set; }
public Rating rating { get; set; }
}

public enum Rating
{
G, PG, PG13, R
}

Next, I scaffold out the Index, Create, Edit, Delete, etc. Unfortunately scaffolding doesn't do Enums (I'm sad) for my Movie Ratings so I add EditorFor() calls to my Create and Edits, and update my Index.

<div class="editor-label">
@Html.LabelFor(model => model.rating)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.rating)
@Html.ValidationMessageFor(model => model.rating)
</div>

Shared/EditorTemplates/Rating.cshtmlI could have used DropDownList I suppose, but I have always found that helper confusing. Instead, I'll create a Rating.cshtml that makes a dropdown. I could change this at some future point to be fancier and not even use a DropDown.

Aside: How do you guys usually handle Enums? I've seen it done a few ways. I'd like this to be more generic, but here's what I did for the Rating editor Template. Note the nullable ? as it has to work for both Create and Delete

@model MyApp.Models.Rating?
@using MyApp.Models

<select id="@ViewData.TemplateInfo.GetFullHtmlFieldId("")" name="@ViewData.TemplateInfo.GetFullHtmlFieldName("")">
@foreach (int rating in Enum.GetValues(typeof(Rating))) {
var name = Enum.GetName(typeof(Rating), rating);
<option value="@name" selected="@(Model.HasValue ? (int)Model == rating : false)">@name</option>
}
</select>

OK, so there's a basic Desktop CRUD app.

Editing a DVD List of DVDs

Making it Mobile

iPhone in the Visual Studio browser menuAs I mentioned, you've probably noticed when making an ASP.NET MVC application that you can choose a Mobile template with jQuery Mobile along with the standard responsive "desktop" Internet Application. However, there isn't a switchable template. That is, one that is the regular template on Desktops but switches to jQuery Mobile (or KendoUI, or whatever makes you happy) on mobile devices.

Using NuGet, install the jQuery.Mobile.MVC package. You can either right click on References, select Manage NuGet Packages, or you can use the Package Manager Console and type:

install-package jQuery.Mobile.MVC

This package will automatically bring in jQuery Mobile as well as:

  • A ViewSwitcher partial view and supporting Controller
    • This is what lets us switch manually between Desktop and Mobile
  • A basic _Layout.Mobile.cshtml and supporting stylesheet
  • A BundleMobileConfig.cs used with ASP.NET Optimization (the bundler for CSS and JS)

NOTE: There's nothing jQuery Mobile specific about the ViewSwitcher or the techniques here. You can happily change this package for any other popular mobile framework.

We start by adding the new Bundles in the Global.asax:

BundleConfig.RegisterBundles(BundleTable.Bundles);
BundleMobileConfig.RegisterBundles(BundleTable.Bundles); // <-- ADD THIS ONE

Since I installed the Electric Plum iPhone simulator, I'll select it from my browser dropdown in Visual Studio and run my app and navigate to /DVD.

The system sees that I'm on a mobile device and rather than using _Layout.cshtml it uses _Layout.mobile.cshtml.

This doesn't really look nice for a few reasons. First, I don't like the default style. Just go into _Layout.Mobile.cshtml and change the data-theme attribute to a value that makes you happy. I changed it from theme a to theme b.

Basic App in an iPhoneSame baisic app with a light background

Second, while I'm using the _Layout.Mobile.cshtml I'm still using the desktop Index.cshtml which looks lousy when displayed in the mobile layout. Remember that I only have an single Index.cshtml. If I had an Index.mobile.cshtml the system would use that page instead when rendering on a mobile device.

I can make an Index.Mobile.cshtml with simpler markup, change the table into an unordered list and add some jQuery Mobile specific attributes like this:

@model IEnumerable<MvcApplication2.Models.DVD>
@{
ViewBag.Title = "My DVDs";
}
<ul data-role="listview" data-filter="true" >
@foreach (var item in Model) {
<li>
<a href="@Url.Action("Details", new { item.ID })">
<h2>@Html.DisplayFor(modelItem => item.Title)</h2>
<p>@Html.DisplayFor(modelItem => item.Year) - @Html.DisplayFor(modelItem => item.rating)</p>
</a>
</li>
}
</ul>

Note the data-role and very basic elements like <ul>, <li>, <h2< and <p>. I also added the client side data-filter attribute to get some nice client-side searching. Hit refresh and now it's starting to look like a real mobile site.

See the ViewSwitcher at the top there? That's a partial view called _ViewSwitcher.cshtml.

A lovely jQuery Mobile example list of DVDs

By default it will let you switch back and forth between Desktop and Mobile but only on mobile devices. Why? Check out the first line of code:

@if (Request.Browser.IsMobileDevice && Request.HttpMethod == "GET")
{
<div class="view-switcher ui-bar-a">
@if (ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice)
{
@: Displaying mobile view
@Html.ActionLink("Desktop view", "SwitchView", "ViewSwitcher", new { mobile = false, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
else
{
@: Displaying desktop view
@Html.ActionLink("Mobile view", "SwitchView", "ViewSwitcher", new { mobile = true, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
</div>
}

That first line checks if it's a mobile device making the request. Just surround that with /* comments */ and you'll be able to view and debug mobile layouts with your desktop browser without faking User Agents.

So far we've been talking about "mobile" and creating files with "mobile" in their file names and it's all been magic. Turns out we have lots of control over these things. Perhaps we want more than Index.mobile.cshtml but also, perhaps, Index.iPhone.cshtml and Index.WP7.cshtml.

Create lines like these in your Global.asax:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("WP7") {
ContextCondition = ctx => ctx.GetOverriddenUserAgent().Contains("Windows Phone OS")
});
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone") {
ContextCondition = ctx => ctx.GetOverriddenUserAgent().Contains("iPhone")
});

You can affect the DisplayMode collection (of which "mobile" is one) by inserting in your own. Here I'm making two more specific mobile ones but you don't need to do mobile things to change DisplayModes.  A DisplayMode could be right to left for RTL languages, or "gold" and "bronze" for customers or whatever makes you happy, based on the ContextCondition you evaluate on each request.

There's lots of choices and you've got the flexibility to do things however you like. I'll show an example of a totally offline iPhone site using cache.manifest in a future post.

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

WebMatrix 2 - Front End Web Developers take note (ASP.NET, PHP, node.js and more)

September 19, '12 Comments [32] Posted in ASP.NET | ASP.NET MVC | Mobile | Open Source | WebMatrix
Sponsored By

Did you notice the release of WebMatrix 2? WebMatrix is a free, lightweight web development tool introduced in 2010. It's focus is on simplifying the web development experience for ASP.NET and PHP, and more recently node. Rob Conery actually turned me onto WebMatrix and we use it for the This Developer's Life Podcast website. I recommend it for students, designers, and web pros that either don't need or don't want the whole Visual Studio experience. It's also a nice companion to Sublime Text 2. There's even Git extensions, LESS, easy deployment and more for the front end developer.

If you want to download WebMatrix 2 and get started, it's free. It will use the Web Platform installer to install and you can use it along site VS if you want, or all by itself on any machine. It's actually a great companion for sites like Codecademy. Watch Vishal and Brady give a brief overview of the new features in WebMatrix 2 on Channel 9.

HTML, JavaScript, & CSS

The new Web Matrix 2 HTML editor adds useful stuff like code outlining, tag completion, formatting, syntax validation, and IntelliSense for HTML5.

WebMatrix has intellisense

The JavaScript editor has a better auto-formatting experience, and IntelliSense:

Intellisense in JavaScript

The new CSS editor in WebMatrix shares a lot with the new features included in the Visual Studio 2012 CSS editor. There's the color pickers, IntelliSense for the latest CSS3 modules, and better language support:

New CSS Color Picker

Here's the new hotness that I'm digging. For those who want to go beyond basic CSS, you can get preprocessors like LESS and Sass. Folks have been asking for better preprocessor support, and WebMatrix 2 includes support for LESS and Sass. The LESS editor supports the same great CSS features, along with IntelliSense for custom variables and mix-ins:

LESS

Like I said, there's lots of stuff built in. It's kind of a playground for features that will move their way between VS and WebMatrix.

File New Item

 

Related Videos: To learn more about the new CSS editors in WebMatrix 2, check out these short videos:

Razor with ASP.NET Web Pages 2

As I mentioned, Rob and I use ASP.NET Web Pages on This Developers Life. It's kind of like PHP in that you can mix code and HTML in one file except the syntax is Razor and the language is C#. The combination of WebMatrix and ASP.NET Web Pages is an easy way to get started with web development. Make a file, start writing HTML then add some code. Later you can graduate (if you want) to ASP.NET MVC. In addition to improving the web editors, WebMatrix 2 adds IntelliSense for Razor, C#, and Visual Basic:

Intellisense in Razor

WebMatrix 2 and ASP.NET Web Pages 2 include many built in helpers that make it easy to do common things like send email, post a tweet, or resize an image. If the built in packages and helpers in Web Pages 2 aren't enough for what you want, WebMatrix now also supports installing libraries and helpers directly from NuGet!

WebMatrix pulls from NuGet

Video: Justin and Vishal build an image sharing site with ASP.NET Web Pages and Windows Azure Web Sites.

Web Development with PHP

For applications that use PHP, there is a brand new PHP editor that features IntelliSense, code collapsing, and PHP 5.4 support. There's docs showing up inline there as well.

PHP Editor has intellisense

Video: Thao and Brady show the new PHP features in WebMatrix 2.

Web Development with Node.js

I have mentioned Azure before on the site and now that it supports Git deployment as well as node.js I've been paying more attention to it. One of the goals of WebMatrix 2 and Windows Azure is to make it easier to develop and host open source applications on the Windows platform. December brought support for node.js to Windows Azure. While Azure is making it easier to host node.js applications in cloud, WebMatrix 2 is aiming to improve the development experience. Out of the box are multiple project templates, IntelliSense, and support for several new languages. There's even an Express.js template.

Node.js in WebMatrix

Along with IntelliSense for the core modules built into node.js, WebMatrix also provides IntelliSense for third party modules installed via the Node Package Manager (NPM). This makes using things like the Windows Azure npm package much simpler:

Azure in node

Node.js uses a variety of rendering engines for writing views. I am told there are more planned and today WebMatrix 2 includes support for both Jade and EJS. I like Jade, myself.

Jade in WebMatrix

I'm not sure where you fall in the Great CoffeeScript debate, but some developers like to write their applications using CoffeeScript. WebMatrix supports that also.CoffeeScript in WebMatrix

Video: Justin and Vishal show new Node.js features in WebMatrix 2.

Streamlined Database Tools

The database tools in WebMatrix work with SQL Server, SQL Compact Edition, and MySQL as well.:

MySQL in WebMatrix. Scandalous.

When you're ready to publish your application, WebMatrix 2 will include your Database along with the list of changed files:

Publishing a database

Video: Learn more about managing databases in WebMatrix 2.

Mobile Web Development

You can integrate an iPhone or iPad simulator into Visual Studio, as I've blogged before. WebMatrix 2 adds an extensible model for adding new browsers, and also includes support for simulating Windows Phone 7 as well as the iPhone and iPad via a partnership with Electric Plum. Full disclosure: I love the Electric Plum guys. So nice.

iPad and iPhone

The templates included in WebMatrix 2 look nice on mobile devices out of the box since they use a combination of responsive design and jQuery Mobile.

iPhone

iPad

For those working with jQuery Mobile, there's included IntelliSense for custom data-* attributes.

jQuery Mobile

Videos: For more examples of using the mobile capabilities in WebMatrix 2, check out these videos:

The Application Gallery

There's a metric pile of Open Source projects in the Application Gallery so you can get WordPRess or Umbraco or whatever running just by File | New. Takes just a minute or two. ASP.NET and PHP apps all live together.

Web Gallery

After you install your application, there is now a customizable dashboard, designed specifically for your app type. That means that WebMatrix knows you're using WordPress and will show you stuff that WordPress folks care about.

The team has worked with the community to provide a customizable experience for many of the applications in the gallery. The dashboard provides a series of links and resources that make it easier to learn more about your app.

WordPress

For many of the applications in the gallery, there are a core set of files that should not be changed. WebMatrix will even warn users of these files.

Don't touch that file!

While customizing an application, WebMatrix 2 also includes application-specific IntelliSense. This is useful when you're just getting started working with a new application:

clip_image025

Windows Azure & Remote Editing

You can also create and deploy apps directly from the Windows Azure portal. So, rather than starting from an app on your machine, you can create it in Azure, get it running then...

clip_image027

...you've got remote site editing. After creating an application in the cloud, you can directly open a remote view into your site. This is great for making edits on the go. I'd personally use Git or source control, but I do have to admit I have a few sites that are just up there as brochures and aren't formally deployed. This feature is nice for those sites.

Remote Editing

For users using Windows Azure, the management portal allows users to install WebMatrix and open their site by clicking on a button in the command bar. This will download the site and install any required dependencies. This means once you've made your site in Azure, you can open it in WebMatrix, it will install, figure out your app (if it's in the gallery) and open it.

WebMatrix from Azure

When you've finished making your changes, the same publish command will only push the files that have changed back to your host.

WordPress editing

Video: Watch Faith customize a WordPress site and deploy it to Windows Azure.

Extension Gallery

If you're interested in extending WebMatrix 2, there is a new SDK that allows anyone to add new features or functionality. Many of the things in this post (including the mobile emulators!) are actually extensions that ship with WebMatrix 2.

Extensions

When you're ready to start building, check out the extension gallery. It has a list of extensions currently available, along with documentation on how to make your own extension.

Video: Watch Walter show you how to build a basic extension and publish it to the WebMatrix 2 extension gallery.

Wrapping it up

This release of WebMatrix 2 supports making apps ASP.NET, PHP, and node.js. You can download WebMatrix free. Let the team know if you have any feedback. Thanks Justin for helping me with some details and screenshots on this post!

Related Links


Sponsor: Thanks to DevExpress for sponsoring this week's feed. Multi-channel experiences made easy: Discover DXTREME. Delight your users with apps designed expressly for their device. DXTREME, multi-channel tools build stunning apps across devices & optimize for the best of each platform, from Win8 to the iPhone. And, the powerful HTML5, CSS and JavaScript tools in DXTREME also build interactive web apps.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Introducing ASP.NET FriendlyUrls - cleaner URLs, easier Routing, and Mobile Views for ASP.NET Web Forms

September 9, '12 Comments [91] Posted in ASP.NET | Javascript | Mobile | Open Source | VS2012
Sponsored By

I've said before how surprised I am that more ASP.NET Web Forms developers don't use Routing to make their URLs prettier. If you don't want "foo.aspx" in your URL, then change it with Routes.MapPageRoute(). However, managing Routing Tables is a little tedious and most WebForms folks aren't used to the concept and don't want to invest the time.

I've also heard a number of ASP.NET Web Forms Developers express a little envy at how easy it is to make a site that has both desktop and mobile views using ASP.NET MVC. They like the idea of seeing an iPhone show up and showing a different  view while reusing logic as I've shown in my mobile talks before.

Let's solve both these problems with a new ASP.NET feature just pre-released today in alpha form on NuGet. My peer Damian Edwards and developer Levi Broderick along with QA by  Pranav and Anton have come up with a pretty awesome solution based on the original "Smarty Routes" idea from Eilon Lipton and the result is FriendlyUrls.

Install-Package Microsoft.AspNet.FriendlyUrls -pre

NOTE: If you've been paying attention to ASP.NET for the last few months you'll recognize this incremental useful but appropriately sized forward motion as being all part of the One ASP.NET master plan.

It's also worth noting that this FriendlyUrls NuGet package includes BOTH an ASP.NET 4.5 and ASP.NET 4 version so .NET 4 folks get love too.

FriendlyUrls Hello World Example

First, the obvious example. Bring up Visual Studio and File | New Project | New ASP.NET Web Forms Application. Now, from the Package Manager Console or from Manage NuGet Packages, install Microsoft.AspNet.FriendlyUrls. You'll need to "Include Prerelease" packages with -pre from the command line or via the dropdown in the UI.

Microsoft.AspNet.FriendlyUrls -pre shown in the UI

Be sure to read the readme.txt that pops up as you'll need to ensure that the FriendlyUrls routing gets called on application startup! I added this one line to my Application_Start:

RouteConfig.RegisterRoutes(RouteTable.Routes);

Here's the cool part. If I hit one of my existing links, like Contact.aspx, look what happened. See how the GET request for /Contact.aspx turned into a 301 redirect to /Contact?

/Contact.aspx turned into a 301 redirect to /Contact

If you have a Web Form called /Foo.aspx, you automatically get a /Foo route and can call your page like that! Hence, Microsoft.AspNet.FriendlyUrls.

Just by adding the one package and calling

routes.EnableFriendlyUrls();

in RouteConfig (this default came down with the NuGet package) my whole WebForms app loses its .ASPX extensions and gets reasonable defaults.

FriendlyUrls Advanced Sample

Get it? Ok, let's dig into some of the obvious next questions and some more advanced scenarios. How do I get values out of the URL? I'm used to Request.QueryString and Request.Form, but how do I get ahold of these URL segments?

Here's a Foo.aspx that I've visited via /Foo.

A basic Foo WebForms page

If I click "Click Me" the URL points to /Foo/bar/34.

Visiting /Foo/bar/34

NOTE: Be aware of the magic. It makes sense. If there was a 34.aspx in a folder called Bar in a folder called Foo, we would have used that file. There wasn't. If there was a file called Bar.aspx in a folder called Foo we would have used that. There wasn't. So, we used Foo.aspx and passed in the rest of the URL.

I can get the segments out like this:

<% foreach (var segment in Request.GetFriendlyUrlSegments()) { %>
<li><%: segment %></li>
<% } %>

UPDATE: One thing I forgot to mention was how to get the values out of the FriendlyURL. You can use things like [Form] and [QueryString] to model bind in WebForms. Now you can add [FriendlyUrlSegments] to get data out, like the ID in this example:

public SomeItem SomeItem_GetItem([FriendlyUrlSegments]int? id)
{
SomeItem item = db.SomeItem.Find(id);
return item;
}

They're sitting on the Request option. I did have to import the Microsoft.AspNet.FriendlyUrls namespace to have this extension appear.

<%@ Import Namespace="Microsoft.AspNet.FriendlyUrls" %>

Better yet, I can generate Friendly URLs without string concatenation!

<a href="<%: FriendlyUrl.Href("~/Foo", "bar", 34) %>">Click me</a>

Nice, eh? OK, let's make it mobile.

Mobile Routes with ASP.NET FriendlyUrls

When you bring down the NuGet package you'll also get a Site.Mobile.Master. If I visit them with the Electric Plum Mobile Simulator (iPhone) I see a default mobile page, automatically.

The Default Mobile Web Forms page in an iPhone

Ah, you see where this is going. I'll copy Foo.aspx to Foo.Mobile.aspx. I'll make a small change. I'll visit /Foo/bar/34 again except now I get the mobile master and the mobile foo, automatically.

image

What I want to support switching back and forth from Desktop to Mobile? Just add a ViewSwitcher control, also included.

<friendlyUrls:ViewSwitcher runat="server" />

Now I re-render and I get a "switch to mobile" and switch to desktop.

image

Now I can go back and forth between views and request a desktop site even when on mobile.

image

So basic mobile is nice but I might want very specific mobile views for iPhone, iPad, Opera Mobile, etc.

Super Advanced Mobile Routes for Specific Devices with ASP.NET FriendlyUrls

By default FriendlyUrls uses a class called WebFormsFriendlyUrlResolver but you can derive from this class and change its behavior however you like. Here's an example of a "DeviceSpecificWebFormsFriendlyUrlResolver" or, better yet, Mobile Friendly Urls for WebForms.

This derived URL resolver does just that, it resolves URLs to physical Web Forms pages. You'd then pass it into the overload of EnableFriendlyUrls(...);

IMPORTANT NOTE: This code is just a very early sample, there will be a more complete one released later.

public class DeviceSpecificWebFormsFriendlyUrlResolver : WebFormsFriendlyUrlResolver
{
private readonly IDictionary<string, string> _deviceUserAgentMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Opera Mobi", "OperaMobile" },
{ "iPhone", "iPhone" },
{ "iPad", "iPad" }
};

protected override IList<string> GetExtensions(HttpContextBase httpContext)
{
var extensions = base.GetExtensions(httpContext).ToList();
if (extensions.Contains(MobileAspxExtension, StringComparer.OrdinalIgnoreCase))
{
// Base has determined we should look for a mobile page, let's add device specific
// extension to the beginning.
var deviceSpecificSufffix = GetDeviceSpecificSuffix(httpContext);
if (!String.IsNullOrEmpty(deviceSpecificSufffix))
{
extensions.Insert(0, "." + deviceSpecificSufffix + AspxExtension);
}
}
return extensions;
}

protected override bool IsMobileExtension(HttpContextBase httpContext, string extension)
{
return base.IsMobileExtension(httpContext, extension) ||
_deviceUserAgentMap.Values.Any(v => extension.Contains(v, StringComparison.OrdinalIgnoreCase));
}

protected override bool TrySetMobileMasterPage(HttpContextBase httpContext, Page page, string mobileSuffix)
{
var deviceSpecificSufffix = GetDeviceSpecificSuffix(httpContext);
if (!String.IsNullOrEmpty(deviceSpecificSufffix) && base.TrySetMobileMasterPage(httpContext, page, deviceSpecificSufffix))
{
// We were able to set a device specific master page, so just return
return true;
}

// Just use the base logic
return base.TrySetMobileMasterPage(httpContext, page, mobileSuffix);
}

private string GetDeviceSpecificSuffix(HttpContextBase httpContext)
{
foreach (var item in _deviceUserAgentMap)
{
if (httpContext.Request.UserAgent.Contains(item.Key, StringComparison.OrdinalIgnoreCase))
{
return item.Value;
}
}

return String.Empty;
}
}

Now we've created a map of device specific suffixes, so we can have not Foo.Mobile.aspx, but rather Foo.iPhone.aspx and Foo.OperaMobile.aspx, etc.

Here's a little demo that loads a bunch of names into a list. Here's /async, the desktop view.

A list of names on the desktop

Now we'll add jQuery mobile to the mobile master page, and use it on the mobile version of the same page. We're still calling the same data source and reusing all that code.

The list of names now as a jQuery mobile page inside an iPhone

I'm pretty jazzed about what this means for ASP.NET and Web Forms developers. We're going to continue to push forward and improve ASP.NET even now, after Visual Studio 2012 has been released. Sometimes we'll add small features via NuGet packages, sometimes editor improvements as free VSIX Extensions like the Web Essentials playground for 2012 and larger changes via released updates to all of ASP.NET.  I hope you like the direction we're heading.

Go play with Microsoft.AspNet.FriendlyUrls now and thank Damian and friends on Twitter!


This week's sponsor: Be part of GENERATION APP. Your Idea. Your App. 30 Days. Begin your 30-day journey to create a Windows Store style app and talk 1-on-1 with a Windows 8 app development pro. Get started today.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Create a great mobile experience for your website today. Please.

April 23, '12 Comments [32] Posted in ASP.NET | HTML5 | Mobile
Sponsored By

People are fascinating with making mobile web sites. It's amazing that we're not impressed with the fact we carry tiny supercomputers in our pockets but we're amazed when a website looks decent on our phones.

There's a few directions you can go when going mobile for your site, and the key is finding balance. You can:

  • Do nothing. Your site will probably work on a mobile device but each day it will look worse and worse to a discerning public with increasing expectations.
  • Use Adaptive/Responsive Design. This is my favorite option. If your site is read-mostly (rather than a data-entry application) you can get a great experience on all devices by adaptively rendering your site based on screen-size. If you're focused on performance you can add a server-side component and resize image for mobile as well. Visit http://mediaqueri.es for inspiration.
  • Use a mobile framework. There's lots great frameworks like Sencha, Kendo, jQuery Mobile and others. These frameworks can get you near-native looking applications using HTML5 techniques.
  • Write a native app. For the time-being while it's cool to try to get native experiences using non-native tools, it's hard. The best native experience on a mobile device will remain a native-built application. This requires the most work with arguably the best experience. However, you CAN get 90-95% of the experience with less than 90% of the effort if you use some of these other techniques. Plus, you'll anger fewer users by not forcing them to download a crappy mobile app by just making a lovely mobile website.

image

If you take a moment and visit my site (this site) on your phone, or just resize the browser to a smaller size, you'll see that this blog is using a "responsive design" by designer Jeremy Kratz. The blog will change it's look based on if it's on a large monitor, an iPad or medium tablet, or a narrow phone. Watch the navigation bar turn into a drop down as the browser gets really narrow, for example.

My site's responsive design, as featured on the MediaQueri.es site

This was a relatively small - although thoughtful - change that instantly made my blog more accessible to the 8% of people who visit my site from a mobile device.

For larger data-focused sites, or sites that are "almost applications" you will want to consider a custom mobile version of your site. This is often done with the help of a mobile framework as mentioned above. I'll use jQuery Mobile as an example here. Let's say I have a conference browser application that looks like this on the desktop. I can navigate by date, speaker, tag, as well as view session details.

My site looks lousy on an iPhone

If I look at this same page on a mobile browser or something like the Electric Plum Mobile Simulator, it looks like crap.

Electric Mobile Simulator

I could use a mobile custom stylesheet just for phones, or I could use a CSS3 media query to make my existing stylesheet more mobile friendly...for example:

@media only screen and (max-width: 1024px) and (max-height: 768px)
{
/* do something, hide something, move something */
}

Or I could use a mobile framework along with a display mode in ASP.NET MVC to render a different view while still using the same controller logic. For example, I could have a _Layout.cshtml (that's like a "master page") and then a _Layout.Mobile.cshtml for mobile devices.

A Views folder with optional *.mobile.cshtml files for each mobile view

Mobile is just a included "display mode." You can create your own however you like. Here's one for Windows Phone. You could theoretically have ones like "tablet" or "nokia." I think you should have as few as you can get away with. Try to avoid complexity. This is just an example.

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("WP7") {
ContextCondition = ctx => ctx.GetOverriddenUserAgent().Contains("Windows Phone OS")
});

That "WP7" string is what you put in place of * in filename.*.cshtml. So that's _Layout.WP7.cshtml, or Index.WP7.cshtml, etc. For my example I'll just make a _Layout.Mobile.cshtml that will automatically be used when most mobile browsers like Mobile Safari, Blackberry or Windows Phone hit my new site.

Here is a new _Layout.Mobile.cshtml as a starting point for my conference browser mobile site. Remember that you can just File | New Project in Visual Studio with ASP.NET MVC 4 and select Mobile Site to get started on your own.

<!DOCTYPE html> 
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="@Url.Content("~/Content/jquery.mobile-1.0.min.css")" />
<link rel="stylesheet" href="@Url.Content("~/Content/Site.Mobile.css")" />
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")"></script>
<script type="text/javascript">
$(document).bind("mobileinit", function() {
// jQuery Mobile's Ajax navigation does not work in all cases (e.g.,
// when navigating from a mobile to a non-mobile page), especially when going back, hence disabling it.
$.mobile.ajaxEnabled = false;
});
</script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery.mobile-1.0.min.js")"></script>
</head>
<body>

<div data-role="page" data-theme="a">
@Html.Partial("_ViewSwitcher")

<div data-role="header">
<h1>@ViewBag.Title</h1>
</div>

<div data-role="content">
@RenderSection("featured", false)
@RenderBody()
</div>

</div>
</body>
</html>

Now that I have a custom _Layout.mobile.cshtml for mobile, THIS file will be used when I hit the site on a mobile device rather than the main _Layout.cshtml.

OK, here my application is using the mobile layout, but the existing session HTML which looks, again, like crap. I'm using a mobile layout with a desktop view.

Electric Mobile Simulator Sessions

The desktop view for a session uses a table (and that's OK you tableless-CSS people because it's a table of information):

<table>
<thead>
<tr><th>Title</th><th>Speaker(s)</th><th>Date</th><th>Room</th><th>Tags</th></tr>
</thead>
<tbody>
@foreach(var session in Model) {
<tr>
<td>@Html.ActionLink(session.Title, "Session", new { session.Code })</td>
<td>@Html.Partial("_SpeakersLinks", session)</td>
<td>@session.DateText</td>
<td>@session.Room</td>
<td>@Html.Partial("_TagsLinks", session)</td>
</tr>
}
</tbody>
</table>

But I need a cleaner mobile layout that respects a smaller screen size. I'll copy my SessionsTable.cshtml and make a SessionsTable.Mobile.cshtml with contents like this:

@using ConferenceSessionsBrowserMvc4.Models
@model IEnumerable<Session>

<h2>@ViewBag.Title</h2>

<ul data-role="listview">
@foreach(var session in Model) {
<li>
<a href="@Url.Action("Session", new { session.Code })">
<h3>@session.Title</h3>
<p><strong>@string.Join(", ", session.Speakers)</strong></p>
<p>@session.DateText</p>
</a>
</li>
}
</ul>

There are a few things to note in this HTML. First, I like that it's not littered with CSS that describes the look and feel of the site, but rather it uses the data- attributes from HTML5 to express the "role" of an element. The UL uses data-role="listview" that tells me it's a listview but doesn't dictate what it looks like.

Within the UL I've got some LIs that use standard semantic tags like A, H3, and P along with STRONG and along with the default theme it looks nice on mobile.

A nice mobile view using jQuery Mobile

ASIDE: See the the "Displaying mobile view" link at the top of the image there? With ASP.NET MVC 4 you can make a View Switcher easily with a partial View like this:

@if (Request.Browser.IsMobileDevice && Request.HttpMethod == "GET")
{
<div class="view-switcher ui-bar-a">
@if (ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice)
{
@: Displaying mobile view
@Html.ActionLink("Desktop view", "SwitchView", "ViewSwitcher", new { mobile = false, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
else
{
@: Displaying desktop view
@Html.ActionLink("Mobile view", "SwitchView", "ViewSwitcher", new { mobile = true, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
</div>
}

And a ViewSwitcherController to change the "overridden" browser when you click the link. This is all in the jQuery.Mobile.MVC NuGet package that we will update for the final release.

public class ViewSwitcherController : Controller
{
public RedirectResult SwitchView(bool mobile, string returnUrl) {
if (Request.Browser.IsMobileDevice == mobile)
HttpContext.ClearOverriddenBrowser();
else
HttpContext.SetOverriddenBrowser(mobile ? BrowserOverride.Mobile : BrowserOverride.Desktop);

return Redirect(returnUrl);
}
}
OK, back to the Dates view. I can apply the same data- jQuery Mobile techniques to other screens, like the list of dates. I've got a data-role="listview" and a data-role="list-divider" as the dates change.
@model IEnumerable<DateTime>

@{
ViewBag.Title = "All dates";
DateTime lastDay = default(DateTime);
}
<ul data-role="listview">
@foreach(var date in Model) {
if (date.Date != lastDay) {
lastDay = date.Date;
<li data-role="list-divider">@date.Date.ToString("ddd, MMM dd")</li>
}
<li>@Html.ActionLink(date.ToString("h:mm tt"), "SessionsByDate", new { date })</li>
}
</ul>

And get a nice result like this:

jQuery mobile applied to a ListView of dates

You can even get cool data filtering "as you type" features for jQuery Mobile list views by using data-filter="true" on a listview.

data-filter=true in jQuery Mobile

Because these templates are all mobile specific they don't affect the way the site looks on the desktop. Also because these are simply new views for existing URLs and Controllers, I don't need write any new business logic.

It is worth reminding you that it won't always be the case that an application will have its controllers and URLs map neatly such that one desktop view = one mobile view. Sometimes you may need to split up a complex single page desktop interaction into multiple mobile views. This conference application ended up with six views for desktop and six for mobile (Index, Dates, tags, Session(Detail), SessionsTable, and Speakers.) It's conceivable if the application included data entry that I would need to break up some views as well as create some custom methods just for mobile, although with some planning around User Experience you can usually keep this to a minimum.

If the default browser sniffing that decides what's mobile and what's not isn't enough for your project, consider using a 3rd party database of mobile devices like the one provided by 51degrees.mobi. Their 51degrees.mobi mobile framework will help your site adapt to support all mobile devices as they include a database of devices as well as their capabilities. They can even compress images and improve low-bandwidth performance.

They have a NuGet package I can install like this:

51degrees.mobile mobile framework for ASP.NET

51Degrees and libraries like it will add new capabilities to the Request.Browser object. These are just a few examples, there's dozens.

Screen Width: <% =Request.Browser.ScreenPixelsWidth %></li>
Screen Height: <% =Request.Browser.ScreenPixelsHeight %></li>
LayoutEngine: <% =Request.Browser["LayoutEngine"] %></li>
AnimationTiming: <% =Request.Browser["AnimationTiming"] %></li>
BlobBuilder: <% =Request.Browser["BlobBuilder"] %></li>
CssBackground: <% =Request.Browser["CssBackground"] %></li>
CssBorderImage: <% =Request.Browser["CssBorderImage"] %></li>
CssCanvas: <% =Request.Browser["CssCanvas"] %></li>
CssColor: <% =Request.Browser["CssColor"] %></li>
CssColumn: <% =Request.Browser["CssColumn"] %></li>
CssFlexbox: <% =Request.Browser["CssFlexbox"] %></li>
CssFont: <% =Request.Browser["CssFont"] %></li>
CssMediaQueries: <% =Request.Browser["CssMediaQueries"] %></li>

You can use this information on the server side to augment these other techniques. For example, if the requesting device supports CssMediaQueries, great, you should use them, but it not, perhaps you need to fall back to another technique. If you know the screen-size on the server and it's below a certain size you can resize the image before you send it.

Thanks to Jon Galloway, Damian Edwards and Erik Porter for their brainstorming and Steve Sanderson for the sample application.


Sponsor: I want to thank my friends at DevExpress for sponsoring this week's feed. Do take a moment and check out a free trial of CodeRush, one of my favorite products!  Introducing CodeRush by DevExpress. The Visual Studio add-in that helps you create more reliable applications. Tools to build & maintain your code without getting in the way of your IDE.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Page 1 of 3 in the Mobile category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.