Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Actualités

  • Blog de Cyril DURAND, passionné de JavaScript, Ajax, ASP.net et tout ce qui touche au developpement Web Client-Side.

    View Cyril Durand's profile on LinkedIn

    hit counters

Que contient le cache de mon navigateur ?

Lorsque l'on développe des sites internet, on aimerait bien savoir ce que contient le cache de notre navigateur, certes on peut utiliser des analyseurs de trames HTTP comme firebug, fiddler, httpwatch, mais ce n'est pas ce qu'il y a de plus pratique.

J'ai récemment découvert une astuce pour Firefox. Dans l'url il suffit de taper "about:cache", vous aurez alors la possibilité de lister le contenu du cache disque et mémoire de votre Firefox :

"about:cache"

 image

"about:cache?device=memory"

image

"about:cache-entry"

image

A noter que l'on peut utiliser, l'extension CacheViewer qui permet d'obtenir les différents éléments du cache d'une façon plus graphique.

Quel outil pour Internet Explorer ?

A partir de là, je me suis dit qu'il serait intéressant d'avoir une astuce similaire pour Internet Explorer. Malheureusement je n'ai rien trouvé de très pratique, la seule piste est une appli (avec les sources en C++) qui utilise l'API WinInet pour accéder au cache "Reading the Internet Explorer Cache"


Petite chose amusante essayer d'accéder à "about:robots" dans votre Firefox 3 ;-)

WCF : serialiser un objet non serialisable lorsque l'on n'a pas accès au type

Lorsqu'on utilise un service WCF, il se peut que l'on ne possède pas le contrôle des différents types que l'on transfère. Dans ce cas il est possible d'avoir des problèmes pour sérialiser, en effet, ne pouvant pas modifier le type, on ne peut pas rajouter des attributs utiles à la sérialisation, rajouter un constructeur vide, ... 
Ces problèmes surviennent quelques soit le binding utilisé, dans la suite de mon exemple j'utilise le netTcpBinding.

J'ai récemment été confronté à ce problème, après pas mal de recherche, j'ai trouvé une solution qui me convenait, celle-ci ne nécessite pas de réécrire une version simplifié de l'objet à transiter.

Pour cela j'ai créé une classe possédant une propriété du type de l'objet à transférer. Dans mon exemple, l'objet qui pose problème lors de la sérialisation est de type Query.

public class QueryEncapsulator{ public QueryEncapsulator() { } public QueryEncapsulator(Query q) { this.Query = q; } public Query Query { get; set; } }

Bien sur, cela ne suffit pas pour résoudre le problème, l'étape suivante est d'implémenter l'interface IXmlSerializable. Cette interface va nous permettre de personnaliser la sérialisation de cet objet, pour ne pas avoir de problème nous allons utiliser une sérialisation binaire via le BinaryFormatter puis écrire le binaire dans le XMLWriter en base64.

[Serializable] public class QueryEncapsulator : IXmlSerializable { public QueryEncapsulator() { } public QueryEncapsulator(Query q) { this.Query = q; } public Query Query { get; set; } public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { BinaryFormatter formatter = new BinaryFormatter(); Byte[] b = new Byte[1024]; using (MemoryStream ms = new MemoryStream()) { if (!reader.Read()) throw new Exception("boom"); int i; while ((i = reader.ReadContentAsBase64(b, 0, b.Length)) > 0) { ms.Write(b, 0, i); } ms.Position = 0; Query obj = (Query)formatter.Deserialize(ms); this.Query = obj; } } public void WriteXml(System.Xml.XmlWriter writer) { using (MemoryStream ms = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(ms, this.Query); Byte[] b = ms.ToArray(); writer.WriteBase64(b, 0, b.Length); } } }

A partir de là, nous pouvons transférer notre objet QueryEncapsulator à la place de notre objet Query.

Mais pourquoi cette astuce fonctionne ? En effet, pourquoi une sérialisation binaire via le BinaryFormatter fonctionne alors que lorsqu'on utilise le NetTcpBinding le message est transféré sous forme binaire.

[disclaimer:information non confirmé] D'après ce que j'ai compris, quelque soit le binding utilisé la sérialisation WCF se fait via le DataContractSerializer. Ce DataContractSerializer utilise en interne les mécanismes sous-jacent de la sérialisation XML afin de créer le graph de la sérialisation. Cela implique que l'on peut utiliser l'interface IXmlSerializable afin de personaliser la sérialisation.

Niveau performance, le surcout de cette astuce est négligeable par rapport à la machinerie WCF, dans mon cas, le surcout ne prend pas 1ms alors que le service met entre 5 et 30ms à répondre.

[Astuce] Projet de démarrage multiple dans Visual Studio

Lorsque vous développer des grosses applications avec Visual Studio, il est parfois nécessaire de démarrer plusieurs projets en même temps pour pouvoir tester son code. C'est typiquement le cas avec WCF, on a souvent besoin de lancer l'application hébérgeant le service WCF ainsi que l'application consommant ce service.

Dans Visual Studio, il est possible de configurer au niveau de la solution plusieurs projets de démarrage. Pour cela allé dans les propriétés de la solution (clique droit sur la solution puis propriétés), il y a une page "Startup Project" qui permet de définir le ou les projets de démarrage.

image

WCF : KnownType - Comment retourner des types enfants du type définit dans le ServiceContract

Lorsque vous créez un service WCF, vous allez créer un contrat. Dans la plupart des cas, ce contrat est une interface décorée de l'attribut ServiceContract contenant des méthodes décorées de l'attribut OperationContract.

Si vous voulez retourner un objet d'un type enfant du type déclaré vous obtiendrez une exception de type : "CommunicationException".

Prenons cet exemple :

[ServiceContract] public interface IMyService { [OperationContract] Animal GetAnimal(String name); } [DataContract] public abstract class Animal { } [DataContract] public class Dog : Animal { } [DataContract] public class Cat : Animal { }

L'implémentation de ce contrat retournera soit un objet de type Dog ou un objet de type Cat suivant la valeur du paramètre name.

Lors de l'utilisation de ce service vous obtiendrez une CommunicationException :

There was an error while trying to serialize parameter http://tempuri.org/:query. The InnerException message was 'Type 'Test.Dog' with data contract name 'Dog:http://schemas.datacontract.org/2004/07/Test.Doc' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details.

La raison est que vous retournez un objet qui n'est pas connu du service, il ne sait alors pas comment le sérialiser. Pour palier à ce problème, il faut faire connaitre ces types à notre service. Il existe plusieurs solutions :

  • Utilisation de l'attribut ServiceKnownType : Cet attribut s'utilise au niveau du contrat soit sur la définition de l'interface, soit sur les différentes méthodes de l'interface :

[ServiceContract] public interface IMyService { [OperationContract] [ServiceKnownType(typeof(Dog))] [ServiceKnownType(typeof(Cat))] Animal GetAnimal(String name); }

  • Utilisation de l'attribut KnownType : Cet attribut s'utilise au niveau de la classe mère :

[DataContract] [KnownType(typeof(Dog))] [KnownType(typeof(Cat))] public abstract class Animal { }

<configuration> <system.runtime.serialization> <dataContractSerializer> <declaredTypes> <add type="Test.Animal, MyAssembly"> <knownType type="Test.Dog, MyAssembly"/> </add> <add type="Test.Animal, MyAssembly"> <knownType type="Test.Dog, MyAssembly"/> </add> </declaredTypes> </dataContractSerializer> </system.runtime.serialization> </configuration>

Il existe d'autres solutions, mais toutes dérives de ces 3 solutions. J'aime particulièrement la solution qui consiste à appeler une méthode static d'un certain type, cette solution permet d'inclure des KnownType générique ou alors d'inclure tous les enfants d'un type via de la reflection.

Pour en savoir plus sur comment référencer vos types dans le contrat, je vous conseille cet article : All about knownTypes ou cet article MSDN : Data Contract Known Types

RegexStringValidator et propriété requise dans un élément de section de configuration personnalisé

Lorsqu'on créé une section de configuration personnalisé, .net permet de valider les entrées grâce aux attributs StringValidator et RegexStringValidator.

J'ai voulu utiliser l'attribut RegexStringValidator sur un élément de ma configuration afin de vérifier que l'entrée contenait un String "simple", j'ai donc écrit ce code :

public class IndexElement : ConfigurationElement { [RegexStringValidator(@"[\w\-\.]+")] [ConfigurationProperty("Name", IsKey = true, IsRequired = true)] public String Name { get { return (String)base["Name"]; } set { base["Name"] = value; } } }

Dans mon fichier de configuration, j'ai rajouté la ligne qui va bien, mais lorsque je charge la configuration j'obtiens cette erreur :

The value for the property 'Name' is not valid. The error is: The value does not conform to the validation regex string '[\w\-\.]+'.

Après quelques recherches, je me suis rendu compte que .net valide également la valeur par défaut, dans mon cas cette valeur vaut null ce qui ne valide pas l'expression régulière.

Pour corriger le problème, il faut renseigner une valeur par défaut, on peut le faire via l'attribut ConfigurationProperty.

[RegexStringValidator(@"[\w\-\.]+")] [ConfigurationProperty("Name", IsKey = true, IsRequired = true, DefaultValue="abc")] public String Name { get { return (String)base["Name"]; } set { base["Name"] = value; } }

La valeur par défaut importe peu, en effet si on ne renseigne pas la propriété, .net va retourner une exception puique nous avons marquer cette propriété comme "Required".

Editer un fichier de config WCF : SVCConfigEditor

Toutes les personnes ayant déjà utilisé WCF savent à quel point il est pénible de faire la configuration. En effet WCF repose essentiellement sur des fichiers de config qui possèdent de très nombreuses options, éditer ce fichier de config à la main devient vite un enfer.

Lorsque vous créez un nouveau projet de type "WCF Service Library" :

Untitled

Vous pouvez modifier le fichier de config du projet via un éditeur spécial :

Untitled2

Malheureusement lorsque l'on fait un client, ou alors qu'on décide d'héberger "manuellement" notre service WCF, on ne dispose pas de cette option.

Untitled4

Pour palier à ce manque, on peut rajouter notre propre éditeur à n'importe quel fichier. Pour cela faites un click droit sur le fichier de config dans l'explorateur de solution puis "Ouvrir Avec ...", il faut ensuite rajouter un nouvel éditeur. svcConfigEditor se trouve ici : "C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\SVCConfigEditor.exe"

Untitled5

SVCConfigEditor nous permet de configurer toute les options de WCF. Si vous ne le connaissez pas et utilisez WCF, je vous invite grandement à le découvrir, attention cet outil nécessite malgré tout de comprendre le fonctionnement de la configuration WCF.

Untitled3

JSON - optimiser la sérialisation des List<T> par ASP.net AJAX

Grâce au service web et ASP.net Ajax, il est très simple de communiquer avec notre serveur à partir de JavaScript. En effet, il suffit de créer un service WCF (ou asmx) de rajouter quelques attributs et on peut appeler nos WebMethods à partir de JavaScript. En interne une sérialisation JSON (JavaScript Object Notation) est utilisée.

Malheureusement il est impossible de customiser cette sérialisation. Pourquoi vouloir personnaliser cette sérialisation ? Prenons le cas d'une méthode GetPersons qui renvoie une centaine de personne.

Le JSON aura cette tête :

{"d": [ { "__type":"Person:#", "BirthDate":"\/Date(1211790363564+0200)\/", "City":"Chénas", "FirstName":"Steven", "LastName":"Vincent" },{ "__type":"Person:#", "BirthDate":"\/Date(1211790441107+0200)\/", "City":"Légny", "FirstName":"Janet", "LastName":"laurent" },{ // etc...

On peut voir que le nom des "colonnes" se répètent inutilement. L'idée serait d'avoir un JSON ayant cette tête :

{"d":{ "Columns":[ "FirstName", "LastName", "BirthDate", "City" ],"Values":[ ["Andrew","Alex","\/Date(1211790586937+0200)\/","Saint-Didier-sur-Beaujeu"], ["Laura","Claude","\/Date(1211790697591+0200)\/","Chénas"], ["Anne","Isabelle","\/Date(1211790655756+0200)\/","Saint-Cyr-le-Chatoux"], ["Nancy","Steph","\/Date(1211790592372+0200)\/","Sainte-Paule"], // etc

Pour arriver à un tél résultat, je ne retourne plus une List<Person> mais une JsonList<Person>. Cette JsonList<T> possède 2 propriétés : Columns de type IEnumerable<String> et Values de type IEnumerable<IEnumerable<Object>>.

Dans mon exemple, pour 100 personnes, on passe de 12.8 ko à 6.8 ko, soit presque 50%. Cette optimisation nous permet alors d'obtenir une application plus réactive.

Côté client, pour retrouver un tableau d'objet plutôt qu'un objet "bizarre", on pourra utiliser ce code :

// permet de retourner un tableau d'objet classique plutot qu'une objet bizarre var a = []; var propCount = result.Columns.length; var propNames = result.Columns; for(var i = 0, l = result.Values.length; i < l; i++){ var values = result.Values[ i ]; var o = {}; for(var j = 0; j < propCount; j++){ o[propNames[j]] = values[j] } a[a.length] = o; }

Le code et l'exemple de cette JsonList se trouve sur aspfr :  Optimisation de la sérialisation Json pour les List<T>

IIS et ASP.net 3.5 : avoir la bonne version du framework

Mais où est donc passé le framework 3.5 dans IIS ? En effet si l'on regarde dans IIS on voit qu'on peut choisir la version de ASP.net.

Sous IIS6 (Win 2003) :

Untitled2

Sous IIS7 (Vista) :

Untitled

On voit sur les captures que l'on peut choisir entre le framework 1.1 et 2.0 mais pas de framework 3.0 ni de 3.5, pourtant le framework 3.5 est bien installé.

C'est tout à fait normal ! Votre site .net 3.5 tournera très bien avec le framework .net 2.0, il n'y a rien à modifier.

Pour comprendre en quoi c'est normal, il faut d'abord se rappeler ce qu'est le framework 3.0 et 3.5 ; redo nous l'explique ici : qu'est-ce que le framework 3.5. Pour résumer le framework 3.x ne fait qu'apporter de nouvelles assemblies (des dll) à .net 2.0 ainsi que de nouveaux compilos (C#3 et VB9), .net 3.x se repose sur la CLR 2.0.

Lorsque vous créez un site .net 3.5, vous ne faites qu'utiliser ces nouvelles assemblies. Ces assemblies sont installées dans le GAC (Global Assembly Cache) lors de l'installation du framework 3.5, IIS n'aura alors aucun mal à les retrouver.
D'un point de vue IIS, votre site web 3.5, reste un "site 2.0" ayant un web.config un peu spécial (mais parfaitement conforme 2.0). Ce web.config référencie les assemblies de .net 3.5 ainsi qu'un nouveau compilo. Si vous voulez comprendre les différents éléments du web.config d'un site .net 3.5, je ne peux que vous conseillez l'article de Scott Mitchell : Dissecting ASP.NET Version 3.5's Web.config File

Il faut voir la combobox de choix du framework au niveau de IIS comme un choix de la CLR, et non comme le choix du framework !

Nested Type et Ajax-enabled WCF service => bug

Dans le cadre d'un test, j'ai récemment fait un service WCF qui ressemblait à peu près à ça :

[ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class Service { [DataContract] public class Person { [DataMember] public String FirstName { get; set; } } [OperationContract] [WebGet] public Person GetPerson() { return null; } }

J'ai donc rajouté une référence vers ce service au niveau de ma page pour que je puise accéder à ce service WCF en JavaScript.

<asp:ScriptManager runat="server"> <Services> <asp:ServiceReference Path="~/Service.svc" /> </Services> </asp:ScriptManager>

Lorsque l'on rajoute une référence vers un service WCF (ou asmx) via le ScriptManager. Lors de la phase de rendering, on ajoute un script vers /service.svc/js ce fichier contient le proxy nous permettant de dialoguer avec le WebService.

Dans le cas d'un nested type, le proxy généré contient ça :

var Service.Person=gtc("Service.Person:http://schemas.datacontract.org/2004/07/");

Ce qui fait évidemment planter le proxy puisque l'on ne peut pas déclarer une variable contenant le signe "."

Ce n'est pas très génant puisqu'on doit très rarement renvoyer des nested type (et je ne trouve pas ça propre). Je viens néanmoins d'ouvrir un bug sur connect : nested type in WCF with JSON Serialization

Mis à niveau d'une assembly VB .net 2.0 vers .net 3.5

Visual Studio 2008 permet de créer des projets pour le framework 2.0, 3.0 et 3.5

Untitled

.net 3.x n'étant que l'addition de quelques nouvelles assemblies, le choix d'utiliser tel ou tel framework au niveau de Visual Studio ne fait que restreindre les assemblies disponible lorsqu'on veut ajouter une référence.

Untitled2

Lorsqu'on a un projet de type C#, on peut facilement mettre à niveau le framework utilisé dans les propriétés du projet :

Untitled3

Par contre en VB, pas moyen de trouver cette option :

Untitled4

La seule solution que j'ai trouvée est de modifier le .vbproj en rajoutant la clé TargetFrameworkVersion

<Project ... > <PropertyGroup> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>

[Update]

Suite au commentaire de actionthomas, l'option se trouve dans l'onglet "Compile" puis "Advanced Compile Options"

Reflector : la fonction "Analyze" - analyser le fonctionnement d'un code .net

Beaucoup de personnes ne savent pas utiliser le maximum de Reflector, certains ne connaissent même pas cet excellent outil. Pour lutter contre cette ignorance, j'ai écrit il y a quelques temps un tutorial de présentation de cet outil : Reflector : un décompilateur .net 

J'aimerais revenir avec un exemple concret d'utilisation de la fonction "Analyze".

Imaginons que vous avez réussi à obtenir un fichier qu'une application .net utilise (en analysant les accès disques via filemon par exemple). Malheureusement, ce fichier est chiffré, il ne vous est donc pas possible de voir le contenu de celui-ci. Comme vous êtes tenace et curieux, vous décider d'analyser l'application .net avec Reflector.
Il y a deux solutions :

  1. soit vous utilisez le plugin CodeSearch pour trouver la méthode qui écrit le fichier, puis vous analysez cette méthode,
  2. soit vous êtes plus astucieux et vous décidez de vous servir de la fonction "analyze" de reflector.

Qu'est-ce que la fonction "Analyze" de Reflector ? C'est une fonction qui permet de savoir qui utilise une méthode, une classe, voir même une assembly.

Untitled

A partir de là, on peut trouver toutes les méthodes qui utilise une méthode particulière.

Dans notre cas, on tente notre chance avec une classe de chiffrement du framework .net : Rijndaël. On s'aperçoit vite que cette classe est utilisée par notre assembly obfusqué. 

Untitled2

Malheureusement la méthode est obfusqué et Reflector ne parvient pas à convertir le code MSIL de cette méthode vers du C#, on pourrait lire le code MSIL mais avant on va essayer d'être plus astucieux. En effet  nous savons que pour utiliser la classe RijndaelManaged il faut une clé et une "IV" qui sont des Byte[], or on voit dans notre classe qu'il y a deux champs de type Byte[], regardons alors dans le constructeur de cette classe afin de vérifier si ces bytes n'ont pas été initialisé.

Untitled3

Nous avons donc réussit à obtenir très facilement la clé de chiffrement du fichier.

Comment complexifier le travail du vilain pirate ?

  • Essayez de pirater votre code et voyez comment complexifier cette tâche.
  • Utilisez un obfuscateur qui chiffre les strings, cela complexifiera la tache du pirate ; la version pro de dotfuscator le fait
  • N'inscrivez pas la clé en dur dans votre code. Si votre application s'utilise avec un serveur, envoyez la clé seulement lorsque celle-ci est nécessaire !

Je ne suis pas expert en sécurité .net et je n'ai jamais eu cette problématique, si vous connaissez d'autres façon de protéger votre code, merci de partager votre expérience dans les commentaires.

 


Attention, ce post explique comment vérifier si son application est facilement piratable, je ne peux en aucun cas être tenu responsable de vos agissements.

ScriptManager.RegisterDataItem - envoyer des données à travers un UpdatePanel

Je viens de découvrir la méthode ScriptManager.RegisterDataItem, cette méthode permet de transférer des données entre le serveur et le client lors d'un AsyncPostback. Pour ceux qui ne sont pas familier avec les asyncpostback, un asyncpostback reprend le mécanisme d'un postback classique sauf qu'il se fait via Ajax (XMLHttpRequest) et ne rafraichit que certaines parties de la page, ces zones sont définies via les UpdatePanels.

En plus d'envoyer le contenu HTML des UpdatePanels à rafraichir, la méthode RegisterDataItem permet de transférer les données que l'on souhaite. Cette méthode dispose de deux signatures, soit l'on envoie un simple string, soit on envoie un objet complexe sérialiser en JSON.

// permet de renvoyer un simple string associé au ClientID d'un contrôle ScriptManager1.RegisterDataItem(Label1, DateTime.Now.ToString()); // permet d'envoyer des données au format JSON JavaScriptSerializer serializer = new JavaScriptSerializer(); Person p = new Person("Cyril", "Durand"); ScriptManager1.RegisterDataItem(Label2, serializer.Serialize(p), true);

Pour récupérer les données côtés clients, il faut s'abonner à l'événement pageLoading du pageRequestManager :

Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(function(sender, args){ var dataItems = args.get_dataItems(); var serverDate = dataItems['ClientID_Label1']; var person = dataItems['clientID_Label2']; });

Je trouve néanmoins dommage que la clé soit un objet de type Control et non un simple String, en effet, cela nécessite de connaitre le clientID du contrôle ce qui n'est pas toujours facile.

[MSIL] surcharge de méthode avec même signature mais type de retour différents

En C# il n'est pas possible d'avoir deux méthodes ayant une signature identique qui différent seulement par le type de retour.

public class Foo { public int Bar() { return 0; } public String Bar() { return "pouet"; } }

En MSIL c'est possible. Le code ci dessous compile parfaitement.

.assembly Foo{} .module Foo.dll .class public CFoo { .method public static int32 Bar() { ldc.i4.0 ret } .method public static string Bar() { ldstr "Toto" ret } }

Lorsqu'on fait un appel de méthode en MSIL, on spécifie le nom complet de la méthode :

call int32 CFoo::Bar() // ou call string CFoo::Bar()

Que se passe t'il en C# ? Comment spécifier la méthode que l'on veut utiliser ?

Untitled

L'IntelliSense de Visual Studio nous propose la méthode Bar qui retourne un String, mais si nous utilisons cette méthode.

String s = CFoo.Bar();

Nous avons une erreur de la part de compilation :

Untitled2

J'ai essayé en castant la méthode (String)CFoo.Bar() mais même erreur. J'ai tenté VB, l'IntelliSense ne me propose même pas de méthode Bar.

Quelle est alors l'utilité de ce type de surcharge ? J'en ai trouvé qu'une seule, cela permet aux obfuscateurs de complexifier le code. En effet, si notre classe possède des méthodes GetPerson() et GetCompany() qui retournent respectivement une List<Person> et une List<Company>, un obfuscateur pourra utiliser le même nom pour renommer ces méthodes.

Est-ce que C# doit pouvoir utiliser/créer de tels surcharges ?

Techniquement, l'inférence de type nous permettrait de ne pas avoir trop d'ambigüité, si cela ne suffit pas on pourra toujours caster explicitement, afin de séléctionner la bonne méthode.
Conceptuellement, bien que dans certains cas cela puisse être utile, je ne pense pas que cela soit une bonne chose, cela risque de complexifier le code pour peu. L'utilisation des generics, nous permet dans la majorité des cas d'obtenir des solutions alternatives.

Et vous qu'en pensez vous ? Est-ce que le futur compilateur devra prendre ce cas en compte ?

Modification header HTTP d'une requête Ajax vers un service WCF / ASMX Ajax

Lorsque l'on fait une requête vers un service WCF / ASMX "Ajax enabled" il se peut que l'on ait besoin de passer des paramètres qui ne sont pas propres à la méthode, par exemple un ticket d'authentification, ...

Ajout d'un paramètre dans le header de la requête (donc coté client) :

Pour ajouter un paramètre dans le header HTTP de la requête il faut s'abonner à l'événement invokingRequest.

Sys.Net.WebRequestManager.add_invokingRequest(function(){ e._webRequest._headers['CacheID'] = ... });

Côté serveur on peut relire le paramètre via :

[WebMethod] [ScriptMethod(UseHttpGet = true)] public List<Person> GetPersons() { Guid cacheID = Guid.Empty; try { cacheID = new Guid(HttpContext.Current.Request.Headers["CacheID"]); } catch { cacheID = Guid.NewGuid(); } }

Ajout d'un paramètre dans le header de la réponse (donc côté serveur) :

[WebMethod] [ScriptMethod(UseHttpGet = true)] public List<Person> GetPersons() { // ... HttpContext.Current.Response.AddHeader("CacheID", cacheID.ToString()); }

Ensuite pour lire ce paramètre côté client :

DataService.GetPersons(function(persons){ var cacheID = null; try { // ca ressemble à un bug Atlas ... cacheID = this._WSRequest._executor.getResponseHeader('CacheID'); } catch (ex) { } // ... });

Je me suis servi de cette astuce pour gérer une sorte de cache, session cliente afin de ne pas renvoyer les informations que le serveur m'avait déjà envoyé.

Communication grille extjs vers WCF ou WebService ASMX "Ajax" via JSON

Je suis actuellement en train d'utiliser le framework extjs.com. Parmi les contrôles de ce framework il existe un contrôle Ext.grid.GridPanel qui, comme son nom l'indique affiche une grille coté client (démo grid extjs).
Pour ceux qui ne connaissent pas extjs, je vous conseille vivement de faire un tour sur la page de démos de ce framework : démos des contrôles extjs, vous risquez d'être étonné.

Comment binder le contrôle grid avec un service WCF ou ASMX "Ajax Enabled" ?

Pour binder un contrôle à une source de donnée, extjs a un mécanisme de "store", celui-ci s'occupe de rechercher les données, les transformer en une donnée utilisable par les contrôles, etc...
Puisqu'un service WCF est capable de nous générer du JSON, le store qui nous intéresse est le JsonStore. Ce "store" permet de faire une requête XMLHttpRequest vers une url nous retournant du JSON. Le JsonStore effectue une requête GET, par défaut WCF n'autorise pas les requêtes GET, il faut donc rajouter l'attribut [WebGet] au niveau du service WCF afin d'autoriser ce verbe.

Voici à quoi ressemble notre service WCF.

[WebGet] [OperationContract] public List<Person> GetPersons() { return new List<Person>() { new Person(){FirstName = "Cyril", LastName = "Durand", BirthDate = new DateTime(1986, 1, 31), Company = "Freelance"}, new Person(){FirstName = "Toto", LastName="Pouet", BirthDate = new DateTime(1900, 1, 1), Company="Student"} }; }