Могу ли я использовать сеанс в приложении ASP.NET WebApi [дубликат]

Вот худший случай:

Все казалось идеальным. Проведя пару часов, я понял, что файл jquery, который добавлен в правильную позицию, был фактически файлом с пустым контентом!

228
задан Mark 7 March 2012 в 02:49
поделиться

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

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

Выполните вышеуказанное, но измените функцию 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

Добавьте пакет 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

Проблемы производительности

Прочтите ниже ответ Саймона Уивера относительно производительности. Если вы получаете доступ к данным сеанса в проекте WebApi, это может иметь очень серьезные последствия для производительности. Я видел, что ASP.NET обеспечивает задержку в 200 мс для одновременных запросов. Это может скомпенсировать и стать катастрофическим, если у вас много одновременных запросов.


Проблемы безопасности

Убедитесь, что вы блокируете ресурсы для каждого пользователя - аутентифицированный пользователь не должен извлекать данные из вашего 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

277
ответ дан Rocklan 24 August 2018 в 03:49
поделиться

Возвращаясь к основам, почему бы не сохранить его простым и сохранить значение 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 */}

}

-4
ответ дан Andy A. 24 August 2018 в 03:49
поделиться

Ну, вы правы, REST - без гражданства. Если вы используете сеанс, обработка станет работоспособной, последующие запросы смогут использовать состояние (из сеанса).

Для того, чтобы сеанс был регидратирован, вам нужно предоставить ключ для ассоциировать государство. В обычном приложении asp.net этот ключ предоставляется с помощью cookie-сессии или параметра url (сеансы cookieless).

Если вам нужен сеанс, забудьте отдохнуть, сеансы не имеют отношения к проектам на основе REST , Если вам нужна сессия для проверки, используйте маркер или авторизацию по IP-адресам.

21
ответ дан Antony Scott 24 August 2018 в 03:49
поделиться

Последний не работает сейчас, возьмите это, он работал для меня.

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

здесь: http://forums.asp.net/t/1773026.aspx/1

10
ответ дан Cruiser KID 24 August 2018 в 03:49
поделиться

Я выполнил подход @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);
    }
}
5
ответ дан JCallico 24 August 2018 в 03:49
поделиться

нужно упомянуть о ответе @Lachlan B.

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

Если вы опустите строку if (IsWebApiRequest())

Весь сайт будет иметь проблему медленности загрузки страницы, если ваш сайт смешивается с страницами веб-формы.

3
ответ дан maxisam 24 August 2018 в 03:49
поделиться

Чтобы исправить проблему:

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

в Global.asax.cs

9
ответ дан nhahtdh 24 August 2018 в 03:49
поделиться

Вы можете получить доступ к состоянию сеанса с помощью настраиваемого 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

61
ответ дан Owen Blacker 24 August 2018 в 03:49
поделиться

Отметить, если вы проверите пример 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 .

20
ответ дан Schiavini 24 August 2018 в 03:49
поделиться

Почему бы избежать использования сеанса в WebAPI?

Производительность, производительность, производительность!

Существует очень хорошая и часто упущенная причина, почему вы не должны использовать Session в WebAPI вообще.

Способ ASP.NET, когда Session используется, - это сериализовать все запросы, полученные от одного клиента. Теперь я не говорю о сериализации объектов, но выполняю их в полученном порядке и ожидаю завершения каждого из них до запуска следующего. Это необходимо для предотвращения неприятных условий нити / гонки, если два запроса каждый раз пытаются получить доступ к сеансу одновременно.

Параллельные запросы и состояние сеанса

Доступ к Состояние сеанса ASP.NET является исключительным для каждого сеанса, а это означает, что если два разных пользователя выполняют параллельные запросы, доступ к каждому отдельному сеансу предоставляется одновременно. Однако, если два одновременных запроса сделаны для одного и того же сеанса (с использованием того же значения SessionID), первый запрос получает эксклюзивный доступ к информации о сеансе. Второй запрос выполняется только после завершения первого запроса. (Второй сеанс также может получить доступ, если исключительная блокировка информации освобождается, потому что первый запрос превышает тайм-аут блокировки.) Если значение EnableSessionState в директиве @ страницы установлено в ReadOnly, запрос для чтения только для чтения информация сеанса не приводит к исключительной блокировке данных сеанса. Однако запросы на чтение только для данных сеанса могут по-прежнему ждать блокировки, установленной с помощью запроса на чтение и запись для данных сеанса.

. Что это значит для веб-API? Если у вас есть приложение, на котором запущено множество запросов AJAX, тогда только одно может работать одновременно. Если у вас более медленный запрос, он блокирует все остальные от этого клиента до его завершения. В некоторых приложениях это может привести к очень заметной производительности.

Таким образом, вы, вероятно, должны использовать контроллер MVC, если вам абсолютно нужно что-то из сеанса пользователей, и избегайте чрезмерного снижения производительности, предоставляя его для WebApi.

Вы можете легко проверить это самостоятельно, просто поместив Thread.Sleep(5000) в метод WebAPI и включите сеанс. Запустите 5 запросов, и они займут в общей сложности 25 секунд. Без сеанса они занимают всего более 5 секунд.

(Это же рассуждение относится к SignalR).

37
ответ дан Simon_Weaver 24 August 2018 в 03:49
поделиться

Следуя ответу 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);
    }
8
ответ дан Stumblor 24 August 2018 в 03:49
поделиться

У меня была такая же проблема в 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;
        }
      }
    }
  }

Надеюсь, это поможет.

7
ответ дан Treyphor 24 August 2018 в 03:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: