Вот худший случай:
Все казалось идеальным. Проведя пару часов, я понял, что файл jquery, который добавлен в правильную позицию, был фактически файлом с пустым контентом!
MVC
blockquote>Для проекта 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 } ); } }
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); } }
Это решение имеет дополнительный бонус, что мы можем получить базовый 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
blockquote>Выполните вышеуказанное, но измените функцию WebApiConfig.Register, чтобы вместо этого использовать 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
blockquote>Добавьте пакет Microsoft.AspNetCore.Session NuGet, а затем выполните следующие изменения кода:
Запуск .cs
Вызвать методы AddDistributedMemoryCache и AddSession для объекта services в функции ConfigureServices: [/ g 19]
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); ... services.AddDistributedMemoryCache(); services.AddSession();
и в функции «Настройка» добавьте вызов UseSession:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseSession(); app.UseMvc();
SessionController.cs
В своем контроллере добавьте оператор использования вверху :
using Microsoft.AspNetCore.Http;
, а затем используйте объект HttpContext.Session внутри вашего кода следующим образом:
[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
Больше информации о доступе к данным сеанса в точечном ядре сети здесь: https://docs.microsoft.com/ en-us / aspnet / core / основы / app-state
Проблемы производительности
blockquote>Прочтите ниже ответ Саймона Уивера относительно производительности. Если вы получаете доступ к данным сеанса в проекте WebApi, это может иметь очень серьезные последствия для производительности. Я видел, что ASP.NET обеспечивает задержку в 200 мс для одновременных запросов. Это может скомпенсировать и стать катастрофическим, если у вас много одновременных запросов.
Проблемы безопасности
blockquote>Убедитесь, что вы блокируете ресурсы для каждого пользователя - аутентифицированный пользователь не должен извлекать данные из вашего WebApi, к которому у них нет доступа.
Прочитайте статью Microsoft об аутентификации и авторизации в веб-интерфейсе ASP.NET - 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
Возвращаясь к основам, почему бы не сохранить его простым и сохранить значение Session в скрытом значении html для перехода к вашему API?
Controller
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
$ (document) .ready (function () {
var sessionValue = $('#hBlah').val();
alert(sessionValue);
/* Now call your API with the session variable */}
}
Ну, вы правы, REST - без гражданства. Если вы используете сеанс, обработка станет работоспособной, последующие запросы смогут использовать состояние (из сеанса).
Для того, чтобы сеанс был регидратирован, вам нужно предоставить ключ для ассоциировать государство. В обычном приложении asp.net этот ключ предоставляется с помощью cookie-сессии или параметра url (сеансы cookieless).
Если вам нужен сеанс, забудьте отдохнуть, сеансы не имеют отношения к проектам на основе REST , Если вам нужна сессия для проверки, используйте маркер или авторизацию по IP-адресам.
Последний не работает сейчас, возьмите это, он работал для меня.
в 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);
}
Я выполнил подход @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);
}
}
нужно упомянуть о ответе @Lachlan B.
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Если вы опустите строку if (IsWebApiRequest())
Весь сайт будет иметь проблему медленности загрузки страницы, если ваш сайт смешивается с страницами веб-формы.
Чтобы исправить проблему:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
в Global.asax.cs
Вы можете получить доступ к состоянию сеанса с помощью настраиваемого 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 -доступ-session.html
Отметить, если вы проверите пример nerddinner MVC , логика практически не изменится.
Вам нужно только извлечь файл cookie и установить его в текущем сеансе.
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
Вам нужно будет определить свой класс «SampleIdentity», который вы можете взять из проекта nerddinner .
Производительность, производительность, производительность!
Существует очень хорошая и часто упущенная причина, почему вы не должны использовать Session в WebAPI вообще.
Способ ASP.NET, когда Session используется, - это сериализовать все запросы, полученные от одного клиента. Теперь я не говорю о сериализации объектов, но выполняю их в полученном порядке и ожидаю завершения каждого из них до запуска следующего. Это необходимо для предотвращения неприятных условий нити / гонки, если два запроса каждый раз пытаются получить доступ к сеансу одновременно.
Параллельные запросы и состояние сеанса
Доступ к Состояние сеанса ASP.NET является исключительным для каждого сеанса, а это означает, что если два разных пользователя выполняют параллельные запросы, доступ к каждому отдельному сеансу предоставляется одновременно. Однако, если два одновременных запроса сделаны для одного и того же сеанса (с использованием того же значения SessionID), первый запрос получает эксклюзивный доступ к информации о сеансе. Второй запрос выполняется только после завершения первого запроса. (Второй сеанс также может получить доступ, если исключительная блокировка информации освобождается, потому что первый запрос превышает тайм-аут блокировки.) Если значение EnableSessionState в директиве @ страницы установлено в ReadOnly, запрос для чтения только для чтения информация сеанса не приводит к исключительной блокировке данных сеанса. Однако запросы на чтение только для данных сеанса могут по-прежнему ждать блокировки, установленной с помощью запроса на чтение и запись для данных сеанса.
blockquote>. Что это значит для веб-API? Если у вас есть приложение, на котором запущено множество запросов AJAX, тогда только одно может работать одновременно. Если у вас более медленный запрос, он блокирует все остальные от этого клиента до его завершения. В некоторых приложениях это может привести к очень заметной производительности.
Таким образом, вы, вероятно, должны использовать контроллер MVC, если вам абсолютно нужно что-то из сеанса пользователей, и избегайте чрезмерного снижения производительности, предоставляя его для WebApi.
Вы можете легко проверить это самостоятельно, просто поместив
Thread.Sleep(5000)
в метод WebAPI и включите сеанс. Запустите 5 запросов, и они займут в общей сложности 25 секунд. Без сеанса они занимают всего более 5 секунд.(Это же рассуждение относится к SignalR).
Следуя ответу LachlanB, если ваш ApiController не находится в определенном каталоге (например, / api), вы можете вместо этого проверить запрос с помощью RouteTable.Routes.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;
}
Затем в вашем апи-вызове, который вы хотите получить доступ к сеансу, который вы только что сделали:
HttpContextWrapper httpContextWrapper = GetHttpContextWrapper();
var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];
У меня также есть это в моем файле Global.asax.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.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;
}
}
}
}
Надеюсь, это поможет.