280 votes

Accès à la session à l'aide de l'API Web ASP.NET

Je sais que session et REST ne vont pas exactement de pair, mais n'est-il pas possible d'accéder à l'état de la session en utilisant la nouvelle API Web ? HttpContext.Current.Session est toujours nul.

5 votes

[SessionState(SessionStateBehavior.Required)] sur le ApiController fait l'affaire (ou .ReadOnly le cas échéant).

0 votes

@RomanStarkov Je n'ai pas réussi à le faire fonctionner. Quel environnement utilisiez-vous ? .NET Core ?

1 votes

@Bondolin non, ce n'était pas Core.

351voto

LachlanB Points 1703

MVC

Pour un projet MVC, effectuez les changements suivants (WebForms et Dot Net Core répondent plus bas) :

WebApiConfig.cs

public static class WebApiConfig
{
    public static string UrlPrefix         { get { return "api"; } }
    public static string UrlPrefixRelative { get { return "~/api"; } }

    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    ...

    protected void Application_PostAuthorizeRequest()
    {
        if (IsWebApiRequest())
        {
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
        }
    }

    private bool IsWebApiRequest()
    {
        return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
    }

}

Cette solution présente l'avantage supplémentaire de pouvoir récupérer l'URL de base en javascript pour effectuer les appels AJAX :

_Layout.cshtml

<body>
    @RenderBody()

    <script type="text/javascript">
        var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)';
    </script>

    @RenderSection("scripts", required: false) 

et ensuite dans nos fichiers/codes Javascript nous pouvons faire nos appels webapi qui peuvent accéder à la session :

$.getJSON(apiBaseUrl + '/MyApi')
   .done(function (data) {
       alert('session data received: ' + data.whatever);
   })
);

WebForms

Faites ce qui précède mais modifiez la fonction WebApiConfig.Register pour qu'elle prenne une RouteCollection à la place :

public static void Register(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

Et ensuite, appelez ce qui suit dans Application_Start :

WebApiConfig.Register(RouteTable.Routes);

Dot Net Core

Ajouter le Microsoft.AspNetCore.Session NuGet, puis apportez les modifications suivantes au code :

Startup.cs

Appelez le AddDistributedMemoryCache et AddSession sur l'objet services dans la fonction ConfigureServices :

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    ...

    services.AddDistributedMemoryCache();
    services.AddSession();

et dans la fonction Configure, ajoutez un appel à UseSession :

public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
ILoggerFactory loggerFactory)
{
    app.UseSession();
    app.UseMvc();

SessionController.cs

Dans votre contrôleur, ajoutez une déclaration d'utilisation en haut :

using Microsoft.AspNetCore.Http;

et ensuite utiliser l'objet HttpContext.Session dans votre code comme suit :

    [HttpGet("set/{data}")]
    public IActionResult setsession(string data)
    {
        HttpContext.Session.SetString("keyname", data);
        return Ok("session data set");
    }

    [HttpGet("get")]
    public IActionResult getsessiondata()
    {
        var sessionData = HttpContext.Session.GetString("keyname");
        return Ok(sessionData);
    }

vous devriez maintenant être en mesure de frapper :

http://localhost:1234/api/session/set/thisissomedata

et ensuite aller à cette URL le fera sortir :

http://localhost:1234/api/session/get

Pour en savoir plus sur l'accès aux données de session dans Dot Net Core, cliquez ici : https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state

Problèmes de performance

Lisez la réponse de Simon Weaver ci-dessous concernant les performances. Si vous accédez à des données de session dans un projet WebApi, cela peut avoir des conséquences très graves sur les performances. J'ai vu ASP.NET imposer un délai de 200 ms pour les requêtes simultanées. Cela peut s'accumuler et devenir désastreux si vous avez beaucoup de demandes simultanées.


Problèmes de sécurité

Assurez-vous que vous verrouillez les ressources par utilisateur - un utilisateur authentifié ne doit pas être en mesure de récupérer des données de votre WebApi auxquelles il n'a pas accès.

Lisez l'article de Microsoft sur l'authentification et l'autorisation dans ASP.NET Web API -. https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

Lisez l'article de Microsoft sur la façon d'éviter les attaques de type Cross-Site Request Forgery. (En bref, vérifiez la méthode AntiForgery.Validate) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks

0 votes

J'ai eu l'erreur dans le modèle RIS n'existe pas. Je suis un n00b extrême et je n'ai pas réalisé que vous devez remplacer cela par le nom de votre projet, si quelqu'un d'autre rencontre ce problème

0 votes

Désolé pour ça Jameo, j'ai mis à jour pour dire ProjectNameSpace donc ça devrait être un peu plus évident.

7 votes

Parfait. C'est simple et ça marche. Pour un système non-MVC, il suffit d'ajouter Application_PostAuthorizeRequest() à Global.ascx.cs.

66voto

Warrick Hunter Points 556

Vous pouvez accéder à l'état de la session en utilisant un RouteHandler personnalisé.

// In global.asax
public class MvcApp : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        var route = routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        route.RouteHandler = new MyHttpControllerRouteHandler();
    }
}

// Create two new classes
public class MyHttpControllerHandler
    : HttpControllerHandler, IRequiresSessionState
{
    public MyHttpControllerHandler(RouteData routeData) : base(routeData)
    { }
}
public class MyHttpControllerRouteHandler : HttpControllerRouteHandler
{
    protected override IHttpHandler GetHttpHandler(
        RequestContext requestContext)
    {
        return new MyHttpControllerHandler(requestContext.RouteData);
    }
}

// Now Session is visible in your Web API
public class ValuesController : ApiController
{
    public string Get(string input)
    {
        var session = HttpContext.Current.Session;
        if (session != null)
        {
            if (session["Time"] == null)
                session["Time"] = DateTime.Now;
            return "Session Time: " + session["Time"] + input;
        }
        return "Session is not availabe" + input;
    }
}

Trouvé ici : http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html

14 votes

Mise à jour : si vos fonctions API lisent la session et ne la modifient pas, il peut être judicieux d'utiliser IReadOnlySessionState au lieu de IRequiresSessionState. Cela garantit que la session n'est pas verrouillée pendant le traitement de la fonction API.

6 votes

Ne fonctionne pas pour moi dans MVC 4 - route.RouteHandler n'est même pas une propriété pour moi. @LachlanB semble avoir ce qui a fonctionné pour moi.

3 votes

Merci à @bkwdesign d'avoir indiqué la solution MVC. Cette réponse ne concerne que l'API Web.

22voto

Nickz Points 2761

Vous avez raison, REST est sans état. Si vous utilisez une session, le traitement deviendra avec état, les demandes suivantes pourront utiliser l'état (de la session).

Pour qu'une session puisse être réhydratée, vous devrez fournir une clé pour associer l'état. Dans une application asp.net normale, cette clé est fournie en utilisant un cookie (sessions avec cookie) ou un paramètre url (sessions sans cookie).

Si vous avez besoin d'une session pour oublier le repos, les sessions ne sont pas pertinentes dans les conceptions basées sur REST. Si vous avez besoin d'une session pour la validation, utilisez un jeton ou autorisez par adresse IP.

10 votes

Je ne suis pas sûr de ça. Dans les exemples de Microsoft, il est indiqué d'utiliser l'attribut Authorize. J'ai essayé et cela fonctionne avec l'authentification basée sur les formulaires. L'API Web est consciente de l'état d'authentification qui est transmis dans le cookie d'authentification par défaut.

4 votes

Voici l'échantillon auquel je fais référence, code.msdn.microsoft.com/ASPNET-Web-API-JavaScript-d0d64dd7 . Il utilise la nouvelle API Web basée sur REST et met en œuvre l'authentification par formulaire.

4 votes

J'ai utilisé l'attribut [Authorize] avec succès sans avoir besoin de l'état de la session. J'ai simplement écrit un gestionnaire de message d'authentification pour définir l'identité.

20voto

JSancho Points 797

Mark, si tu vérifies le exemple MVC nerddinner la logique est à peu près la même.

Il suffit de récupérer le cookie et de le placer dans la session en cours.

Global.asax.cs

public override void Init()
{
    this.AuthenticateRequest += new EventHandler(WebApiApplication_AuthenticateRequest);
    base.Init();
}

void WebApiApplication_AuthenticateRequest(object sender, EventArgs e)
{
    HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

    SampleIdentity id = new SampleIdentity(ticket);
    GenericPrincipal prin = new GenericPrincipal(id, null); 

    HttpContext.Current.User = prin;
}

enter code here

Vous devrez définir votre classe "SampleIdentity", que vous pouvez emprunter à la classe projet nerddinner .

0 votes

La classe d'identité est dans NerdDinner_2.0 \NerdDinner\Models\NerdIdentity.cs.

0 votes

Cela ne fonctionne pas pour moi (en .NET 4). Je n'ai jamais ce cookie. Cela ne fonctionne-t-il que si vous avez activé FormsAuthentication ?

0 votes

Le cookie est effectivement généré après l'authentification via le formulaire de connexion. Vous pouvez également personnaliser le moment et la manière dont il est créé. stackoverflow.com/questions/7217105 Mais vous avez toujours besoin que l'utilisateur s'authentifie effectivement auprès du serveur web.

10voto

Cruiser KID Points 375

Le dernier ne fonctionne pas maintenant, prenez celui-là, il a marché pour moi.

dans WebApiConfig.cs à App_Start

    public static string _WebApiExecutionPath = "api";

    public static void Register(HttpConfiguration config)
    {
        var basicRouteTemplate = string.Format("{0}/{1}", _WebApiExecutionPath, "{controller}");

        // Controller Only
        // To handle routes like `/api/VTRouting`
        config.Routes.MapHttpRoute(
            name: "ControllerOnly",
            routeTemplate: basicRouteTemplate//"{0}/{controller}"
        );

        // Controller with ID
        // To handle routes like `/api/VTRouting/1`
        config.Routes.MapHttpRoute(
            name: "ControllerAndId",
            routeTemplate: string.Format ("{0}/{1}", basicRouteTemplate, "{id}"),
            defaults: null,
            constraints: new { id = @"^\d+$" } // Only integers 
        );

Global.asax

protected void Application_PostAuthorizeRequest()
{
  if (IsWebApiRequest())
  {
    HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
  }
}

private static bool IsWebApiRequest()
{
  return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_WebApiExecutionPath);
}

le quatrième ici : http://forums.asp.net/t/1773026.aspx/1

0 votes

C'est la solution la plus simple, mais elle comporte quelques erreurs dans le code, de sorte qu'elle ne fonctionne pas réellement. J'ai posté une autre solution basée sur celle-ci, n'hésitez pas à modifier la vôtre pour qu'elle corresponde à la mienne.

0 votes

Une légère correction sur la ligne _WebApiExecutionPath doit se lire public static string _WebApiExecutionPath = "~/api" ;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X