我知道会话和REST并不完全是齐头并进的,但是使用新的Web API访问会话状态是不可能的吗?session总是空的。


当前回答

我在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"];

我在我的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;
        }
      }
    }
  }

希望这能有所帮助。

其他回答

回到基础,为什么不保持它的简单和存储会话值在一个隐藏的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 */}

}

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

为什么在WebAPI中避免使用Session ?

业绩,业绩,业绩!

有一个很好的,但经常被忽视的原因,为什么你不应该在WebAPI中使用Session。

ASP。NET在使用Session时的工作是序列化从单个客户端接收的所有请求。现在我不是在谈论对象序列化——而是按照接收到的顺序运行它们,并在运行下一个之前等待每个对象完成。这是为了避免当两个请求同时访问Session时出现糟糕的线程/竞争情况。

Concurrent Requests and Session State Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. (The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out.) If the EnableSessionState value in the @ Page directive is set to ReadOnly, a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.

那么这对Web API意味着什么呢?如果您的应用程序运行许多AJAX请求,那么一次只能运行一个。如果你有一个较慢的请求,那么它将阻止来自该客户端的所有其他请求,直到它完成。在某些应用程序中,这可能会导致非常明显的缓慢性能。

因此,如果你绝对需要用户会话中的某些东西,你可能应该使用MVC控制器,以避免为WebApi启用它所带来的不必要的性能损失。

你可以通过将Thread.Sleep(5000)放在WebAPI方法中并启用Session来轻松测试。向它运行5个请求,总共需要25秒才能完成。如果没有塞申斯,他们总共只需要5秒多一点。

(同样的道理也适用于SignalR)。

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

}

这个解决方案有额外的好处,我们可以在javascript中获取基本URL来进行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);
   })
);

网络表单

执行上述操作,但更改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);

网点核心

添加Microsoft.AspNetCore.Session NuGet包,然后进行以下代码更改:

Startup.cs

在ConfigureServices函数的服务对象上调用AddDistributedMemoryCache和AddSession方法:

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

更多关于在。net core中访问会话数据的信息请访问:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state

性能问题

下面是西蒙·韦弗关于性能的回答。如果你在WebApi项目中访问会话数据,可能会有非常严重的性能后果。NET对并发请求强制执行200ms延迟。如果您有许多并发请求,这可能会导致灾难性的后果。


安全问题

确保你为每个用户锁定了资源——一个经过身份验证的用户不应该能够从你的WebApi中检索他们没有权限访问的数据。

阅读微软关于ASP中的身份验证和授权的文章。NET Web API - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

阅读微软关于避免跨站点请求伪造黑客攻击的文章。(简而言之,看看AntiForgery。验证方法)- https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks

@LachlanB的回答中有一件事需要提一下。

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

如果省略了If (IsWebApiRequest())

如果你的网站混合了网页表单,整个网站都会有页面加载缓慢的问题。