Zugriff auf die Sitzung über die ASP.NET-Web-API

268

Ich stelle fest, dass Sitzung und REST nicht genau Hand in Hand gehen, aber ist es nicht möglich, über die neue Web-API auf den Sitzungsstatus zuzugreifen? HttpContext.Current.Sessionist immer null.

Kennzeichen
quelle
4
[SessionState(SessionStateBehavior.Required)]auf dem ApiControllermacht den Trick (oder .ReadOnlywo angebracht).
Roman Starkov
@ RomanStarkov Konnte das nicht zum Laufen bringen. Welche Umgebung haben Sie verwendet? .NET Core?
Bondolin
@ Bondolin nein, das war nicht Core.
Roman Starkov
@ RomanStarkov MVC dann? Ich habe Probleme, es zu finden.
Bondolin
@Bondolin SessionStateAttribute und ja, MVC.
Roman Starkov

Antworten:

336

MVC

Nehmen Sie für ein MVC-Projekt die folgenden Änderungen vor (Antwort von WebForms und Dot Net Core weiter unten):

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);
    }

}

Diese Lösung hat den zusätzlichen Vorteil, dass wir die Basis-URL in Javascript für die AJAX-Aufrufe abrufen können:

_Layout.cshtml

<body>
    @RenderBody()

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

    @RenderSection("scripts", required: false) 

und dann können wir in unseren Javascript-Dateien / Code unsere Webapi-Aufrufe tätigen, die auf die Sitzung zugreifen können:

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

WebForms

Führen Sie die obigen Schritte aus, ändern Sie jedoch die Funktion WebApiConfig.Register, um stattdessen eine RouteCollection zu verwenden:

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

Rufen Sie dann in Application_Start Folgendes auf:

WebApiConfig.Register(RouteTable.Routes);

Dot Net Core

Fügen Sie das Microsoft.AspNetCore.Session NuGet-Paket hinzu und nehmen Sie die folgenden Codeänderungen vor:

Startup.cs

Rufen Sie die Methoden AddDistributedMemoryCache und AddSession für das Services-Objekt in der Funktion ConfigureServices auf:

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

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

und fügen Sie in der Funktion Konfigurieren einen Aufruf von UseSession hinzu :

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

SessionController.cs

Fügen Sie in Ihrem Controller oben eine using-Anweisung hinzu:

using Microsoft.AspNetCore.Http;

und verwenden Sie dann das HttpContext.Session-Objekt in Ihrem Code wie folgt:

    [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);
    }

Sie sollten jetzt in der Lage sein zu schlagen:

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

Wenn Sie dann zu dieser URL gehen, wird Folgendes angezeigt:

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

Weitere Informationen zum Zugriff auf Sitzungsdaten innerhalb des dot net core finden Sie hier: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state

Leistungsprobleme

Lesen Sie unten die Antwort von Simon Weaver zur Leistung. Wenn Sie in einem WebApi-Projekt auf Sitzungsdaten zugreifen, kann dies schwerwiegende Auswirkungen auf die Leistung haben. Ich habe gesehen, dass ASP.NET eine Verzögerung von 200 ms für gleichzeitige Anforderungen erzwingt. Dies kann sich summieren und katastrophal werden, wenn Sie viele gleichzeitige Anfragen haben.


Sicherheitsbedenken

Stellen Sie sicher, dass Sie Ressourcen pro Benutzer sperren. Ein authentifizierter Benutzer sollte keine Daten von Ihrem WebApi abrufen können, auf die er keinen Zugriff hat.

Lesen Sie den Artikel von Microsoft zur Authentifizierung und Autorisierung in der ASP.NET-Web-API - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

Lesen Sie den Artikel von Microsoft zur Vermeidung von Hackangriffen auf Cross-Site Request Forgery. (Kurz gesagt, sehen Sie sich die AntiForgery.Validate-Methode an.) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks

Rocklan
quelle
7
Perfekt. Einfach und es funktioniert. Fügen Sie für Nicht-MVC einfach Application_PostAuthorizeRequest () zu Global.ascx.cs hinzu.
Mhenry1384
1
Vielen Dank an @JCallico, ich denke, dass die meisten Leute zuerst die ASP.NET-Seite aufrufen, die die Sitzung erstellt.
Rocklan
3
Ich musste IsWebApiRequest () ändern, um auch true zurückzugeben, wenn der Pfad mit WebApiConfig.UrlPrefix sowie WebApiConfig.UrlPrefixRelative beginnt. Ansonsten funktioniert es wie erwartet.
gb2d
7
Eine Sache zu diesem Fix zu erwähnen. Wenn Sie das SessionStateBehavior auf Erforderlich setzen, kommt es zu einem Engpass im Webapi, da alle Ihre Anforderungen aufgrund von Sperren für das Sitzungsobjekt synchronisiert ausgeführt werden. Sie können es stattdessen als SessionStateBehavior.Readonly ausführen. Auf diese Weise werden keine Sperren für das Sitzungsobjekt erstellt.
Michael Kire Hansen
2
Seien Sie vorsichtig, wenn Sie das Sitzungsstatusverhalten auf "Erforderlich" setzen. Anforderungen mit Schreibberechtigungen sperren die Sitzung und verhindern, dass mehrere HttpApplications pro Client erzeugt werden. Sie sollten den Sitzungsstatus für jede Route auf ein geeignetes Niveau einstellen. Bitte beziehen Sie sich auf meine Antwort hier: stackoverflow.com/a/34727708/1412787
Axel Wilczek
66

Sie können mit einem benutzerdefinierten RouteHandler auf den Sitzungsstatus zugreifen.

// 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;
    }
}

Hier zu finden: http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html

Warrickh
quelle
14
Update: Wenn Ihre API-Funktionen aus der Sitzung lesen und die Sitzung nicht ändern, ist es möglicherweise eine gute Idee, IReadOnlySessionState anstelle von IRequiresSessionState zu verwenden. Dadurch wird sichergestellt, dass die Sitzung während der Verarbeitung der API-Funktion nicht gesperrt wird.
Warrickh
6
Ich arbeite nicht für mich in MVC 4 - route.RouteHandler ist nicht einmal eine Eigenschaft für mich. @LachlanB scheint das zu haben, was für mich funktioniert hat.
bkwdesign
3
Vielen Dank an @bkwdesign für den Hinweis auf die MVC-Lösung. Diese Antwort bezieht sich nur auf die Web-API.
Warrickh
2
Dies scheint Routenattribute nicht zu unterstützen. Gedanken?
Tim S
Wie bkwdesign betonte, wird dies nicht mehr unterstützt. Es gibt jedoch eine Möglichkeit, das Sitzungsstatusverhalten pro Route mithilfe von DataTokens zu definieren: stackoverflow.com/a/34727708/1412787
Axel Wilczek
46

Warum sollte die Verwendung von Session in WebAPI vermieden werden?

Leistung, Leistung, Leistung!

Es gibt einen sehr guten und oft übersehenen Grund, warum Sie Session in WebAPI überhaupt nicht verwenden sollten.

ASP.NET funktioniert bei Verwendung der Sitzung, indem alle von einem einzelnen Client empfangenen Anforderungen serialisiert werden . Jetzt spreche ich nicht über die Objekt-Serialisierung, sondern über das Ausführen in der empfangenen Reihenfolge und das Warten auf den Abschluss, bevor die nächste ausgeführt wird. Dies dient dazu, unangenehme Thread- / Race-Bedingungen zu vermeiden, wenn jeweils zwei Anforderungen versuchen, gleichzeitig auf die Sitzung zuzugreifen.

Gleichzeitige Anforderungen und Sitzungsstatus

Der Zugriff auf den ASP.NET-Sitzungsstatus ist pro Sitzung exklusiv. Wenn also zwei verschiedene Benutzer gleichzeitig Anforderungen stellen, wird der Zugriff auf jede einzelne Sitzung gleichzeitig gewährt. Allerdings , wenn zwei gleichzeitige Anforderungen für die gleiche Sitzung gemacht werden (durch den gleichen SessionID - Wert verwendet wird ), die erste Anforderung erhält exklusiven Zugriff auf die Sitzungsinformationen. Die zweite Anforderung wird erst ausgeführt, nachdem die erste Anforderung abgeschlossen wurde.(Die zweite Sitzung kann auch Zugriff erhalten, wenn die exklusive Sperre für die Informationen aufgehoben wird, da die erste Anforderung das Zeitlimit für die Sperre überschreitet.) Wenn der Wert EnableSessionState in der @ Page-Direktive auf ReadOnly festgelegt ist, wird eine Anforderung für das schreibgeschützte Element festgelegt Sitzungsinformationen führen nicht zu einer exklusiven Sperre der Sitzungsdaten. Schreibgeschützte Anforderungen für Sitzungsdaten müssen jedoch möglicherweise noch auf eine Sperre warten, die durch eine Lese- / Schreibanforderung zum Löschen von Sitzungsdaten festgelegt wurde.

Was bedeutet das für die Web-API? Wenn in einer Anwendung viele AJAX-Anforderungen ausgeführt werden, kann jeweils nur EINE ausgeführt werden. Wenn Sie eine langsamere Anforderung haben, werden alle anderen von diesem Client blockiert, bis sie abgeschlossen ist. In einigen Anwendungen kann dies zu einer merklich schleppenden Leistung führen.

Daher sollten Sie wahrscheinlich einen MVC-Controller verwenden, wenn Sie unbedingt etwas aus der Benutzersitzung benötigen, und den unnötigen Leistungsverlust vermeiden, wenn Sie ihn für WebApi aktivieren.

Sie können dies ganz einfach selbst testen, indem Sie einfach Thread.Sleep(5000)eine WebAPI-Methode eingeben und die Sitzung aktivieren. Führen Sie 5 Anfragen aus, und es dauert insgesamt 25 Sekunden, bis sie abgeschlossen sind. Ohne Sitzung dauern sie insgesamt etwas mehr als 5 Sekunden.

(Dieselbe Argumentation gilt für SignalR).

Simon_Weaver
quelle
18
Sie können dies umgehen, indem Sie [SessionState (SessionStateBehavior.ReadOnly)] verwenden, wenn Ihre Methode nur aus der Sitzung liest.
Rocklan
21

Du hast recht, REST ist staatenlos. Wenn Sie eine Sitzung verwenden, wird die Verarbeitung statusbehaftet, nachfolgende Anforderungen können den Status (aus einer Sitzung) verwenden.

Damit eine Sitzung rehydriert werden kann, müssen Sie einen Schlüssel angeben, um den Status zuzuordnen. In einer normalen asp.net-Anwendung wird dieser Schlüssel mithilfe eines Cookies (Cookie-Sessions) oder eines URL-Parameters (Cookieless-Sessions) bereitgestellt.

Wenn Sie eine Sitzung benötigen, vergessen Sie die Ruhezeiten. Sitzungen sind in REST-basierten Designs irrelevant. Wenn Sie eine Sitzung zur Validierung benötigen, verwenden Sie ein Token oder autorisieren Sie nach IP-Adressen.

Nickz
quelle
10
Ich bin mir darüber nicht sicher. In den Beispielen von Microsoft wird die Verwendung des Attributs Authorize angezeigt. Ich habe das versucht und es funktioniert mit formularbasierter Authentifizierung. Die Web-API kennt den Authentifizierungsstatus, der im Standardauthentifizierungscookie übergeben wird.
Mark
4
Hier ist das Beispiel, auf das ich mich beziehe: code.msdn.microsoft.com/ASPNET-Web-API-JavaScript-d0d64dd7 . Es verwendet die neue REST-basierte Web-API, die die Formularauthentifizierung implementiert.
Mark
4
Ich habe das Attribut [Authorize] erfolgreich verwendet, ohne den Sitzungsstatus zu benötigen. Ich habe gerade einen Authentifizierungsnachrichten-Handler geschrieben, um die Identität festzulegen.
Antony Scott
57
Sie wurden markiert, weil Sie keine Antwort auf sein Problem angeboten haben. Außerdem ist die Web-API ein asynchrones Framework, das mit einer Ajax-schweren Web-App hervorragend funktioniert. Niemand sagte, Sie müssten alle Tenente des RESTful-Designs respektieren, um von der Verwendung des Web-API-Frameworks zu profitieren.
Brian Ogden
3
@ MarkS. Es ist richtig zu informieren, dass die Web-API den Sitzungsstatus nicht kennen soll. Negative Antwort bleibt immer noch eine Antwort. Up Abstimmung.
Antoine Meltzheim
20

Mark, wenn Sie das nerddinner MVC-Beispiel überprüfen, ist die Logik ziemlich gleich.

Sie müssen nur das Cookie abrufen und in der aktuellen Sitzung festlegen.

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

Sie müssen Ihre "SampleIdentity" -Klasse definieren, die Sie aus dem nerddinner-Projekt ausleihen können .

JSancho
quelle
Die Identitätsklasse befindet sich in NerdDinner_2.0 \ NerdDinner \ Models \ NerdIdentity.cs.
Mhenry1384
Dies funktioniert bei mir nicht (in .NET 4). Ich habe diesen Keks nie. Funktioniert es nur, wenn Sie FormsAuthentication aktiviert haben?
Mhenry1384
Das Cookie wird tatsächlich generiert, nachdem Sie sich über das Anmeldeformular authentifiziert haben. Sie können auch anpassen, wie / wann es erstellt wird, siehe stackoverflow.com/questions/7217105. Der Benutzer muss sich jedoch weiterhin effektiv beim Webserver authentifizieren
JSancho,
Die Frage fragt nach HttpContext.Current.Session und diese Antwort erklärt nicht klar, was zu tun ist. Siehe @LachlanB Antwort.
JCallico
14

So beheben Sie das Problem:

protected void Application_PostAuthorizeRequest()
{
    System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}

in Global.asax.cs

Suresh Muttagi
quelle
4
Warnung! Dadurch wird die Sitzung für ALLE Anforderungen aktiviert. Dies kann die Leistung erheblich beeinträchtigen, wenn Ihre Anwendung eingebettete Ressourcen verwendet.
Cgatian
@cgatian irgendeine alternative Lösung behoben ?
Kiquenet
Ich denke, der beste Ansatz ist das, was @Treyphor vorschlägt. Aktivieren Sie es nicht für alle Anfragen. Nur Routen mit "/ api" oder etwas in der URL. Wenn möglich, setzen Sie den Sitzungsstatus so, dass er nur für Ihre API-Controller schreibgeschützt ist.
Cgatian
10

Letzteres funktioniert jetzt nicht, nimm dieses, es hat bei mir funktioniert.

in WebApiConfig.cs bei 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);
}

Viertens hier: http://forums.asp.net/t/1773026.aspx/1

Kreuzer KID
quelle
Dies ist die einfachste Lösung, weist jedoch einige Fehler im Code auf, sodass er nicht funktioniert. Ich habe eine andere Lösung veröffentlicht, die auf dieser basiert. Sie können Ihre Lösung so bearbeiten, dass sie meiner entspricht.
Rocklan
Eine leichte Korrektur in der Zeile _WebApiExecutionPath muss die öffentliche statische Zeichenfolge lesen. _WebApiExecutionPath = "~ / api";
Stephen Ebichondo
8

Wenn sich Ihr ApiController nach der Antwort von LachlanB nicht in einem bestimmten Verzeichnis befindet (wie / api), können Sie die Anforderung stattdessen mit RouteTable.Routes.GetRouteData testen, zum Beispiel:

protected void Application_PostAuthorizeRequest()
    {
        // WebApi SessionState
        var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));
        if (routeData != null && routeData.RouteHandler is HttpControllerRouteHandler)
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
    }
Stumblor
quelle
8

Ich hatte das gleiche Problem in asp.net mvc. Ich habe es behoben, indem ich diese Methode in meinen Basis-API-Controller eingefügt habe, von dem alle meine API-Controller erben:

    /// <summary>
    /// Get the session from HttpContext.Current, if that is null try to get it from the Request properties.
    /// </summary>
    /// <returns></returns>
    protected HttpContextWrapper GetHttpContextWrapper()
    {
      HttpContextWrapper httpContextWrapper = null;
      if (HttpContext.Current != null)
      {
        httpContextWrapper = new HttpContextWrapper(HttpContext.Current);
      }
      else if (Request.Properties.ContainsKey("MS_HttpContext"))
      {
        httpContextWrapper = (HttpContextWrapper)Request.Properties["MS_HttpContext"];
      }
      return httpContextWrapper;
    }

Dann in Ihrem API-Aufruf, dass Sie auf die Sitzung zugreifen möchten, die Sie gerade tun:

HttpContextWrapper httpContextWrapper = GetHttpContextWrapper();
var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];

Ich habe dies auch in meiner Global.asax.cs-Datei, wie andere Leute es gepostet haben. Ich bin mir nicht sicher, ob Sie es mit der oben beschriebenen Methode noch benötigen, aber hier ist es nur für den Fall:

/// <summary>
/// The following method makes Session available.
/// </summary>
protected void Application_PostAuthorizeRequest()
{
  if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/api"))
  {
    HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
  }
}

Sie können auch einfach ein benutzerdefiniertes Filterattribut erstellen, mit dem Sie Ihre API-Aufrufe festhalten können, die Sie benötigen. Anschließend können Sie die Sitzung in Ihrem API-Aufruf verwenden, wie Sie es normalerweise über HttpContext.Current.Session ["SomeValue"] tun würden:

  /// <summary>
  /// Filter that gets session context from request if HttpContext.Current is null.
  /// </summary>
  public class RequireSessionAttribute : ActionFilterAttribute
  {
    /// <summary>
    /// Runs before action
    /// </summary>
    /// <param name="actionContext"></param>
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
      if (HttpContext.Current == null)
      {
        if (actionContext.Request.Properties.ContainsKey("MS_HttpContext"))
        {
          HttpContext.Current = ((HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"]).ApplicationInstance.Context;
        }
      }
    }
  }

Hoffe das hilft.

Treyphor
quelle
6

Ich folgte dem @ LachlanB-Ansatz und tatsächlich war die Sitzung verfügbar, als das Sitzungscookie auf der Anfrage vorhanden war. Der fehlende Teil ist, wie das Sitzungscookie zum ersten Mal an den Client gesendet wird.

Ich habe ein HttpModule erstellt, das nicht nur die Verfügbarkeit von HttpSessionState aktiviert, sondern auch das Cookie an den Client sendet, wenn eine neue Sitzung erstellt wird.

public class WebApiSessionModule : IHttpModule
{
    private static readonly string SessionStateCookieName = "ASP.NET_SessionId";

    public void Init(HttpApplication context)
    {
        context.PostAuthorizeRequest += this.OnPostAuthorizeRequest;
        context.PostRequestHandlerExecute += this.PostRequestHandlerExecute;
    }

    public void Dispose()
    {
    }

    protected virtual void OnPostAuthorizeRequest(object sender, EventArgs e)
    {
        HttpContext context = HttpContext.Current;

        if (this.IsWebApiRequest(context))
        {
            context.SetSessionStateBehavior(SessionStateBehavior.Required);
        }
    }

    protected virtual void PostRequestHandlerExecute(object sender, EventArgs e)
    {
        HttpContext context = HttpContext.Current;

        if (this.IsWebApiRequest(context))
        {
            this.AddSessionCookieToResponseIfNeeded(context);
        }
    }

    protected virtual void AddSessionCookieToResponseIfNeeded(HttpContext context)
    {
        HttpSessionState session = context.Session;

        if (session == null)
        {
            // session not available
            return;
        }

        if (!session.IsNewSession)
        {
            // it's safe to assume that the cookie was
            // received as part of the request so there is
            // no need to set it
            return;
        }

        string cookieName = GetSessionCookieName();
        HttpCookie cookie = context.Response.Cookies[cookieName];
        if (cookie == null || cookie.Value != session.SessionID)
        {
            context.Response.Cookies.Remove(cookieName);
            context.Response.Cookies.Add(new HttpCookie(cookieName, session.SessionID));
        }
    }

    protected virtual string GetSessionCookieName()
    {
        var sessionStateSection = (SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");

        return sessionStateSection != null && !string.IsNullOrWhiteSpace(sessionStateSection.CookieName) ? sessionStateSection.CookieName : SessionStateCookieName;
    }

    protected virtual bool IsWebApiRequest(HttpContext context)
    {
        string requestPath = context.Request.AppRelativeCurrentExecutionFilePath;

        if (requestPath == null)
        {
            return false;
        }

        return requestPath.StartsWith(WebApiConfig.UrlPrefixRelative, StringComparison.InvariantCultureIgnoreCase);
    }
}
JCallico
quelle
Das funktioniert super. Dadurch bleibt die Sitzung zwischen den Anforderungen gleich, solange keine Zeitüberschreitung auftritt. Ich bin mir noch nicht sicher, ob ich es in prod verwenden werde, bis ich einen guten Weg gefunden habe, den Sitzungsstatus zwischen erforderlich und schreibgeschützt umzuschalten, um das Blockieren von Anforderungen zu stoppen, aber dies hat mir den gewünschten Startpfad gegeben. Danke dir!
Derreck Dean
3

Eines muss in der Antwort von @LachlanB erwähnt werden.

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

Wenn Sie die Zeile weglassen if (IsWebApiRequest())

Auf der gesamten Website tritt ein Problem mit der Langsamkeit beim Laden von Seiten auf, wenn Ihre Website mit Webformularseiten gemischt wird.

Maxisam
quelle
0

Ja, die Sitzung geht nicht mit der Rest-API einher, und wir sollten diese Vorgehensweise auch vermeiden. Aber gemäß den Anforderungen müssen wir die Sitzung irgendwie so aufrechterhalten, dass der Client-Server bei jeder Anfrage den Status oder die Daten austauschen oder verwalten kann. Der beste Weg, dies zu erreichen, ohne die REST-Protokolle zu beschädigen, ist die Kommunikation über ein Token wie JWT.

https://jwt.io/

Sulabh Singla
quelle
-4

Zurück zu den Grundlagen, warum nicht einfach halten und den Sitzungswert in einem versteckten HTML-Wert speichern, um ihn an Ihre API zu übergeben?

Regler

public ActionResult Index()
        {

            Session["Blah"] = 609;

            YourObject yourObject = new YourObject();
            yourObject.SessionValue = int.Parse(Session["Blah"].ToString());

            return View(yourObject);
        }

cshtml

@model YourObject

@{
    var sessionValue = Model.SessionValue;
}

<input type="hidden" value="@sessionValue" id="hBlah" />

Javascript

$ (Dokument) .ready (Funktion () {

    var sessionValue = $('#hBlah').val();

    alert(sessionValue);

    /* Now call your API with the session variable */}

}}

Andy A.
quelle
1
Was ist, wenn die Anwendung sowohl MVC als auch WebAPI verwendet? Es ist auch sinnvoller, einige Dinge auf der Serverseite zu speichern, z. B. Sharepoint-Sicherheitstoken. Anstatt einen speziellen Wrapper für die Token-Speicherung wie einen Azure-Blobs-Container zu implementieren, ist es manchmal sinnvoll, die Sitzung für diesen Datentyp wiederzuverwenden. Der in der App-Vorlage implementierte Sharepoint-Sicherheitskontext verwendet die Sitzung zum Speichern dieser Sicherheitskontexte und es werden nur kleine Datenmengen (Sitzungs-Tag) anstelle weniger Kilobyte Daten übertragen. Es wäre fantastisch, wenn dieser Kontext kleiner wäre ...
Konstantin Isaev