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


当前回答

您可以使用自定义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中避免使用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)。

要解决这个问题:

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

在Global.asax.cs

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

最后一个不行,拿这个,我用过。

在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

Mark,如果你检查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项目中借用这个类。