12 ответов:
MVC
для проекта MVC внесите следующие изменения (WebForms и Dot Net Core ответ ниже):
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 } ); } }глобальные.асакс.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); } }это решение имеет дополнительный бонус, что мы можем получить базовый URL в javascript для выполнения вызовов AJAX:
файл _Layout.cshtml
<body> @RenderBody() <script type="text/javascript"> var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)'; </script> @RenderSection("scripts", required: false)и затем в наших файлах Javascript / коде мы можем сделать наши вызовы webapi что может получить доступ к сессии:
$.getJSON(apiBaseUrl + '/MyApi') .done(function (data) { alert('session data received: ' + data.whatever); }) );
WebForms
сделайте выше, но измените WebApiConfig.Регистрация функции, чтобы принять RouteCollection вместо:
public static void Register(RouteCollection routes) { routes.MapHttpRoute( name: "DefaultApi", routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }а затем вызвать следующее В Application_Start:
WebApiConfig.Register(RouteTable.Routes);
Dot Net Core
добавить Microsoft.AspNetCore.Сессия NuGet пакет, а затем сделать следующий код изменения:
Автозагрузка.cs
вызов AddDistributedMemoryCache и AddSession методы объекта services в функции ConfigureServices:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); ... services.AddDistributedMemoryCache(); services.AddSession();и в функции Configure добавьте вызов UseSession:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseSession(); app.UseMvc();SessionController.cs
в вашем контроллере добавьте оператор using вверху:
using Microsoft.AspNetCore.Http;и затем использовать HttpContext.Объект сеанса в вашем коде выглядит так:
[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); }теперь вы должны быть в состоянии ударить:
http://localhost:1234/api/session/set/thisissomedataа затем перейдя на этот URL вытащит его:
http://localhost:1234/api/session/getмного больше информации о доступе к данным сеанса в dot net core здесь:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state
Производительностью
прочитайте ответ Саймона Уивера ниже относительно спектакль. Если вы получаете доступ к данным сеанса внутри проекта WebApi, это может иметь очень серьезные последствия для производительности - я видел ASP.NET примените задержку в 200 мс для одновременных запросов. Это может сложиться и стать катастрофическим, если у вас есть много одновременных запросов.
Проблемы Безопасности
убедитесь, что вы блокируете ресурсы для каждого пользователя-аутентифицированный пользователь не должен иметь возможность получать данные из вашего WebApi, которые они не делают иметь доступ.
прочитайте статью Microsoft по аутентификации и авторизации в ASP.NET Web API - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
прочитайте статью Microsoft об избежании межсайтовых запросов подделки хакерских атак. (Короче говоря, проверьте AntiForgery.Метод validate) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks
вы можете получить доступ к состоянию сеанса с помощью пользовательского RouteHandler.
// 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; } }найдено здесь: http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html
почему бы не использовать сеанс в WebAPI?
производительность, производительность, производительность!
есть очень хорошая и часто упускаемая из виду причина, по которой Вы вообще не должны использовать сеанс в WebAPI.
путь ASP.NET работает, когда сеанс используется для сериализовать все запросы, полученные от одного клиента. Теперь я не говорю о сериализации объектов - но запускаю их в полученном порядке и жду завершения каждого перед запуском следующего. Это делается для того, чтобы избежать неприятных условий потока / гонки, если два запроса каждый пытается получить доступ к сеансу одновременно.
параллельные запросы и состояние сеанса
доступ к ASP.NET состояние сеанса является эксклюзивным для каждого сеанса, что означает, что если два разных пользователя делают параллельные запросы, доступ к каждому отдельному сеансу предоставляется одновременно. Однако,если два одновременных запросов для тот же сеанс (с использованием того же значения SessionID), первый запрос получает эксклюзивный доступ к информации о сеансе. Второй запрос выполняется только после завершения первого запроса. (вторая сессия может также получить доступ, если исключительная блокировка на информацию освобождается потому что первый запрос превышает время ожидания блокировки.) Если Значение EnableSessionState в директиве @ Page имеет значение только для чтения, a запрос информации о сеансе только для чтения не приводит к эксклюзивная блокировка данных сеанса. Однако запросы только для чтения данные сеанса все еще могут ожидать блокировки, установленной методом чтения-записи запрос на очистку данных сеанса.
Так что же это значит для веб-API? Если у вас есть приложение, выполняющее много запросов AJAX, то только один из них будет работать одновременно. Если у вас есть более медленный запрос, то он будет блокировать все остальные от этого клиента, пока он не будет завершен. В некоторых приложениях это может привести к очень заметно вялое исполнение.
поэтому вы, вероятно, должны использовать контроллер MVC, если вам абсолютно нужно что-то из сеанса пользователей и избежать ненужного снижения производительности при включении его для WebApi.
вы можете легко проверить это сами, просто поставив
Thread.Sleep(5000)в методе WebAPI и включить сеанс. Запустите 5 запросов к нему, и они займут в общей сложности 25 секунд. Без сеанса они займут в общей сложности чуть более 5 секунд.(эта то же самое рассуждение относится и к SignalR).
Ну вы правы, отдых без гражданства. Если вы используете сеанс, обработка станет stateful, последующие запросы смогут использовать состояние (из сеанса).
кроме того, на сессии будут восстановлены, вам необходимо поставить ключ, чтобы связать государство. В нормальном состоянии asp.net приложение, которое ключ поставляется с помощью cookie (cookie-сеансы) или url-параметр (cookieless сеансы).
Если вам нужен сеанс забыть отдых, сеансы не имеют значения в REST основе дизайны. Если вам нужен сеанс для проверки, то используйте маркер или авторизуйте по IP-адресам.
отметьте, если вы проверяете пример nerddinner в MVC логика такая же.
вам нужно только получить файл cookie и установить его в текущем сеансе.
глобальные.асакс.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вам нужно будет определить свой класс" SampleIdentity", который вы можете заимствовать из проект nerddinner.
последний не работает сейчас, возьмите этот, он работал для меня.
в 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 );глобальные.асакс
protected void Application_PostAuthorizeRequest() { if (IsWebApiRequest()) { HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required); } } private static bool IsWebApiRequest() { return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_WebApiExecutionPath); }fournd здесь:http://forums.asp.net/t/1773026.aspx/1
решить проблему:
protected void Application_PostAuthorizeRequest() { System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required); }в глобальном.асакс.cs
следуя ответу LachlanB, если ваш ApiController не находится в определенном каталоге (например, /api), вы можете вместо этого проверить запрос с помощью RouteTable.Маршруты.GetRouteData, например:
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); }
у меня была такая же проблема в asp.net mvc, я исправил это, поместив этот метод в мой базовый контроллер api, который все мои контроллеры api наследуют от:
/// <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; }затем в вашем вызове api, который вы хотите получить доступ к сеансу, который вы просто делаете:
HttpContextWrapper httpContextWrapper = GetHttpContextWrapper(); var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];у меня также есть это в моем глобальном.асакс.cs-файл, как и другие люди, опубликовал, не уверен, что он вам все еще нужен, используя метод выше, но вот он на всякий случай:
/// <summary> /// The following method makes Session available. /// </summary> protected void Application_PostAuthorizeRequest() { if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/api")) { HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required); } }вы также можете просто сделать заказ атрибут фильтра, который вы можете использовать в своих вызовах api, которые вам нужны в сеансе, затем вы можете использовать сеанс в своем вызове api, как обычно, через HttpContext.Текущий.Сессия["Значение"]:
/// <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; } } } }надеюсь, что это помогает.
я следовал подходу @LachlanB, и действительно сеанс был доступен, когда файл cookie сеанса присутствовал в запросе. Недостающая часть-это то, как файл cookie сеанса отправляется клиенту в первый раз?
Я создал HttpModule, который не только позволяет доступность HttpSessionState, но и отправляет cookie клиенту при создании нового сеанса.
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); } }
одно нужно упомянуть в ответе @LachlanB.
protected void Application_PostAuthorizeRequest() { if (IsWebApiRequest()) { HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required); } }Если вы опустите строку
if (IsWebApiRequest())весь сайт будет иметь проблему медленной загрузки страницы, если ваш сайт смешивается со страницами веб-формы.
возвращаясь к основам, почему бы не сохранить его простым и сохранить значение сеанса в скрытом значении html для передачи в ваш API?
контроллер
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
$(документ).готовые(функция () {
var sessionValue = $('#hBlah').val(); alert(sessionValue); /* Now call your API with the session variable */}}
Comments