Je me rends compte que session et REST ne vont pas exactement de pair, mais est-il impossible d'accéder à l'état de la session à l'aide de la nouvelle API Web? HttpContext.Current.Session
est toujours nul.
MVC
Pour un projet MVC, apportez les modifications suivantes (WebForms et Dot Net Core répondent ci-dessous):
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 }
);
}
}
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 a l'avantage supplémentaire de pouvoir récupérer l'URL de base en javascript pour effectuer les appels AJAX:
<body>
@RenderBody()
<script type="text/javascript">
var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)';
</script>
@RenderSection("scripts", required: false)
et ensuite dans nos fichiers/code Javascript, nous pouvons faire nos appels webapi pouvant accéder à la session:
$.getJSON(apiBaseUrl + '/MyApi')
.done(function (data) {
alert('session data received: ' + data.whatever);
})
);
WebForms
Procédez comme ci-dessus, mais changez la fonction WebApiConfig.Register pour prendre un RouteCollection à la place:
public static void Register(RouteCollection routes)
{
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Et appelez ensuite le suivant dans Application_Start:
WebApiConfig.Register(RouteTable.Routes);
Dot Net Core
Ajoutez le package NuGet Microsoft.AspNetCore.Session , puis apportez les modifications de code suivantes:
Appelez les méthodes AddDistributedMemoryCache et AddSession de l'objet services dans la fonction ConfigureServices. :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
services.AddDistributedMemoryCache();
services.AddSession();
et dans la fonction Configurer, ajoutez un appel à UseSession :
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseSession();
app.UseMvc();
Dans votre contrôleur, ajoutez une instruction using en haut:
using Microsoft.AspNetCore.Http;
puis utilisez 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 pouvoir frapper:
http://localhost:1234/api/session/set/thisissomedata
et puis aller à cette URL va le sortir:
http://localhost:1234/api/session/get
Vous trouverez ici de nombreuses autres informations sur l'accès aux données de session dans le noyau net de base: 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 appliquer un délai de 200 ms pour les demandes simultanées. Cela pourrait s’additionner et devenir désastreux si vous avez plusieurs demandes simultanées.
Problèmes de sécurité
Assurez-vous de verrouiller les ressources par utilisateur - un utilisateur authentifié ne devrait 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 les API Web ASP.NET - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
Lisez l'article de Microsoft sur la prévention des attaques de piratage de type Cross-Site Request Forgery. (En bref, consultez la méthode AntiForgery.Validate) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks =
Vous pouvez accéder à l'état de la session à l'aide d'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
Performance, performance, performance!
Il existe une très bonne raison, souvent négligée, de ne pas utiliser du tout Session dans WebAPI.
Le fonctionnement d’ASP.NET lorsque Session est utilisée consiste à sérialiser toutes les demandes reçues d’un seul client . Maintenant, je ne parle pas de sérialisation d'objet - mais de les exécuter dans l'ordre reçu et d'attendre que chacun se termine avant d'exécuter le suivant. Ceci permet d'éviter les conditions de concurrence/thread si deux requêtes tentent chacune d'accéder à Session simultanément.
demandes simultanées et état de session
L'accès à l'état de session ASP.NET est exclusif par session, ce qui signifie que si deux utilisateurs différents effectuent des demandes simultanées, l'accès à chaque session distincte est accordé simultanément. Cependant, si deux demandes simultanées sont effectuées pour la même session (en utilisant la même valeur SessionID), la première demande obtient un accès exclusif aux informations de session. La deuxième requête ne s'exécute qu'après la fin de la première requête. (La deuxième session peut également avoir accès si le verrou exclusif sur les informations est libéré car la première demande dépasse le délai de verrouillage.) Si la valeur EnableSessionState de la directive @ Page est définie sur ReadOnly, une demande d'informations de session en lecture seule n'entraîne pas de verrouillage exclusif des données de session. Cependant, les demandes en lecture seule pour les données de session peuvent encore attendre un verrou défini par une demande en lecture-écriture pour que les données de session soient effacées.
Alors, qu'est-ce que cela signifie pour l'API Web? Si vous avez une application qui exécute de nombreuses AJAX requêtes, seule UNE pourra être exécutée à la fois. Si votre demande est plus lente, tous les autres de ce client seront bloqués jusqu'à ce qu'elle soit terminée. Dans certaines applications, cela pourrait entraîner des performances très lentes.
Donc, vous devriez probablement utiliser un contrôleur MVC si vous avez absolument besoin de quelque chose de la session des utilisateurs et éviter la pénalité de performances inutile de l'activer pour WebApi.
Vous pouvez facilement tester cela pour vous-même en mettant simplement Thread.Sleep(5000)
dans une méthode WebAPI et en activant Session. Exécutez 5 demandes et cela prendra 25 secondes au total. Sans session, cela prendra un peu plus de 5 secondes.
(Ce même raisonnement s'applique à SignalR).
Eh bien, vous avez raison, REST est apatride. Si vous utilisez une session, le traitement deviendra dynamique, les requêtes suivantes pourront utiliser l'état (d'une session).
Pour qu'une session soit réhydratée, vous devez fournir une clé permettant d'associer l'état. Dans une application asp.net normale, cette clé est fournie à l'aide d'un paramètre cookie (cookie-sessions) ou url (sessions sans cookie).
Si vous avez besoin d'une session, oubliez 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 les adresses IP.
Mark, si vous vérifiez le exemple de MVC nerddinner la logique est à peu près la même.
Il vous suffit de récupérer le cookie et de le définir 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 auprès de projet nerddinner .
Pour résoudre le problème:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
dans Global.asax.cs
Le dernier ne fonctionne pas maintenant, prenez celui-ci, cela a fonctionné 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);
}
fournd ici: http://forums.asp.net/t/1773026.aspx/1
Pour faire suite à la réponse de LachlanB, si votre ApiController ne se trouve pas dans un répertoire particulier (comme/api), vous pouvez plutôt tester la demande à l'aide de RouteTable.Routes.GetRouteData, par exemple:
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);
}
J'ai eu ce même problème dans asp.net mvc, je l'ai corrigé en mettant cette méthode dans mon contrôleur api de base dont tous mes contrôleurs api héritent:
/// <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;
}
Ensuite, dans votre appel API, vous souhaitez accéder à la session que vous venez de faire:
HttpContextWrapper httpContextWrapper = GetHttpContextWrapper();
var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];
J'ai aussi ceci dans mon fichier Global.asax.cs comme d'autres personnes l'ont posté, je ne sais pas si vous en avez toujours besoin en utilisant la méthode ci-dessus, mais ici c'est juste au cas où:
/// <summary>
/// The following method makes Session available.
/// </summary>
protected void Application_PostAuthorizeRequest()
{
if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/api"))
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Vous pouvez également créer un attribut de filtre personnalisé que vous pouvez coller sur les appels d'API dont vous avez besoin pour la session. Vous pouvez ensuite utiliser session dans votre appel d'API comme vous le feriez normalement via HttpContext.Current.Session ["SomeValue"]:
/// <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;
}
}
}
}
J'espère que cela t'aides.
J'ai suivi l'approche @LachlanB et en effet, la session était disponible lorsque le cookie de session était présent sur la demande. La partie manquante est comment le cookie de session est envoyé au client la première fois?
J'ai créé un HttpModule qui non seulement active la disponibilité de HttpSessionState, mais envoie également le cookie au client lors de la création d'une nouvelle session.
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);
}
}
une chose doit être mentionnée dans la réponse de @LachlanB.
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Si vous omettez la ligne if (IsWebApiRequest())
Si votre site est mélangé à des pages de formulaire Web, le problème de lenteur du chargement des pages s’affichera sur l’ensemble du site.