In the first part of the article I described the way to implement the deep linking feature in MVVM-based Silverlight application.
In this post I am going to describe two other important functionalities that should be implemented to achieve great level of search engine optimization. First on is sitemaps and second one is html content providing.
Sitemap for Silverlight applications
As soon as the application is able to work with deep links, I can start implementing support of Sitemaps. The application should be able to generate automatically links to any content within the application or to the most important content elements. I going to add these links to an XML file with special structure. It will be sitemap of the application.
All information about structure of sitemap file you can find on the official site
I am going to implement HttpHandler to be able to generate sitemap file on the fly depends on current structure and content. I will add Sitemap.ashx handler to the web project and use following code for generating sitemap xml response:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/xml";
string serverUrl = context.Request.Url.AbsoluteUri.Replace(
context.Request.Url.PathAndQuery, string.Empty);
VacanciesDomainService service = new VacanciesDomainService();
XNamespace xnsp = "http://www.sitemaps.org/schemas/sitemap/0.9";
XElement root = new XElement(xnsp + "urlset");
//url for AllVacancies
root.Add(GenerateUrlElement(xnsp, serverUrl +
RouteTable.Routes.GetVirtualPath(null, RoutingNames.AllVacancies,
new RouteValueDictionary()).VirtualPath));
//urls for LoadVacanciesByRegion
IEnumerable<string> regions = service.LoadAllRegions().Select(r => r.Name);
foreach (string region in regions)
root.Add(GenerateUrlElement(xnsp, serverUrl +
RouteTable.Routes.GetVirtualPath(null, RoutingNames.VacanciesByRegion,
new RouteValueDictionary() { { "regionName", region } }).VirtualPath));
//urls for LoadVacancyById
IEnumerable<Vacancy> vacancies = service.LoadAllVacancies();
foreach (Vacancy vacancy in vacancies)
root.Add(GenerateUrlElement(xnsp, serverUrl +
RouteTable.Routes.GetVirtualPath(null, RoutingNames.VacancyById,
new RouteValueDictionary() { { "id", vacancy.Id } }).VirtualPath));
context.Response.Write(root.ToString());
}
private static XElement GenerateUrlElement(XNamespace nameSpace, string url)
{
return new XElement(nameSpace + "url",
new XElement(nameSpace + "loc", url),
new XElement(nameSpace + "lastmod", DateTime.Now.Date.ToString("yyyy-MM-dd")),
new XElement(nameSpace + "changefreq", "weekly"),
new XElement(nameSpace + "priority", "1"));
}
As you can see, I generate sitemap urls for each piece of content (all vacancies, vacancy by region, vacancy details). I use GetVirtualPath() method of RouteCollection to generate a url for the particular piece of content. Also, I use the VacancyDomainService class to get enumeration of region names and set of vacancy Ids. I need it because these values will be used to build urls like “vacancies-in-Auckland” or “vacancy-8” etc.
I use generated Url to build a node in the Sitemap xml file. Function GenerateUrlElement() does it. A node url contains following subnodes:
- loc. Contains link to particular content (post, article, web page).
- lastmod. The date of the last modification of the content.
- changefreq. The frequency of the content updating. Can be always, hourly, daily, weekly, monthly, yearly, never.
- priority. Means how this content is important relatively to other content on the site. The value is between 0 to 1. Bigger value means bigger importance.
At the end, I convert XDocument to string representation of the sitemap and write it to the Context.Response. So, if I open Sitemap.ashx file in a browser, I get following result:
Submitting Sitemaps to search engines
Once I get sitemap, I can submit it to search engines. I have to do it as soon as I can because a web site will be crawled in one-two weeks after sitemap submitting.
Bing. Go to Webmaster tools page, register an account and submit a sitemap.
Google. Go to webmasters web site, register an account and submit a sitemap.
Yahoo. Go to Yahoo, register and login and go to SiteExplorer. Then submit a web site address and a feed (Sitemap).
Html Content Providing
Last but not least step to become successful in SEO is to provide html content depends on requested url. These data will be used by search engines to index a web site.
I proceed from the assumption that html data is not so important for end users because they will see a Silverlight application; also, search engines do not care a lot about layout and format of displaying data. So, I am going to automate generating html data.
I plan using GridView controls to display data; DomainDataSource components to read data from DomainContext and prepare them for GridView.
Html content should be displayed only if a user does not have a Silverlight plug-in installed. So, I put Grids and DataSources into hidden div. Once page is loaded, I check availability of the Silverlight plug-in and display div with grids if it is not installed:
<div id="AlternativeContent" style="display: none;">
<h1>
Alternative content for the Silverlight Job Board</h1>
<asp:GridView ID="RegionsGridView" runat="server" AllowPaging="false" AllowSorting="false"
AutoGenerateColumns="true" AutoGenerateDeleteButton="false" AutoGenerateEditButton="false"
CellPadding="4" DataSourceID="RegionsDDS" ForeColor="#333333" GridLines="None"
Visible="false">
<AlternatingRowStyle BackColor="White" />
<EditRowStyle BackColor="#2461BF" />
<FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
<RowStyle BackColor="#EFF3FB" />
<SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
<SortedAscendingCellStyle BackColor="#F5F7FB">
............
............
</div>
<script type="text/javascript">
if (!isSilverlightInstalled()) {
var obj = document.getElementById('AlternativeContent');
obj.style.display = "";
}
</script>
I use following JavaScript code to detect the Silverlight plug-in availability.
function isSilverlightInstalled() {
var isSilverlightInstalled = false;
try {
//check on IE
try {
var slControl = new ActiveXObject('AgControl.AgControl');
isSilverlightInstalled = true;
}
catch (e) {
//either not installed or not IE. Check Firefox
if (navigator.plugins["Silverlight Plug-In"]) {
isSilverlightInstalled = true;
}
}
}
catch (e) {
//we don't want to leak exceptions. However, you may
//to add exception tracking code here
}
return isSilverlightInstalled;
}
In the code behind I analyze Route Url of the current request, set particular QueryName and query parameters. These characteristics are used to load data from DataContext:
switch(((Route)RouteData.Route).Url)
{
case RoutingNames.AllVacancies:
default:
VacanciesDDS.QueryName = "LoadAllVacancies";
VacanciesDDS.QueryParameters.Clear();
VacancyGridView.Visible = false;
VacanciesGridView.Visible = true;
RegionsGridView.Visible = true;
return;
case RoutingNames.VacanciesByRegion:
VacanciesDDS.QueryName = "LoadVacanciesByRegion";
VacanciesDDS.QueryParameters.Add(RouteData.Values.First().Key,
RouteData.Values.First().Value.ToString());
VacancyGridView.Visible = false;
VacanciesGridView.Visible = true;
RegionsGridView.Visible = true;
return;
case RoutingNames.VacancyById:
VacancyGridView.Visible = true;
RegionsGridView.Visible = false;
VacanciesGridView.Visible = false;
VacancyDDS.QueryName = "LoadVacancyById";
VacancyDDS.QueryParameters.Add(RouteData.Values.First().Key,
RouteData.Values.First().Value.ToString());
return;
}
Here is the web site with turned off Silverlight plug-in. The request is “Vacancies in Auckland”.
Here is the same request with turned on Silverlight plug-in:
Silverlight application in the search index.
As I had built demo application, I added the sitemap to search engines. By now, the application is indexed and I can show several top-search queries. For example, my demo application is #1 in Google by the “silverlight vacancies in Auckland”, “silverlight vacancies moscow” etc queries:
Source codes and demo application
I have prepared and uploaded both binaries and sources, also I you can browse and try the demo application.
Related links
Here is the list of links that can help to build a search engine optimized silverlight web site:
Hope, this helps!
Tagged: Silverlight
, SEO
, MVVM
, Bing
, Google
, Yahoo
, ReMIX
, Ria application
, Ria services
, Routing
, Prism
, Deep Linking
, Navigation Framework
, Sitemap
, Domain Service