Silverlight 4 + RIA Services - Prêt pour les affaires
Optimisation pour les moteurs de recherche (SEO)
Date de publication : 24/02/2012. Date de mise à jour : 29/02/2012.
Par
Brad Abrams
Deepin Prayag (Traduction)
Cet article fait partie d'une série de traductions d'articles de Brad Abrams sur le développement d'applications métier avec Silverlight 4 et .NET RIA Services.
Cette série se concentre uniquement sur la base des applications métier : l'interrogation, la mise à jour, la validation et la sécurisation de vos données métier importantes.
Traduction
Prérequis
Optimisation pour les moteurs de recherche (SEO)
Étape 1 : Faire lier en profondeur le contenu important pour l'application.
Étape 2 : Mettre les moteurs de recherche au courant de tous ces liens profonds avec un sitemap.
Étape 3 : Fournir une version de « bas niveau » du contenu important.
Résumé
Conclusion
Liens
Remerciements
Traduction
Prérequis
La procédure pas à pas requiert :
Je l'ai implémentée avec Silverlight 4 RC, mais cela devrait fonctionner avec Silverlight 4 RTM.
Optimisation pour les moteurs de recherche (SEO)
Pour
continuer notre série, jetons un oeil à SEO et Silverlight. La grande majorité du trafic web est dictée par la recherche. Les moteurs de recherche sont la première étape pour beaucoup d'utilisateurs de l'internet public et cela augmente aussi dans les environnements professionnels. La recherche est aussi la technologie clé qui entraîne la plus grande partie des revenus publicitaires. Donc, inutile de le préciser, le SEO est important. Mais comment fonctionne le SEO dans une application Silverlight où la plupart du contenu intéressant est généré dynamiquement ? Je vous présenterai un modèle d'application pour faire du SEO dans une application Silverlight avec un minimum de travail additionnel.
Il y a trois étapes faciles et amusantes pour rendre votre application Silverlight « SEO friendly ».
- Étape 1 : Faire lier en profondeur le contenu important pour l'application.
- Étape 2 : Mettre les moteurs de recherche au courant de tous ces liens profonds avec un sitemap.
- Étape 3 : Fournir une version de « bas niveau » du contenu important.
Étape 1 : Faire lier en profondeur le contenu important pour l'application.
Tout contenu sur votre site dont vous souhaitez qu'il puisse faire l'objet d'une recherche individuelle doit être accessible via une URL. Si je veux que vous soyez en mesure d'utiliser Bing (ou Google, ou autre) pour « Country Fried Steak » et atterrir sur ma page listant des photos de Country Fried Steak, j'ai besoin de proposer une URL qui vous renvoie exactement à ce contenu.
Heureusement, avec la fonctionnalité de navigation de Silverlight, c'est très facile d'ajouter le support pour les liens profonds. Jetons un oeil sur comment faire cela dans une application Silverlight.
Ce que nous voulons faire c'est fournir une URL qui permet d'identifier un certain restaurant ou un restaurant et un plat en particulier. Pour le SEO ainsi qu'une meilleure lisibilité pour l'homme, nous voudrions l'URL dans un format tel que
http://www.hanselman.com/abrams/restaurant/25/plate/4 , pour indiquer que ceci est le restaurant=25 et le plat=4. Pour ce faire, définissons les itinéraires dans le projet web dans global.asax.
|
1 : public class Global : HttpApplication
2 : {
3 : void Application_Start (object sender, EventArgs e)
4 : {
5 : RegisterRoutes (RouteTable. Routes);
6 : }
7 :
8 : void RegisterRoutes (RouteCollection routes)
9 : {
10 : routes. MapPageRoute (
11 : " deepLinkRouteFull " ,
12 : " restaurant/{restaurantId}/plate/{plateId} " ,
13 : " ~/default.aspx " ,
14 : false ,
15 : new RouteValueDictionary { { " restaurant " , " -1 " } ,
16 : { " plate " , " -1 " } } );
17 :
18 : routes. MapPageRoute (
19 : " deepLinkRoute " ,
20 : " restaurant/{restaurantId} " ,
21 : " ~/default.aspx " ,
22 : false ,
23 : new RouteValueDictionary { { " restaurant " , " -1 " } } );
24 :
|
Aux lignes 12 et 20 nous définissons le modèle des liens profonds que nous prenons en charge avec les « place holders » pour le restaurantId et plateId pour les valeurs dans l'adresse URL. Nous les définissons dans l'ordre du plus complexe au moins complexe. Les valeurs par défaut sont données aux lignes 15 et 23 si les identifiants sont omis de l'adresse URL.
Maintenant voyons comment parser cette URL sur le client Silverlight. Dans Plates.xaml.cs :
|
1 :
2 : protected override void OnNavigatedTo (NavigationEventArgs e)
3 : {
4 : int plateID = - 1 ;
5 : int restaurantId = - 1 ;
6 : var s = HtmlPage. Document. DocumentUri. ToString (). Split (new char [ ] { ' / ' , ' # ' } );
7 : int i = Find (s, " plate " );
8 : if (i ! = - 1 )
9 : {
10 : plateID = Convert. ToInt32 (s[ i + 1 ] );
11 : plateDomainDataSource. FilterDescriptors. Add (
12 : new FilterDescriptor (" ID " ,
13 : FilterOperator. IsEqualTo, plateID));
14 : }
15 : i = Find (s, " restaurant " );
16 : if (i ! = - 1 ) restaurantId = Convert. ToInt32 (s[ i + 1 ] );
17 : else restaurantId = Convert. ToInt32 (NavigationContext. QueryString[ " restaurantId " ] );
18 : plateDomainDataSource. QueryParameters. Add (
19 : new Parameter ()
20 : {
21 : ParameterName = " resId " ,
22 : Value = restaurantId
23 : }
24 : );
25 : }
26 :
|
Fondamentalement, ce que le code ci-dessus fait c'est d'obtenir l'URL complète et de distinguer les parties de l'URL et distinguer les identifiants restaurant et plate. Aux lignes 18-23, nous passons le restaurantId comme paramètre à la méthode de requête et aux lignes 11-14 ci-dessus, nous n'utilisons pas une méthode de requête, mais appliquons plutôt un descripteur de filtre qui ajoute une clause « where » à la requête LINQ envoyée au serveur. Résultat, nous n'avons pas besoin de changer de code serveur.
Une autre petite chose que nous devons faire est de nous assurer que le client se retrouve bien sur la page Plates. Ceci est géré par le framework de navigation de Silverlight en utilisant la balise d'ancrage « #/Plates ». Parce que les balises d'ancrage sont une fonction client seulement, les moteurs de recherche ne peuvent pas les traiter de manière efficace. Donc, nous devons ajouter cela sur le client. J'ai trouvé que c'était assez facile de le faire en utilisant juste un peu de JavaScript. J'émets ceci de la page Default.aspx sur le serveur.
|
1 : protected void Page_Init (object sender, EventArgs e)
2 : {
3 : string resId = Page. RouteData. Values[ " restaurant " ] as string ;
4 : if (resId ! = null ) { Response. Write (" <script type=text/javascript>window.location.hash='#/Plates';</script " + " > " ); }
5 : }
6 :
|
Line: 56
Error: Unhandled Error in Silverlight Application
Code: 2104
Category: InitializeError
Message: Could not download the Silverlight application.
Check web server settings
Pour y remédier,
|
< script type= " text/javascript " src= ' <%= this.ResolveUrl("~/Silverlight.js") %> ' > < / script>
|
et
|
< param name= " source " value= " <%= this.ResolveUrl( " ~ / ClientBin/ MyApp. xap" ) %> " / >
|
Maintenant nous donnons une adresse URL qui inclut un PlateId telle que :
En conséquence, nous obtenons notre objet individuel.
Étape 2 : Mettre les moteurs de recherche au courant de tous ces liens profonds avec un sitemap.
Maintenant nous avons notre application liée en profondeur, avec chaque partie intéressante de donnée ayant une URL unique. Mais comment est-ce qu'un moteur de recherche sera capable de trouver ces URL ? Nous espérons certainement qu'à mesure que les gens en parlent (et par conséquence se connectent) sur notre site, sur des réseaux sociaux, les moteurs de recherche reprendront certains d'entre eux, mais nous voudrions peut-être faire un travail plus complet. Nous voudrions peut-être fournir au moteur de recherche TOUS les liens profonds de l'application. Nous pouvons le faire avec un sitemap.
Le format sitemap est accepté par tous les principaux moteurs de recherche... vous pouvez trouver de plus amples renseignements à ce sujet sur
http://sitemap.org.
Pour comprendre comment cela fonctionne, regardons le procédé qu'utiliserait un moteur de recherche pour indexer un site intéressant piloté par les données :
http://amazon.com. Quand un moteur de recherche tombe pour la première fois sur un tel site, il lit le fichier robots.txt à la racine du site. Dans le cas présent :
http://www.amazon.com/robots.txt
Dans cet exemple, vous pouvez voir en haut du fichier une liste de répertoires que les moteurs de recherche sont invités à ignorer. Ensuite au bas de cette page, il y a une liste de sitemaps que le moteur de recherche doit utiliser pour indexer tout le contenu du site.
Note : Vous n'avez pas, a proprement parlé, besoin d'utiliser un sitemap. Vous pouvez utiliser les outils du maître du site fournis par les principaux moteurs de recherche pour inscrire directement votre sitemap.
Si nous accédons à l'une de ces URL, nous voyons un fichier de sitemap, comme indiqué ci-dessous :
Dans ce cas, parce qu'Amazon.com est tellement vaste, ces liens sont en fait pour plusieurs sitemap. (Ce fichier est connu comme un fichier d'index de sitemap). Quand on arrive au bout, nous obtenons des liens vers de réels produits.
Comme vous pouvez le voir, le format ressemble à :
|
< urlset xmlns = " http://www.google.com/schemas/sitemap/0.84 " >
< url >
< loc > http://www.amazon.com/GAITHER-COMMITTEE-EISENHOWER-COLD-WAR/dp/081425005X< / loc >
< / url >
< url >
< loc > http://www.amazon.com/CONTROLLING-VICE-REGULATING-PROSTITUTION-CRIMINAL/dp/0814250076< / loc >
< / url >
|
Une chose qui est intéressante ici est que ces liens sont constamment en train de changer au fur et à mesure que les objets sont ajoutés et enlevés du catalogue Amazon.
Voyons comment créer un sitemap comme celui-ci pour notre site.
Dans le projet web, ajoutez un nouveau Search Sitemap en utilisant le dialogue Add New Item dans VS et sélectionnez l'item Search Sitemap.
Quand on fait cela, on obtient un fichier robots.txt qui ressemble à ça :
# This file provides hints and instructions to search engine crawlers.
# For more information, see http://www.robotstxt.org/.
# Allow all
User-agent: *
# Prevent crawlers from indexing static resources (images, XAPs,
etc
)
Disallow: /
ClientBin
/
# Register your sitemap(s) here.
Sitemap
: /Sitemap.aspx
... et un fichier sitemap.aspx
Pour construire ce sitemap, nous devons créer une autre vue des mêmes données à partir de notre PlateViewDomainService. Dans ce cas, nous le consommons à partir d'une page web ASP.NET. Pour ce faire nous utilisons l'objet DomainDataSource. Vous pouvez le configurer dans le concepteur en :
Glissant et déposant un contrôle Repeater au formulaire et nous obtenons l'expérience de conception ci-dessous :
Ensuite faites un clic droit dessus et configurez la source de données.
Sélectionnez une nouvelle source de données
Finalement on se retrouve avec deux séries de liens dans notre sitemap.
|
1: < asp : DomainDataSource runat = " server " ID = " RestaurauntSitemapDataSource "
2 : DomainServiceTypeName = " MyApp . Web . DishViewDomainService "
3 : QueryName = " GetRestaurants " / >
4:
5: < asp : Repeater runat = " server " id = " repeater " DataSourceID = " RestaurauntSitemapDataSource " >
6: < HeaderTemplate >
7: < urlset xmlns = " http : / / www . sitemaps . org / schemas / sitemap / 0 . 9 " >
8: < / HeaderTemplate >
9: < ItemTemplate >
10: < url >
11: < loc > <% = Request . Url . AbsoluteUri . ToLower (). Replace (" sitemap.aspx " ,string . Empty ) + " restaurant/ " %> <% # HttpUtility. UrlEncode (Eval (" ID " ). ToString ()) %> < / loc >
12: < / url >
13: < / ItemTemplate >
14: < / asp : Repeater >
15:
16: < asp : DomainDataSource ID = " PlatesSitemapDataSource " runat = " server "
17 : DomainServiceTypeName = " MyApp . Web . DishViewDomainService "
18 : QueryName = " GetPlates " >
19: < / asp : DomainDataSource >
20:
21: < asp : Repeater runat = " server " id = " repeater2 " DataSourceID = " PlatesSitemapDataSource " >
22: < ItemTemplate >
23: < url >
24: < loc > <% = Request . Url . AbsoluteUri . ToLower (). Replace (" sitemap.aspx " ,string . Empty ) + " restaurant/ " %> <% # HttpUtility. UrlEncode (Eval (" RestaurantID " ). ToString ()) + " /plate/ " + HttpUtility. UrlEncode (Eval (" ID " ). ToString ()) %> < / loc >
25: < / url >
26: < / ItemTemplate >
27: < FooterTemplate >
28: < / urlset >
29: < / FooterTemplate >
30: < / asp : Repeater >
31:
|
Comme vous pouvez le voir aux lignes 3 et 18, nous faisons appel aux méthodes GetRestaurant et GetPlates définies directement dans notre DomainService.
Maintenant, pour tout ensemble raisonnable de données, cela va être une page TRES coûteuse à exécuter. Elle analyse chaque ligne dans la base de données. Bien qu'il soit agréable de conserver les données fraiches, nous aimerions équilibrer la charge du serveur. Une manière simple de faire cela est d'utiliser la mise en cache de sortie pendant une heure. Pour plus d'informations consultez :
ASP.NET Caching: Techniques and Best Practices
|
< % @ OutputCache Duration= " 3600 " VaryByParam= " None " % >
|
Une autre approche pour les très grands ensembles de données serait de factoriser les données en plusieurs sitemaps comme le fait l'exemple amazon.com que nous avons vu ci-dessus.
Et si nous saisissons une de ces URL et que nous y accédons. bingo ! Nous obtenons la bonne page.
Étape 3 : Fournir une version de « bas niveau » du contenu important.
C'est fantastique, nous avons des liens profonds, nous avons un moyen pour que les moteurs de recherche découvrent l'ensemble de ces liens, mais que trouvera le moteur de recherche quand il arrive sur cette page ? Eh bien, les moteurs de recherche, la plupart du temps, ne font que parser du HTML, donc si nous faisons un « Afficher la source » nous verrons ce que voit le moteur de recherche :
Ou si nous naviguons avec Silverlight désactivé « Outils > Gérer les modules complémentaires », nous voyons ceci :
Nous voyons une vieille page blanche contenant rien !
Certes, aucun contenu dynamique n'est présenté. Le code doit effectivement être exécuté pour que le contenu dynamique soit chargé. Je suis quasiment certain que les moteurs de recherche ne vont pas exécuter ce code Silverlight (ou Flash ou Ajax) dans leurs centres de données de sitôt. Donc ce dont nous avons besoin c'est du contenu alternatif.
Heureusement ceci assez facile à faire. Tout d'abord obtenons n'importe quel contenu alternatif à rendre. Il est important de noter que ce contenu n'est pas uniquement pour les moteurs de recherche. Du contenu écrit uniquement pour les moteurs de recherche est parfois appelé spoofing de moteur de recherche ou spam web quand il est fait pour tromper les utilisateurs des moteurs de recherche sur la véritable nature du site. (
The pernicious perfidy of page-level web spam).
Au lieu de cela, ce contenu est un rendu alternatif de la page pour quelqu'un qui n'a pas Silverlight installé. Il pourrait ne pas avoir toutes les fonctionnalités, mais c'est une bonne expérience de bas niveau. Il se trouve que les robots d'indexation du moteur de recherche n'ont pas Silverlight installé, de manière à obtenir quelque chose de significatif et précis à indexer.
Ajoutez ce code HTML à votre page default.aspx.
|
< div id = " AlternativeContent " style = " display: none; " >
< h2 > Hi, this is my alternative content< / h2 >
< / div >
|
Remarquez que c'est « display : none », ce qui veut dire que nous ne nous attendons pas à ce que ce soit rendu par le navigateur. à moins que Silverlight ne soit pas disponible. Pour accomplir cela, ajouter ce bout de code à la page :
|
<script type = " text / javascript " >
if (! isSilverlightInstalled ()) {
var obj = document. getElementById (' AlternativeContent ' );
obj. style. display = " " ;
}
</script>
|
|
function isSilverlightInstalled () {
var isSilverlightInstalled = false ;
try {
try {
var slControl = new ActiveXObject (' AgControl.AgControl ' );
isSilverlightInstalled = true ;
}
catch (e) {
if (navigator. plugins[ " Silverlight Plug-In " ] ) {
isSilverlightInstalled = true ;
}
}
}
catch (e) {
}
return isSilverlightInstalled;
}
|
Quand nous l'exécutons à partir d'un navigateur sans Silverlight activé, nous obtenons le contenu alternatif :
Mais avec Silverlight installé, nous obtenons notre beau contenu de l'application Silverlight.
C'est très bien, mais comment pouvons-nous exposer le contenu correct ? Nous voulons afficher exactement les mêmes données que dans l'application Silverlight et nous voulons écrire le moins de code possible. Nous ne voulons vraiment pas maintenir plusieurs pages. Donc, ajoutons un peu de code très basique à la page dans notre div AlternativeContent. Ce ListView est pour les détails de Restaurant.
|
< asp : ListView ID = " RestaurnatDetails " runat = " server "
EnableViewState = " false " >
< LayoutTemplate >
< asp : PlaceHolder ID = " ItemPlaceHolder " runat = " server " / >
< / LayoutTemplate >
< ItemTemplate >
< asp : DynamicEntity ID = " RestaurnatEntity " runat = " server " / >
< / ItemTemplate >
< / asp : ListView >
|
Maintenant nous devons le lier à notre source de données. Je trouve que c'est assez facile dans la vue design dans VS. Notez que vous devez rendre le div visible afin que vous puissiez travailler avec dans le concepteur.
Ensuite nous configurons la source de données. C'est très facile de sélectionner la méthode de requête que nous voulons utiliser.
Ensuite nous lions le paramètre de la méthode de requête basé sur les itinéraires que nous avons définis.
Maintenant nous faisons exactement la même chose pour notre ListView Plates.
Cela nous donne un peu de code aspx très simple :
|
1: < asp : ListView ID = " RestaurnatDetails " runat = " server "
2 : EnableViewState = " false " DataSourceID = " restaurantsDomainDataSource " >
3: < LayoutTemplate >
4: < asp : PlaceHolder ID = " ItemPlaceHolder " runat = " server " / >
5: < / LayoutTemplate >
6: < ItemTemplate >
7: < asp : DynamicEntity ID = " RestaurnatEntity " runat = " server " / >
8: < / ItemTemplate >
9: < / asp : ListView >
10:
11: < asp : DomainDataSource ID = " restaurantsDomainDataSource " runat = " server "
12 : DomainServiceTypeName = " MyApp . Web . DishViewDomainService "
13 : QueryName = " GetRestaurant " >
14: < QueryParameters >
15: < asp : RouteParameter name = " id " RouteKey = " restaurantId "
16 : DefaultValue = " - 1 " Type = " Int32 " / >
17: < / QueryParameters >
18: < / asp : DomainDataSource >
19:
|
Ensuite nous voulons permettre à ces contrôles de générer dynamiquement l'interface utilisateur basée sur les données.
|
1 : protected void Page_Init (object sender, EventArgs e)
2 : {
3 : RestaurnatDetails. EnableDynamicData (typeof (MyApp. Web. Restaurant));
4 : PlateDetails. EnableDynamicData (typeof (MyApp. Web. Plate));
5 : string resId = Page. RouteData. Values[ " restaurant " ] as string ;
6 : if (resId ! = null ) { Response. Write (" <script type=text/javascript>window.location.hash='#/Plates';</script " + " > " ); }
7 : }
8 :
|
Remarquez que nous avons ajouté les lignes 4-5 pour activer les données dynamiques sur ces deux ListViews.
La dernière étape est que nous devons ajouter l'ensemble des modèles qu'utilise
DynamicData. Vous pouvez obtenir ceux-là de n'importe quel projet Dynamic Data. Vous n'avez qu'à les copier à la racine du projet web.
Vous pouvez éditer ces modèles pour contrôler exactement la façon dont vos données sont affichées.
Dans le répertoire Entity Templates nous avons besoin de deux modèles pour chacun de nos entités (Plate et Restaurant dans ce cas). Ceci permet de contrôler la façon dont ils sont affichés.
|
1: <% @ Control Language= " C# " CodeBehind= " Restaurant.ascx.cs " Inherits= " MyApp.Web.RestaurantEntityTemplate " %>
2:
3: < asp : DynamicControl ID = " DynamicControl8 " runat = " server " DataField = " ImagePath " / >
4: < ul class = " restaurant " >
5: < li >
6: < ul class = " restaurantDetails " >
7: < li > < h2 > < asp : DynamicControl ID = " NameControl " runat = " server " DataField = " Name " / > < / h2 > < / li >
8: < li > < asp : DynamicControl ID = " DynamicControl1 " runat = " server " DataField = " ContactName " / > (< asp : DynamicControl ID = " DynamicControl2 " runat = " server " DataField = " ContactTitle " / > )< / li >
9: < li > < asp : DynamicControl ID = " DynamicControl3 " runat = " server " DataField = " Address " / > < / li >
10: < li > < asp : DynamicControl ID = " DynamicControl4 " runat = " server " DataField = " City " / > , < asp : DynamicControl ID = " DynamicControl5 " runat = " server " DataField = " Region " / > < asp : DynamicControl ID = " DynamicControl6 " runat = " server " DataField = " PostalCode " / > < / li >
11: < li > < asp : DynamicControl ID = " DynamicControl7 " runat = " server " DataField = " Phone " / > < / li >
12: < li > < asp : HyperLink runat = " server " ID = " link " NavigateUrl = " <% #GetDetailsUrl () %> " Text = " details . . " > < / asp : HyperLink > < / li >
13: < / ul >
14: < / li >
15: < / ul >
16:
|
Remarquez qu'ici aussi, nous ne faisons qu'une mise en forme basique et nous ne mentionnons que les champs que nous voulons voir apparaitre dans le contenu alternatif. Répétez l'opération pour Plate.
Maintenant nous sommes prêts à l'exécuter.
A la racine, sans paramètres de chaine de requête nous obtenons la liste des restaurants, le tout en HTML bien sûr.
Ensuite nous pouvons ajouter les itinéraires pour le réduire à un plat dans un restaurant en particulier.
Mais voyons cela dans un vrai navigateur, pour être sûr que nous sachions à quoi cela ressemble pour un moteur de recherche. Vive
Lynx ! Lynx a été le premier navigateur web que j'ai utilisé en 1992 sur ma machine DEC2100 dans le laboratoire Leazar sur le campus de l'Université d'Etat de Caroline du Nord. Et il fonctionne toujours aussi bien aujourd'hui.
Et les détails
Ce navigateur en mode texte classique nous montre seulement le texte- exactement ce que les robots d'indexation des moteurs de recherche verront.
Maintenant passons au vrai test.
Cliquer sur le lien ?
.nous amène exactement à la bonne page avec les données correctes dans notre belle vue Silverlight.
Résumé
Ce que vous avez vu ici sont les rudiments sur la façon de créer une application web Silverlight pilotée par les données et qui est « SEO ready ». Nous avons passé en revue trois étapes faciles et amusantes :
- Étape 1 : Faire lier en profondeur le contenu important pour l'application.
- Étape 2 : Mettre les moteurs de recherche au courant de tous ces liens profonds avec un sitemap.
- Étape 3 : Fournir une version de « bas niveau » du contenu important.
Profitez-en !
Conclusion
Ceci conclut la septième partie de cette série. Dans la prochaine partie nous verrons comment exposer des services OData.
Liens
Remerciements
Je tiens ici à remercier
Brad Abrams pour m'avoir autorisé à traduire son article.
Je remercie
Jean-Michel Ormes pour sa relecture technique et ses propositions.
Je remercie également
xyz pour sa relecture orthographique et ses propositions.
Copyright © 2012 Brad Abrams.
Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de
son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur.
Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 €
de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.