Comment journaliser dans un flux UDP visible du réseau local avec NLog

Pressés? Sautez directement à la solution.

Le contexte

La librairie NLog est très bien documentée sur Internet pour une utilisation régulière. Par exemple, la configuration suivante (fichier NLog.config) permet de journaliser dans un fichier portant la date du jour dans un répertoire logs en mode Trace :

<nlog xmlns= »http://www.nlog-project.org/schemas/NLog.xsd »
      xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance »
      xsi:schemaLocation= »http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd »
autoReload= »true »
throwExceptions= »false »
internalLogLevel= »Off » internalLogFile= »c:\temp\nlog-internal.log »>

  <targets>
<target xsi:type= »File » name= »f » fileName= »${basedir}/logs/${shortdate}.log »
layout= »${longdate} ${uppercase:${level}} ${message} ${when:when=(level = LogLevel.Error):inner=${newline}${exception:format=ToString}} » deleteOldFileOnStartup= »false » />
</targets>
<rules>
<logger name= »* » minlevel= »Trace » writeTo= »f » />
</rules>
</nlog>

Par contre, la journalisation dans un fichier n’est pas toujours pratique. Par exemple, si vous développez une composante serveur, il se peut que les équipes souhaitant consulter le journal n’aient pas un accès physique au répertoire contenant le fichier. Aussi, le compte Windows configuré pour le processus (EXE) qui exécute le code qui journalise doit avoir les droits pour écrire ce fichier.

Je me suis donc mis à chercher une solution qui permettrait de visualiser le journal à distance, à partir d’un poste client, sans passer par un fichier.

La démarche

Je savais que la librairie NLog pouvait être utilisée avec un outil de visualisation de journal tel que Sentinel ou Log2Console. Il suffit d’ajouter la cible suivante et  de l’ajouter dans les règles :

<target xsi:type= »NLogViewer » name= »v » address= »udp://127.0.0.1:9999″/>

<logger name= »* » minlevel= »Trace » writeTo= »v » />

Mais cela fonctionne uniquement lorsque l’outil de visualisation est installé et exécuté localement sur le serveur. Ce n’est pas très avantageux vis-à-vis l’utilisation d’un fichier.

Ma première idée a été d’utiliser une adresse IP multicast (224.0.0.0 à 239.255.255.255) pour le flux UDP. Malheureusement, cela n’a pas fonctionné lors de mes tests. J’ai épuisé toutes mes idées de combinaisons de mots clé à rechercher sur Google pour NLog et multicast et je n’ai rien trouvé.

L’avantage d’un projet à code source libre est que la meilleure documentation reste encore le code en soi. J’ai découvert que les cibles Chainsaw et NLogViewer étaient des spécialisations de la cible Network :

image

Je suis donc allé étudier le code pour voir s’il n’y avait pas une manière de diffuser le flux UDP sur le réseau, plutôt que de toujours fonctionner localement. Lorsque la classe NetworkTarget est configurée avec une adresse UDP (udp://ip:port), sa fabrique de “NetworkSender” crée un UdpNetworkSender. C’est dans cette classe que se cachait le secret pour diffuser le journal sur le réseau local: la méthode CreateSocket contient une condition intéressante:

Uri uri;

if (Uri.TryCreate(this.Address, UriKind.Absolute, out uri) && uri.Host.Equals(IPAddress.Broadcast.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
proxy.UnderlyingSocket.EnableBroadcast = true;
}

La documentation de la propriété EnableBroadcast indique clairement qu’il peut s’agir d’une technique pour diffuser le journal:

Broadcasting is limited to a specific subnet, and must use User Datagram Protocol (UDP.) For Internet Protocol version 4, you can broadcast to your local subnet by sending a packet to 255.255.255.255; or you can use the directed broadcast address, which is the network portion of an Internet Protocol (IP) address with all bits set in the host portion. For example, if your IP address is 192.168.1.40 (a Class C address, with a netmask of 255.255.255.0 — the network portion is the first three octets, and the host portion is the last octet), your directed broadcast address is 192.168.1.255.

Et la condition de la méthode CreateSocket teste si l’adresse de la cible est égale à IPAddress.Broadcast (255.255.255.255) avant de la mettre à true. Bingo!

La solution

La recette est donc la suivante :

<nlog xmlns= »http://www.nlog-project.org/schemas/NLog.xsd »
      xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance »
      xsi:schemaLocation= »http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd »
autoReload= »true »
throwExceptions= »false »
internalLogLevel= »Off » internalLogFile= »c:\temp\nlog-internal.log »>

  <targets>
<target xsi:type= »Chainsaw » name= »c »  address= »udp://255.255.255.255:9998″ />
</targets>
<rules>
<logger name= »* » minlevel= »Trace » writeTo= »c » />
</rules>
</nlog>

Personnellement je trouve que l’outil Log2Console est moins long à mettre en place sur le poste client. Il suffit de configurer un Receiver sur le même port que celui spécifié pour le « target » du programme qui utilise NLog pour journaliser :

clip_image002

image

Par contre, Sentinel offre plus d’options et permet de sauvegarder la configuration dans un fichier pour une utilisation ultérieure. Voici les étapes :

clip_image002[4]

image

image

image

image

image

image

image

Projet de démonstration

J’ai publié un projet de démonstration sur mon GitHub que vous pouvez utiliser pour tester les cibles NLog décrites dans cette entrée de blogue.

image

En cliquant sur le bouton, le journal contiendra une entrée pour chaque niveau de log supporté par NLog. ll y a trois cibles: File, NLogViewer et Chainsaw. La première cible crée un fichier portant la date du jour dans le répertoire logs. La seconde peut être utilisée avec un outil de visualisation de journal, s’exécutant sur le même poste. La dernière cible démontre l’utilisation de la fonctionnalité de Broadcast en UDP et il est alors possible d’exécuter le programme de visualisation de journal sur un autre poste.

<target xsi:type= »File » name= »f » fileName= »${basedir}/logs/${shortdate}.log »
layout= »${longdate} ${uppercase:${level}} ${message} ${when:when=(level = LogLevel.Error):inner=${newline}${exception:format=ToString}} » deleteOldFileOnStartup= »false » />

<target xsi:type= »NLogViewer » name= »v » address= »udp://127.0.0.1:9999″/>

<target xsi:type= »Chainsaw » name= »c »  address= »udp://255.255.255.255:9998″ />

La conclusion

J’aurais préféré trouver une solution utilisant le protocole UDP en mode multicast. Les routeurs sont intelligents et peuvent envoyer les packets uniquement aux ordinateurs “inscrits” pour un groupe multicast (une adresse IP). De plus, l’outil Log2Console a un champ pour inscrire l’adresse de ce groupe dans sa configuration:

image

Malheureusement je n’y suis pas arrivé. Je préfère ne pas avoir à modifier le code source de NLog alors je devrai me contenter de l’approche utilisant le Broadcast.

Publicités

Inclure JQuery 2.x pour les fureteurs récents et 1.x pour les récalcitrants

L’équipe derrière JQuery a pris la décision de laisser tomber le support pour Internet Explorer 6, 7 et 8 dans la branche 2.x.

Pour l’explication officielle, veuillez consulter jQuery Core: Version 1.9 and Beyond. En voici un extrait:

There’s just one thing interfering with our vision of the future, and that’s the ghost of browsers past. Internet Explorer 6, 7, and 8–collectively, oldIE–have been a thorn in the side of web developers for a decade.

L’API de la branche 2.x est compatible avec l’API de la branche JQuery 1.9+ qui supporte toujours les vielles versions.

Our goal is for 1.9 and 2.0 to be interchangeable as far as the API set they support. When 2.0 comes out, your decision on which version to choose should be as simple as this: If you need IE 6/7/8 support, choose 1.9; otherwise you can use either 1.9 or 2.0.

Cependant, pour y arriver, cette branche doit faire plusieurs pirouettes et tours de magie et cela complique le code et rend plus difficile son évolution. Ou encore mieux formulé (par l’équipe de JQuery):

If jQuery 1.9 and 2.0 are basically the same API, what makes 2.0 compelling?

Smaller size, better performance, and the lack of problems introduced by the need for oldIE support. We expect that we can improve error handling in the $.Deferred implementation in 2.0, for example, whereas we can’t do that as long as oldIE is supported.

En tant que développeur, on veut souvent profiter des dernières améliorations nous permettant d’écrire du code concis, efficace et avec le moins de défaut possible. On veut aussi minimiser le nombre de tests à effectuer. Tester un site pour IE 11, IE 10, IE 9 et Chrome est déjà beaucoup. Si en plus il faut tester avec IE 8, IE7 et IE6, cela rend les choses encore plus compliquées.

Même les grosses compagnies avec de gros comptes de banque et beaucoup d’employés préfèrent supporter les fureteurs récents. Par exemple, Google a abandonné le support pour IE9 quelque temps après la sortie de IE 11:

Google Drops Support for IE9

Alors si vous souhaitez travailler avec les dernières versions des fureteurs et la dernière version de JQuery lors du développement mais garder une porte ouverte pour supporter les vieux fureteurs si on client l’exige, voici un petite recette.

Recette pour inclure automatiquement la dernière version de JQuery dans un application ASP.Net MVC

  1. Ajoutez la dernière version de JQuery 2.x à votre projet ainsi que la dernière version de JQuery 1.ximage
  2. Dans la classe BundleConfig, ajoutez deux bundles:
    public static void RegisterBundles(BundleCollection bundles)
    {
    bundles.Add(new ScriptBundle(« ~/bundles/jquery1 »).Include(« ~/Scripts/jquery-1* »));
    bundles.Add(new ScriptBundle(« ~/bundles/jquery2 »).Include(« ~/Scripts/jquery-2* »));
    (…)
  3. Définissez une vue partielle nommée _JQueryInclude.cshtml et contenant les 5 lignes suivantes:<!– Conditionally include jQuery version based on IE version –>
    <!–[if lt IE 9]>
    @Scripts.Render(« ~/bundles/jquery1 ») <![endif]–>
    <!–[if gte IE 9]><!–>
    @Scripts.Render(« ~/bundles/jquery2 ») <!–<![endif]—>
  4. Dans votre layout page ou à l’endroit où vous désirez inclure JQuery, utilisez:@Html.Partial(« _JQueryInclude »)

Et voilà! Si vos utilisateurs utilisent IE 6, IE 7 ou IE 8, les pages HTML utiliseront la branche 1.x de JQuery. Autrement, la version moderne de la branche 2.x sera utilisée.

Quelques nouveautés MVC 4

Je suis en train de lire le livre Programming ASP.NET MVC 4 de O’Reilly. Il y a vraiment beaucoup de nouveautés vis-à-vis MVC 3 mais ce que vous savez n’est pas perdu. Vous pouvez commencer par intégrer les nouvelles capacités à votre rhytme. Cet article vise à énumérer certains ajouts, il ne vise par à décrire toutes les nouveautés.

Razor V2

Les développeurs de Microsoft ont mis l’engin Razor à jour. Il faut l’activer dans le Web.Config

<appSettings>
<add key= »webpages:Version » value= »2.0.0.0″ />
<add key= »webpages:Enabled » value= »true » />

Razor V2: Résolution d’URL

Il n’y a pas tant d’ajouts mais ils sont pratiques. Par exemple, vous pouvez dire bye bye à @Url.Content ou @Href

<script src=”@Url.Content(“~/Scripts/myscript.js”)”></script>

devient:

<script src=”~/Scripts/myscript.js”></script>

Cela fonctionne pour tous les attributs, pas de traitement spécial!

<html data-root=”~/”>
$(document).data(‘root’)
en JQuery

Et avec une expression complexe:

<a href=”~/MySite/@Foo/Bar/@Baz”>Something!</a>

Razor v2: Simplifier le code fréquent

Mettre un attribut ou non selon une condition

<input type=”checkbox” checked=”@isChecked”>

Si isChecked est true:

<input type=”checkbox” checked=”checked”>

Sinon:

<input type=”checkbox”>

Mettre un attribut ou non si non null

@{ string foo = null; string bar = “bar” }
<div id=”foo” class=”@foo @bar”>

devient:

<div id=”foo” class=”bar”>

car foo est null (l’espace entre foo et bar est même enlevé!)

Exceptions:

  1. Les attributs data-<nom> car ceux-ci sont utilisés avec JQuery et doivent garder la notion de null
  2. Si la variable vaut String.Empty au lieu de null

Bundles

Ils ont ajouté la capacité de donner un nom à un ensemble de fichiers Scripts ou CSS et de les inclure au HTML en utilisant le nom. Cela crée une indirection et facilite la maintenance en permettant d’ajouter ou enlever des scripts à un seul endroit.

Objectifs

  1. Simplifier l’inclusion de scripts
  2. Combiner des fichiers pour réduire le nombre de connexions au serveur Web
  3. Minifier le contenu pour réduire la taille

Terminologie

  1. Bundling : Process of combining all the different JavaScript and CSS files into one file (one for JavaScript and one for CSS).
  2. Minification : Process of shrinking the JavaScript  and CSS content (removing all of the carriage returns,empty spaces,line feeds & shortening the variables  without changing actual functionality).

Fonctionnement

  1. Il y a un nouveau fichier pour configurer les bundles sous App_Start:

image

  • Pour déclarer un bundle de scripts:bundles.Add(new ScriptBundle(« ~/bundles/jquerylib« ).Include(« ~/Scripts/jquery-ui-{version}.js », « ~/Scripts/jquery.unobtrusive* », « ~/Scripts/jquery.validate.js », « ~/Scripts/additional-methods.js »));
  • Facilitants pour utiliser la dernière version{version} ou pattern matching (*) – détails plus loin
  • Pour inclure un bundle de scripts:@Scripts.Render(« ~/bundles/jquerylib »)
  • Pour déclarer un bundle de styles:bundles.Add(new StyleBundle(« ~/Content/themes/base/css »).Include(« ~/Content/themes/base/jquery.ui.core.css »,
    « ~/Content/themes/base/jquery.ui.resizable.css »,
    « ~/Content/themes/base/jquery.ui.tabs.css »,
    « ~/Content/themes/base/jquery.ui.theme.css »));
  • Pour inclure un bundle de styles:@Styles.Render(« ~/Content/themes/base/css »)

 

Notes sur le path virtuel (nom du bundle)

  1. Il est arbitraire mais ceux par défaut sont bizarres

You can also use, new StyleBundle(« ~/css ») and new ScriptBundle(« ~/script ») provided that you also need to change your Layout/Master file to match the new key(css/script) as Scripts.Render(« ~/script ») and Styles.Render(« ~/css », « ~/Content/css »). The fact is that the name must need to match.

  • Le path virtuel ne doit pas référer à un nom d’un fichier existant

    You also need to make sure that there is no file/directory exist with this new key(css/script).
  • Il fera partie de l’URL virtuel qui retourne le contenu du bundle

 

Facilitants: {version}

~/Scripts/jquery-ui-{version}.js

  1. Sélectionne la dernière version de JQuery du projet
  2. Prend la version complète en debug et la version .min autrement<system.web>
    <compilation debug= »true » targetFramework= »4.0″ />
  3. Fonctionne uniquement sur le nom des fichiers et fonctionne via une expression régulière. Je pensais qu’il dépendant du fichier packages.config géré par NuGet, mais cet article semble dire qu’il ne s’agit que d’une expression régulière sur les fichiers du projet ASP.Net.

Facilitants: *

~/Scripts/jquery.validate*

  1. Préfixe ou suffixe de la dernière partie du path
  2. Marche avec .Include et .IncludeDirectory
  3. Case-Insensitive

Exemple du HTML en release

<link href= »/Site/Content/css?v=-ag4GKGu1OPL0-OcBskTm1_ir8lgMDmnaLJg6M00Q-A1″ rel= »stylesheet »/>

<link href= »/Site/Content/themes/base/css?v=58KZ5iata6ouERaCYSZC3XN4JTpS9kDRB9t3LBZ1Ue81″ rel= »stylesheet »/>

<script src= »/Site/bundles/modernizr?v=rGcoDow97GYrNMSwHq7xCCjlcB3UIY4_OhPRc6BBSQA1″></script>

<link href= »/Site/Content/tablesorter/syntell/css?v=Gwg660DZtJBqCmEKqEhqrVLIAnDqfWVy95qmBX5OGCk1″ rel= »stylesheet »/>

Autres fonctionnalités

  1. Les contrôleurs peuvent être dans des répertoires différents
  2. Support pour les méthodes asynchrones du .NET 4.5
  3. Authentification via Oauth et OpenID (ex. Facebook)
  4. Séparation des classes dans App_Start (BundleConfig, FilterConfig, RouteConfig, WebApiConfig)
  5. Mode d’affichage: des vues différentes seront sélectionnées selon le fureteur faisant la requête
  6. Support le Azure SDK 1.6+
  7. MVC 4 inclus Entity Framework
  8. Projet de type Web APIimage
  9. Projet de type mobile (JQuery Mobile)image
  10. Le projet vide est maintenant vide, l’ancien s’appelle Basic
  11. Les gabarits par défaut ont un nouveau look et utilisent le rendu adaptif

Références

Bundling and Minification in ASP.NET MVC 4
http://theshravan.net/blog/bundling-and-minification-in-asp-net-mvc-4/

What’s new in Razor v2
http://vibrantcode.com/blog/2012/4/10/whats-new-in-razor-v2.html

What else is new in Razor v2?
http://vibrantcode.com/blog/2012/4/13/what-else-is-new-in-razor-v2.html

{version} wildcard in MVC 4 Bundle
http://stackoverflow.com/questions/12029161/version-wildcard-in-mvc4-bundle

ASP.NET 4.5, ASP.NET MVC 4, ASP.NET Web Pages 2, and Visual Studio 2012 for web developers: Part 1
http://weblogs.asp.net/jgalloway/archive/2012/08/16/asp-net-4-5-asp-net-mvc-4-asp-net-web-pages-2-and-visual-studio-2012-web-developer-features.aspx

Caractères accentués, ASP.Net MVC Razor et boutons de dialogue JQuery UI

Développeurs MVC francophones: avez-vous déjà eu des problèmes avec des caractères accentués dans les boutons d’un dialogue JQuery UI?

Afin de développer l’application Web en anglais et français, les libellés des boutons sont stockés dans des fichiers de ressources (*.resx).

Tout fonctionnait avec des boutons comme Sauvegarder, Annuler, OK etc. Mais tout à coup, la technique habituelle échoue avec un bouton Générer …

image

Et s’il n’y avait pas de guillement pour la clé (Gén&/233;rer: function() { … ), c’est pire car cela fera planter votre script.

La solution est d’utiliser la méthode @Html.Raw et d’entourer le résultat avec des guillements:

$(« #dlgGenerator »).dialog({
  autoOpen: false,
  width: 800,
  height: 600,
  resizable: false,
  modal: true,
  open: function(event, ui) {
    // Initialization
  },
  buttons: {
    « @Html.Raw(Global.LblGenerate) »: function() {
      $(« #cmdGenerateAssignments »).click();
    },
    « @Html.Raw(Global.LblCancel) »: function() {
      $(this).dialog(« close »);
    }
  }
});

image

Il est peut-être sage de vous protéger tout de suite d’un changement de libellé éventuel! L’utilisation de Html.Raw vous protège des caractères accentués tandis que l’utilisation des guillemets empêchera les erreurs de script dûes à la présence d’un espace ou caractère illégal.

À la prochaine fois,

SharePoint 2013 en mode Claims. Pourquoi IIS est configuré en Anonymous, Forms et Windows Authentication?

Le constat

Regardez la configuration de la section Authentification d’un site “SharePoint – 80” ajouté par SharePoint 2010 lors de la création d’une application Web:

Pourtant, lors de sa création, les options suivantes étaient cochées:

Les mêmes choix avec SharePoint 2010 auraient donné ceci dans IIS:

Pourquoi?

L’authentification Anonymous peut être utilisée conjointement à l’authentification Windows. En gros, l’utilisateur rattaché (ex. IUSR) sera utilisé et s’il n’a pas accès, IIS retombe vers l’autre mode configuré (ex. Windows Authentication). Vous pouvez en apprendre plus sur ce sujet ici.

Mais pourquoi SharePoint en a-t-il besoin? Il semblerait que ce soit un pré requis de Windows Communication Foundation (WCF) sur lequel SharePoint est basé.

Bon d’accord. Mais qu’en est-il de l’utilisation conjointe de Forms et Windows Authentication? Même IIS s’en plaint:

L’explication que je cherchais se trouve ici. SharePoint 2010+ en mode claims supporte dorénavant plus d’un mode d’authentification par zone AAM. Voici un court résumé:

1. SharePoint ajoute un HTTP Module (SPRequestModule, fonctionnant en mode IIS 7+ integrated mode) qui est le premier dans la liste et s’assure que l’utilisateur est authentifié (si c’est requis).

2. C’est pour ça qu’on peut avoir plusieurs options normalement exclusives dans IIS (Ex. Forms et Windows Authentication). Le module a le loisir de proposer le choix à l’utilisateur non authentifié.

3. Par défaut, l’utilisateur se fait demander ce qu’il veut utiliser :
clip_image002

4. Mais c’est configurable pour chacune des zones AAM de l’application Web:

signinpage

5. Il serait donc possible de développer une composante qui ferait ce choix automatiquement en se basant sur un critère X, par exemple l’adressse IP d’origine.

Cela permettrait d’avoir la même adressse à l’interne et à l’externe de la compagnie et utiliser des modes d’authentification différents plutôt que d’avoir à utiliser des TMG, ISA etc.

Ainsi les employés n’ont pas à retenir à une adresse pour l’interne et une autre pour l’externe.

Vous pouvez trouver des exemples de code en référence. Il y a aussi un article qui détaille la manière pour configurer l’authentification par formulaires.

Nouvelles capacités

Voici une image qui illustre une zone AAM avec plusieurs types d’authentification.

clip_image001

Et une autre qui présente l’ancienne manière (toujours supportée), c’est à dire un mode d’authentification par zone AAM:

clip_image001[4]

Références

Cet article n’est qu’un résumé (et une traduction) de plusieurs autres articles dont j’ai tiré les images (merci aux auteurs). Les voici:

Understanding anonymous authentication and the IUSR account
http://helpx.adobe.com/dreamweaver/kb/anonymous-authentication-iusr-account.html

SharePoint – Claims-based web applications require anonymous authentication in IIS
http://support.microsoft.com/kb/2850515

Anonymous Authentication always on in SharePoint 2013
http://blog.blksthl.com/2012/11/02/anonymous-authentication-always-on-in-sharepoint-2013/

Multiple Authentication Methods in SharePoint 2010
http://shpt2010.wordpress.com/2011/11/10/multiple-authentication/

Configuring Forms Based Authentication in SharePoint 2013
http://sharepointsolutions.blogspot.ca/2012/08/configuring-forms-based-authentication.html

SharePoint 2010 custom login page
http://tomaszrabinski.pl/wordpress/2011/06/23/sharepoint-2010-custom-login-page/

SharePoint 2010 automatic sign-in with mixed authentication
http://spautomaticsignin.codeplex.com/

SharePoint 2010: transparent login with mixed authentication
http://www.orbitone.com/en/blog/archive/2010/06/23/sharepoint-2010-mixed-authentication-automatic-login.aspx

SharePoint 2013: Access Denied to Root Site of Web Application
http://www.dmcinfo.com/latest-thinking/blog/articletype/articleview/articleid/8565/sharepoint-2013-access-denied-to-root-site-of-web-application.aspx

Recette améliorée: Intégrer une application ASP.Net MVC sous un site Web IIS hébergeant WSS 3.0 / SharePoint 2007

L’avantage de la recette précédente c’est qu’elle était simple et exigeait peu de modification au Web.Config des applications ASP.Net MVC. Mais elle avait l’inconvénient majeur de faire échouer plusieurs soutions (ex. solution qui ajoute des Safe Controls) car l’API de SharePoint ne semble pas supporter les clauses <location path= ». »> d’IIS. Il fallait donc enlever les location du web.config, procéder à l’installation puis les remettre.

Voici donc la nouvelle recette (l’étape 5 est enlevée et l’étape 7 a été ajoutée):

  1. Vous devez créer un « Application Pool” utilisant le “.NET Framework 4.0” et empruntant la même identité (le même compte de service) que l’ “Application Pool” de SharePoint.Puisque le site IIS a un SPN unique (ex. HTTP/nomgentil.domaine.com), celui-ci est déjà défini sur le compte de service en question. Ainsi, il suffira d’ajouter la déléguation vers SQL Server ou SSAS à ce compte selon ce que fait l’application ASP.Net.
  2. Vous devez copier le répertoire de votre application ASP.Net sous le répertoire contenant SharePoint. Ex. C:\Inetpub\wss\nomgentil.domain.com\ApplicationMVC
  3. Les permissions par défaut ne sont pas les même que pour les sites placés directement sous C:\Inetpub alors assurez-vous de donner les permissions Windows à Users ou autre au répertoire de votre application MVC.
  4. Il faut convertir le virtual directory en application dans IIS en prenant bien soin d’utiliser le pool d’application défini précédemment.
  5. Une application ASP.Net utilise un fichier Web.Config afin de configurer différents éléments propres à .Net. Ce qu’il faut savoir c’est qu’il y a un lien d’héritage entre les Web.Config des applications Web. Ainsi si l’application B est placée sous l’application A dans IIS, elle hérite du Web.Config.Le problème c’est qu’en étant sous SharePoint, on hérite de certains éléments incompatibles avec une application utilisant le “.NET Framework 4.0”.

    On n’ajoute plus de clause <location> autour des noeuds SharePoint.

  6. On hérite aussi des “Handler Mappings” du site SharePoint et il y en a beaucoup. Il faut au moins enlever (dans l’application ASP.Net MVC) celui qui intercepte * et qui porte le nom AboMapperCustom-<numéro> pointant vers ASP.Net 2.0 (aspnet_isapi.dll). Autrement les images, CSS et autres fichiers statiques ne fonctionneront pas.image

    Cela aura pour effet d’ajouter ceci au web.config de votre application:

    <handlers>
    < remove name= »AboMapperCustom-3515455823″ />
    < /handlers>

  7. Les permissions du site MVC sont maintenant héritées du site SharePoint ce qui provoque une erreur dans l’application MVC. Il faut donc ajouter les permissions nécessaires en ajoutant l’élément <trust> dans le web.config de l’application. Ex. :

    <system.web>
    <trust level="Full" originUrl="" />
    </system.web>
  8. IIS Reset
  9. Si vous avez une erreur qui dit que le fichier de configuration n’est pas accessible, donnez temporairement les droits en lecture au groupe local IIS_IUSRS sur ce fichier (l’erreur indique le chemin d’accès).Quand IIS n’arrive pas à déterminer l’authentification utilisée (ex. il y a un nœud dupliqué dans les web.config parent et enfant), il doit avoir accès en mode anonyme. Après l’erreur qui sera affichée sera détaillée (ex. le nœud qui ne peut être redéfini etc.)
  10. Si vous avez une erreur HTTP 500 ou une page blanche, même avec customErrors à OFF, vous pouvez utiliser la fonctionnalité des Failed Request Tracing (FREB). Voici comment l’activer pour un site / application.

    Request Diagnostics HTTP 500

Si vous ne souhaitez pas donner FullTrust à l’application ASP.Net, vous devez:

  1. Ajouter:

    using System.Security;
    [assembly: AllowPartiallyTrustedCallers()]

    dans le fichier AssemblyInfo.cs des Dlls utilisés par votre application afin qu’ils acceptent d’être appelés dans un contexte limité ne permissions.

  2. Définir un fichier de policy listant toutes les permissions nécessaires à votre application (vous pouvez partir d’un fichier utilisé par SharePoint).
  3. Inclure le fichier spécifique en lui donnant un nom et y référer:

    <system.web>
      <securityPolicy>
        <trustLevel name= »MVCTrustLevel » policyFile= »<fullpath> » />
      </securityPolicy>
      <trust level= »MVCTrustLevel » originUrl= » » />
    </system.web>

Bonne chance et à la prochaine …

Références
Troubleshooting HTTP 500.19 Errors in IIS 7
http://blogs.iis.net/webtopics/archive/2010/03/08/troubleshooting-http-500-19-errors-in-iis-7.aspx

Recette améliorée: Intégrer une application ASP.Net MVC 3 sous un site Web IIS hébergeant SharePoint Server 2010

L’avantage de la recette précédente c’est qu’elle était simple et exigeait peu de modification au Web.Config des applications ASP.Net MVC. Mais elle avait l’inconvénient majeur de faire échouer plusieurs soutions (ex. solution qui ajoute des Safe Controls) car l’API de SharePoint ne semble pas supporter les clauses <location path= ». »> d’IIS. Il fallait donc enlever les location du web.config, procéder à l’installation puis les remettre.

Voici donc la nouvelle recette (l’étape 5 est enlevée et l’étape 8 a été ajoutée):

  1. Vous devez créer un “Application Pool” utilisant le “.NET Framework 4.0” et empruntant la même identité (le même compte de service) que l’ “Application Pool” de SharePoint.Puisque le site IIS a un SPN unique (ex. HTTP/nomgentil.domaine.com), celui-ci est déjà défini sur le compte de service en question. Ainsi, il suffira d’ajouter la déléguation vers SQL Server ou SSAS à ce compte selon ce que fait l’application ASP.Net.
  2. Vous pouvez copier le répertoire de votre application ASP.Net sous le répertoire contenant SharePoint. Ex. C:\Inetpub\wss\nomgentil.domain.com\ApplicationMVCNote: Vous pouvez le mettre ailleurs mais cela fonctionne très bien à cet endroit.
  3. Les permissions par défaut ne sont pas les même que pour les sites placés directement sous C:\Inetpub alors assurez-vous de donner les permissions Windows à Users ou autre au répertoire de votre application MVC.
  4. Il faut convertir le virtual directory en application dans IIS en prenant bien soin d’utiliser le pool d’application défini précédemment.
  5. Une application ASP.Net utilise un fichier Web.Config afin de configurer différents éléments propres à .Net. Ce qu’il faut savoir c’est qu’il y a un lien d’héritage entre les Web.Config des applications Web. Ainsi si l’application B est placée sous l’application A dans IIS, elle hérite du Web.Config.Le problème c’est qu’en étant sous SharePoint, on hérite de certains éléments incompatibles avec une application utilisant le “.NET Framework 4.0”. Les étapes 6, 7 et 8 vont travailler dans ce sens.

    On n’ajoute plus de clause <location> autour des noeuds SharePoint.

  6. Il faut déplacer l’élément Xml <sectionGroup> nommé “system.web.extensions” du Web.Config de SharePoint vers le Web.Config qui se trouve à cet endroit: C:\Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIG\Web.Config.

    On doit le mettre sous le noeud <configSections> directement sous <configuration>:

    <configuration>
    < configSections>
    <sectionGroup name= »system.web.extensions » …

    La raison est que le site ASP.Net MVC 3 .NET 4 hérite du Web.Config de SharePoint et ce sectionGroup cause une erreur. On ne peut pas utiliser l’élément <location> à cet endroit. En déplaçant la déclaration directement dans le Web.Config du .NET Framework 2.0, SharePoint en héritera mais pas notre application ASP.Net.

    Vous pouvez obtenir plus d’information ici. Un extrait:

    Move the configSections definition in the Web.config file of the parent application (the application that runs ASP.NET 2.0 or ASP.NET 3.5) into the root Web.config file for the.NET Framework 2.0. The IIS 7 and IIS 7.5 native configuration system scans the configSections element when it merges the hierarchy of configuration files. Moving the configSections definition from the parent Web application’s Web.config file to the root Web.config file effectively hides the element from the configuration merge process that occurs for the child ASP.NET 4 application.

  7. On hérite aussi des Modules du site SharePoint et ceux-ci causent une erreur (en particulier SharePoint14Module). La solution est d’utiliser ce noeud <modules> dans votre application ASP.Net:

    <modules runAllManagedModulesForAllRequests= »true »>
    < remove name= »SPRequestModule » />
    < remove name= »PublishingHttpModule » />
    < remove name= »RSRedirectModule » />
    < remove name= »StateServiceModule » />
    < remove name= »SharePoint14Module » />
    < add name= »Session » type= »System.Web.SessionState.SessionStateModule » />
    < /modules>

    Notez qu’en plus d’enlever les modules spécifiques à SharePoint, on réactive la gestion des Session. Si vous ne le faites pas, vous ne pourrez pas utiliser l’objet Session.

    En fait, comme on ne peut pas utiliser la clause <location> pour <system.webServer>, il faut défaire ce qui est fait par SharePoint. Il se peut que votre implantation nécessite d’activer / désactiver certains modules / “Http Handlers”. Voici un extrait de ce que SharePoint active / désactive:

    <modules runAllManagedModulesForAllRequests= »true »>
    < remove name= »AnonymousIdentification » />
    < remove name= »FileAuthorization » />
    < remove name= »Profile » />
    < remove name= »WebDAVModule » />
    < remove name= »Session » />
    < add name= »SPRequestModule » … />
    < add name= »ScriptModule »  … />
    < add name= »SharePoint14Module » … />
    < add name= »StateServiceModule » … />
    < add name= »RSRedirectModule » … />
    < add name= »PublishingHttpModule » … />
    < /modules>
    < handlers>
    < remove name= »OPTIONSVerbHandler » />
    < remove name= »WebServiceHandlerFactory-Integrated » />
    < remove name= »svc-Integrated » />
    < remove name= »WebDAV » />
    < add name= »svc-Integrated » … />
    < add name= »OwssvrHandler » … />
    < add name= »ScriptHandlerFactory » … />
    < add name= »ScriptHandlerFactoryAppServices » … />
    < add name= »ScriptResource » …  />
    < add name= »JSONHandlerFactory » … />
    < add name= »ReportViewerWebPart » … />
    < add name= »ReportViewerWebControl » … />
    < /handlers>

  8. Les permissions du site MVC sont maintenant héritées du site SharePoint ce qui provoque une erreur dans l’application MVC. Il faut donc ajouter les permissions nécessaires en ajoutant l’élément <trust> dans le web.config de l’application. Ex. :

    <system.web>
    <trust level="Full" originUrl="" />
    </system.web>
  9. IIS Reset
  10. Si vous avez une erreur qui dit que le fichier de configuration n’est pas accessible, donnez temporairement les droits en lecture au groupe local IIS_IUSRS sur ce fichier (l’erreur indique le chemin d’accès).

    Quand IIS n’arrive pas à déterminer l’authentification utilisée (ex. il y a un nœud dupliqué dans les web.config parent et enfant), il doit avoir accès en mode anonyme. Après l’erreur qui sera affichée sera détaillée (ex. le nœud qui ne peut être redéfini etc.)

  11. Si vous avez une erreur HTTP 500 ou une page blanche, même avec customErrors à OFF, vous pouvez utiliser la fonctionnalité des Failed Request Tracing (FREB). Voici comment l’activer pour un site / application.

    C’est ainsi que j’ai su qu’il me fallait désactiver le module SharePoint14Module:

    Request Diagnostics HTTP 500

Bref, oui c’est possible de mettre une application ASP.Net MVC 3 sous SharePoint 2010 mais il faut quand même effectuer un certain nombre de manipulations …

Si vous ne souhaitez pas donner FullTrust à l’application ASP.Net, vous devez:

  1. Ajouter:

    using System.Security;
    [assembly: AllowPartiallyTrustedCallers()]

    dans le fichier AssemblyInfo.cs des Dlls utilisés par votre application afin qu’ils acceptent d’être appelés dans un contexte limité ne permissions.

  2. Définir un fichier de policy listant toutes les permissions nécessaires à votre application (vous pouvez partir d’un fichier utilisé par SharePoint).
  3. Inclure le fichier spécifique en lui donnant un nom et y référer:

    <system.web>
      <securityPolicy>
        <trustLevel name= »MVCTrustLevel » policyFile= »<fullpath> » />
      </securityPolicy>
      <trust level= »MVCTrustLevel » originUrl= » » />
    </system.web>

Bonne chance et à la prochaine …