Archive

Posts Tagged ‘MVC2’

[C# 4 MVC2 JQuery] Appeler une action depuis JQuery – Part 2

Je reviens un peu sur ce que j’avais noté dans Appeler une action depuis JQuery.

Après un audit de sécurité, il s’est avéré que JSon, tel qu’utilisé, pouvait entrainer une faille. Dans notre cas, elle est catégorisée sous « Piratage de Javascript », avec une classification WASC « Divulgation d’informations : Fuite d’informations« .

Du coup, je me suis penché sur le problème et j’ai trouvé Asp.Net MVC 2 JsonRequestBehavior.AllowGet.

J’ai donc modifié mon code en conséquence :

function GetProviders(input){
        $.ajax({
            type: "POST",
            url: "<%: Url.Content("~/Controller/GetEvents") %>",
            data: { date: input },
            dataType: "json",
            async: true,
            cache: false,
            success: function (msg) {
                $("#InputSelect").empty();
                $.each(msg, function (index, item) {
                    $("#InputSelect").get(0).options[$("#InputSelect").get(0).options.length] = new Option(item.Name, item.Code);
                });
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                // ...
            }
        });
    }
    $(document).ready(function () {
        $("#InputDate").live('blur', function () {
            var date = $("#InputDate").val();
            if (date != "") {
                GetProviders(date);
            }
        });
        $("#InputDate").blur();
    });

A noter ici l’utilisation de Url.Content.
Ca permet, ni plus ni moins, de s’affranchir des problèmes de résolutions des URL.
Je m’explique : en local, je travaille avec des URL du type : http://localhost:8080/Controller/Action.
Cependant, sur le serveur de production, les URL sont du type : http://domain/projet/Controller/Action.
On a un niveau supplémentaire et là, c’est vite le drame ! 🙂
Du coup, Url.Content permet de résoudre automatiquement les URL à partir de l’élément racine : « ~ » veut donc dire http://localhost:8080 ou http://domain/projet, suivant l’environnement.

Et là, on peu enlever le JsonRequestBehavior.AllowGet :

        [HttpPost]
        public JsonResult GetPrestataires(string date)
        {
            List<Provider> list = new List<Provider>();
            // Traitements
            return Json(list);
        }

Je mettrais à jour en fonction du résultat du nouvel audit.
Et pis je ferais aussi un retour un peu plus général sur la sécurité en présentant différents sites utiles, dans un autre billet.

EDIT du 11 / 02

Après un audit différentiel, il apparait que la faille est corrigée !
Donc, la méthode $.ajax est à utiliser en lieu et place de $.getJSON.
Pour la petite histoire, l’utilitaire avait détecté 19 failles, l’audit différentiel a indiqué quelles avaient toutes été corrigées \o/

Catégories :.Net, ASP.Net, C#, JQuery, MVC2

[C# 4 MVC2 JQuery] Appeler une action depuis JQuery

La situation : j’ai un champ qui prend une date, selon cette date, je dois alimenter une combobox avec toutes les valeurs valides pour cette date.

Par exemple, je choisis le 24/12/2010 et je dois afficher tous les évènements de cette date dans ma combo.

Donc, coté HTML :

<tr>
    <td><%: Resources.GlobalResource.Date %></td>
    <td><%: Html.TextBox("InputDate", Model.InputDate) %></td>
</tr>
<tr>
    <td><%:Resources.GlobalResource.Events%></td>
    <td><select id="InputSelect" name="InputSelect"></select></td>
</tr>

J’ai utilisé le DatePicker qui est trouvable ici (http://www.eyecon.ro/datepicker/), même si je l’ai un poil modifié pour répondre aux besoins.

Il est pas mal, même si je l’ai trouvé un peu compliqué à skiner (mais mon goût pour faire de la CSS étant…discutable, je ne suis pas forcément partial sur ce point).

$(document).ready(function () {
    $("#InputDate").live('blur', function () {// binding sur l’action de blur (perte du focus)
        var date = $("#InputDate").val();
        if (date != "") {
            $.getJSON("/Controller/GetEvents", // action en question
                    { date: $("#InputDate").val() },// paramètre(s) passé(s) à l’action
                    function (data) {
                        $("#InputSelect").empty();
                        $.each(data, function (index, item) {//pour chaque item
$("#InputSelect").get(0).options[$("InputSelect").get(0).options.length] = new Option(item.Name, item.Code); //on ajoute un item à la combo
                        });
                    });
        }
    });
    $("#InputDate").blur();
});

Là, je renvois une liste d’entités contenant les deux propriétés Name et Code.

Il faut réellement bien faire attention aux fautes de frappe qui peuvent faire perdre un temps fou…

Coté controller, l’action prend en paramètre une string qui sera convertie en date et qui renvoie un JsonResult contenant une liste d’Events, objet qui possède deux propriétés : Name et Code.

public JsonResult GetEvents(string date)
{
   List<Events> list = new List<Events>();
   // Traitements
   return Json(list, JsonRequestBehavior.AllowGet);
}

Très important, en MVC 2, il FAUT ajouter le paramètres JsonRequestBehavior.AllowGet.

Sinon, ça ne marche tout simplement pas…

Catégories :.Net, ASP.Net, JQuery, MVC2

[C# 4 MVC2] Global.asax et gestion des 404

Dans le billet précédent, il y avait la gestion de l’action non reconnue.

Mais qu’en est-il du controller non reconnu ?

Si, comme moi, vous utilisez la route par défaut, il est possible de tomber sur des 404 en spécifiant une URL qui n’existe pas : http://localshot/Unknow.

Là, malheureusement, je n’ai pas trouvé de moyen de gérer cela dans mon BaseController.

Du coup, c’est du coté du global.asax que je me suis tourné.

Comme toute erreur au niveau du global.asax, c’est dans Application_Error que je vais mettre mon code.

/// <summary>
/// Méthode permettant de gérer les exceptions non gérées jusque là
/// </summary>
/// <param name="sender">Objet ayant provoqué l'exception</param>
/// <param name="e">Paramètres</param>
protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();

    // Par défaut, Firefox cherche le /favicon.ico, et génère une 404 en son absence
    if (exception.Message != null && exception.Message.Contains("/favicon.ico"))
        return;
    // Publication de l'exception
    Response.Clear();
    Server.ClearError();

    // Gestion des HttpException
    if (exception.GetType() == typeof(HttpException))
    {
        HttpException httpException = exception as HttpException;
        if (httpException != null)
        {
            // Gestion des 404
            if (httpException.GetHttpCode() == 404)
            {
                IController icontroller = new Controllers.HomeController();
                RouteData routeData = new RouteData();
                routeData.Values.Add("controller", "Home");
                routeData.Values.Add("action", "Error404");
                icontroller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
            }
        }
    }
}

Ici, comme on est pas dans le BaseController, on doit se créer un propre controller afin de gérer la redirection de l’utilisateur vers la page d’erreur qui va bien. Comme c’est dans le HomeController que j’ai les actions qui vont bien, c’est une instance de HomeController que je créé.

Catégories :.Net, ASP.Net, MVC2

[C#4 MVC2] Les méthodes d’action du controller

Dans l’article précédent, j’ai parlé de la méthode OnActionExecuting(ActionExecutingContext).

Ceci dit, il y a d’autres méthodes qui peuvent être utiles (elles seront également présentes dans la BaseController).

Déjà, qu’en est-il de leur ordre d’exécution ?

Le voici (du moins pour celles qui me paraissaient pertinentes) :

Méthode d’action Description MSDN
void Execute(System.Web.Routing.RequestContext) Exécute le contexte de la requête spécifiée.
void Initialize(System.Web.Routing.RequestContext) Initialise des données qui peuvent ne pas être disponibles lorsque le constructeur est appelé.
void ExecuteCore() Exécute la requête.
ITempDataProvider CreateTempDataProvider() Crée un fournisseur de données temporaire.
IActionInvoker CreateActionInvoker() Crée un demandeur d’action.
void OnAuthorization(AuthorizationContext) Méthode appelée lors de l’autorisation.
void OnActionExecuting(ActionExecutingContext) Méthode appelée avant la méthode d’action.
void OnActionExecuted(ActionExecutedContext) Méthode appelée après la méthode d’action.
void OnResultExecuting(ResultExecutingContext) Méthode appelée avant l’exécution du résultat d’action qui est retourné par une méthode d’action.
void OnResultExecuted(ResultExecutedContext) Méthode appelée après l’exécution du résultat d’action qui est retourné par une méthode d’action.
void Dispose(bool) Libère toutes les ressources qui sont utilisées par l’instance actuelle de la classe Controller.

A noter pour la méthode Execute, la MSDN dit :
« Cette méthode est une implémentation de Execute. Vous ne devez pas en général substituer cette méthode. Substituez Initialize ou ExecuteCore à la place. »

Et voici deux autres méthodes qui sont intéressantes :

Méthode d’action Description
protected virtual void OnException(ExceptionContext) Méthode appelée lorsqu’une exception non gérée se produit au cours de l’action.
protected virtual void HandleUnknownAction(string) Méthode appelée lorsqu’une requête correspond à ce contrôleur, mais qu’aucune méthode portant le nom d’action spécifié n’est trouvée dans le contrôleur.

J’ai choisis, dans l’article précédent, de vérifier que l’utilisateur n’est pas anonyme dans la méthode OnActionExecuting, mais la méthode OnAuthorization est bien placée pour cela.

Dans la méthode OnException, on va naturellement gérer les exceptions qui peuvent arriver.

Une des questions que je me suis poser : comment alimenter un model spécifique et rediriger vers une page qui va l’exploiter ?

C’est assez simple :

/// <summary>
/// Evènement appelé lorsqu'une exception non gérée est levée.
/// </summary>
/// <param name="filterContext">Le context de l'action.</param>
protected override void OnException(ExceptionContext filterContext)
{
    try
    {
        // Publication de l'exception
    }
    catch { }
    // On redirige vers la page d'erreur
    HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception,
        filterContext.Controller.ToString(),
        (string)filterContext.RouteData.Values["action"]);
    ViewResult result = new ViewResult() {
        ViewName = "Error",
        TempData = filterContext.Controller.TempData,
        ViewData = new ViewDataDictionary<HandleErrorInfo>(model)
    };
    filterContext.Result = result;
    filterContext.ExceptionHandled = true;
    filterContext.HttpContext.Response.Clear();
    filterContext.HttpContext.Response.StatusCode = 500;
    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}

Dans le cas où la route par défaut est utilisée, il y a toujours moyen de modifier l’url pour faire pointer vers des actions qui ne sont pas prises en charge.

Dans ce cas là, on va devoir utiliser la méthode HandleUnknwonAction (elle porte bien son nom, n’est ce pas ?).

Dans le cas d’une action non connue, par exemple http://localhost/Home/Unknow, une HttpException est levée dans la méthode HandleUnknwonAction.

Du coup, il suffit simplement d’avoir un code du type :

/// <summary>
/// Evènement appelé lorsqu'une action non reconnue est appelée (ex : /Home/UnknowAction).
/// </summary>
/// <param name="actionName">Nom de l'action.</param>
protected override void HandleUnknownAction(string actionName)
{
    try
    {
        base.HandleUnknownAction(actionName);
    }
    catch (HttpException ex)
    {
        HandleErrorInfo model = new HandleErrorInfo(ex,
            this.ToString(),
            actionName);
        // Publication de l'exception
        // […]
        this.View("Error", model).ExecuteResult(this.ControllerContext);
    }
}
Catégories :.Net, ASP.Net, C#, MVC2

[C#4 MVC2] Timeout de session

Il y a plusieurs moyens de gérer le timeout de session, du moins j’en ai trouvé plusieurs.

Ceci dit, j’ai préféré la solution simple mais efficace qui consiste à utiliser un controller « parent » dont tous les autres controllers vont hériter.

Donc, j’ai ma classe BaseController, une classe abstract dont héritent tous mes controllers.

Mais j’y mets quoi dedans ???

J’utilise la méthode OnActionExecuting(ActionExecutingContext).

La description de la MSDN est la suivante : « Méthode appelée avant la méthode d’action.»

Donc, voici la méthode que j’ai utilisée :

/// <summary>
/// Evènement appelé avant l'invocation de l'action.
/// </summary>
/// <param name="filterContext">Informations sur la requête et l'action en cours.</param>
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    // La session existe
    if (filterContext.HttpContext.Session != null)
    {
        // L'utilisateur n'est pas connecté
        if (ContextHelper.GetSessionValue<UserModel>(ContextHelper.SessionKeys.SessionCurrentUser) == null)
        {
            // Redirection vers la page par défaut
            filterContext.Result = RedirectToAction("Index", "Home");
            return;
        }

        // C'est une nouvelle session
        if (filterContext.HttpContext.Session.IsNewSession)
        {
            string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
            // Le cookie existe et que le numéro de session est plus grand que 0
            if ((cookie != null) &&
                (cookie.IndexOf("ASP.NET_SessionId") >= 0))
            {
                // Redirection vers la page d'expiration de session
                filterContext.Result = RedirectToAction("SessionExpired", "Home");
                return;
            }
        }
    }
    // Sinon, on continue
    base.OnActionExecuting(filterContext);
}

J’ai juste ajouter le contrôle sur le user en session qui ne doit pas être null, mais le cas du site sur lequel ça a été utilisé est un peu particulier. En effet, le site est protégé par SSO et c’est lui qui gère l’authentification. Du coup, en pratique, si l’utilisateur est null, je n’ai pas une redirection, mais une récupération des entête HTTP pour loguer l’utilisateur (qui doit ainsi se loguer en arrivant sur la page qu’il voulait, quelle qu’elle soit).

Catégories :.Net, ASP.Net, C#, MVC2