Mar*_*ark 261 asp.net asp.net-web-api
我意识到会话和REST并不完全齐头并进,但是使用新的Web API无法访问会话状态吗?HttpContext.Current.Session永远是空的.
Roc*_*lan 328
MVC
对于MVC项目进行以下更改(WebForms和Dot Net Core在下面回答):
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 }
);
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这个解决方案还有额外的好处,我们可以在javascript中获取基本URL来进行AJAX调用:
<body>
@RenderBody()
<script type="text/javascript">
var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)';
</script>
@RenderSection("scripts", required: false)
Run Code Online (Sandbox Code Playgroud)
然后在我们的Javascript文件/代码中,我们可以进行可以访问会话的webapi调用:
$.getJSON(apiBaseUrl + '/MyApi')
.done(function (data) {
alert('session data received: ' + data.whatever);
})
);
Run Code Online (Sandbox Code Playgroud)
的WebForms
执行上述操作但更改WebApiConfig.Register函数以取代RouteCollection:
public static void Register(RouteCollection routes)
{
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Run Code Online (Sandbox Code Playgroud)
然后在Application_Start中调用以下内容:
WebApiConfig.Register(RouteTable.Routes);
Run Code Online (Sandbox Code Playgroud)
Dot Net Core
添加Microsoft.AspNetCore.Session NuGet包,然后进行以下代码更改:
在ConfigureServices函数中的services对象上调用AddDistributedMemoryCache和AddSession方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
services.AddDistributedMemoryCache();
services.AddSession();
Run Code Online (Sandbox Code Playgroud)
并在Configure函数中添加对UseSession的调用:
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseSession();
app.UseMvc();
Run Code Online (Sandbox Code Playgroud)
在控制器中,在顶部添加using语句:
using Microsoft.AspNetCore.Http;
Run Code Online (Sandbox Code Playgroud)
然后在代码中使用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);
}
Run Code Online (Sandbox Code Playgroud)
你现在应该能够击中:
http://localhost:1234/api/session/set/thisissomedata
Run Code Online (Sandbox Code Playgroud)
然后转到此URL将其拉出:
http://localhost:1234/api/session/get
Run Code Online (Sandbox Code Playgroud)
有关在dot net core中访问会话数据的更多信息,请访问:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state
表现问题
阅读下面关于表现的Simon Weaver的回答.如果您正在访问WebApi项目中的会话数据,它可能会产生非常严重的性能后果 - 我已经看到ASP.NET对并发请求执行了200ms的延迟.如果您有许多并发请求,这可能会加起来并变得灾难性的.
安全问题
确保锁定每个用户的资源 - 经过身份验证的用户不应该从他们无权访问的WebApi中检索数据.
阅读Microsoft关于ASP.NET Web API中的身份验证和授权的文章 - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
阅读微软关于避免跨站点请求伪造黑客攻击的文章.(简而言之,请查看AntiForgery.Validate方法) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks
war*_*ckh 65
您可以使用自定义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;
}
}
Run Code Online (Sandbox Code Playgroud)
在此处找到:http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html
Sim*_*ver 46
性能,性能,性能!
有一个非常好的,经常被忽视的原因,你根本不应该在WebAPI中使用Session.
当Session正在使用时,ASP.NET的工作方式是序列化从单个客户端收到的所有请求.现在我不是在谈论对象序列化 - 而是按照接收的顺序运行它们并等待每个完成后再运行下一个.这是为了避免令人讨厌的线程/竞争条件,如果两个请求都尝试同时访问Session.
并发请求和会话状态
对会话状态的访问是每个会话独占的,这意味着如果两个不同的用户发出并发请求,则同时授予对每个单独会话的访问权限.但是,如果对同一会话发出两个并发请求(通过使用相同的SessionID值),则第一个请求将获得对会话信息的独占访问权.第二个请求仅在第一个请求完成后执行.(如果由于第一个请求超过锁定超时而释放信息的独占锁定,则第二个会话也可以访问.)如果@ Page指令中的EnableSessionState值设置为ReadOnly,则只读请求会话信息不会导致会话数据的独占锁定.但是,会话数据的只读请求可能仍然必须等待由会话数据的读写请求设置的锁定才能清除.
那么这对Web API意味着什么呢?如果您有一个运行许多AJAX请求的应用程序,那么一次只能运行一个.如果您的请求较慢,则会阻止该客户端中的所有其他请求,直到完成为止.在某些应用中,这可能导致非常明显的低迷性能.
所以你应该使用一个MVC控制器,如果你绝对需要来自用户会话的东西,并避免为WebApi启用它的不必要的性能损失.
您只需Thread.Sleep(5000)输入WebAPI方法并启用Session 即可轻松自行测试.运行5个请求,总共需要25秒才能完成.没有Session,他们总共需要5秒钟.
(同样的推理适用于SignalR).
Nic*_*ckz 21
那么你是对的,REST是无国籍的.如果您使用会话,则处理将变为有状态,后续请求将能够使用状态(来自会话).
为了使会话重新水合,您需要提供一个关键状态.在普通的asp.net应用程序中,密钥是使用cookie(cookie-sessions)或url参数(无cookie会话)提供的.
如果您需要会话忘记休息,则会话与基于REST的设计无关.如果您需要会话进行验证,请使用令牌或通过IP地址进行授权.
JSa*_*cho 20
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
Run Code Online (Sandbox Code Playgroud)
你必须定义你的"SampleIdentity"类,你可以从nerddinner项目中借用它.
小智 13
要解决此问题:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
Run Code Online (Sandbox Code Playgroud)
在Global.asax.cs中
Cru*_*KID 10
最后一个现在不工作,拿这个,它对我有用.
在App_Start的WebApiConfig.cs中
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
);
Run Code Online (Sandbox Code Playgroud)
Global.asax中
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
private static bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_WebApiExecutionPath);
}
Run Code Online (Sandbox Code Playgroud)
在这里:http://forums.asp.net/t/1773026.aspx/1
继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);
}
Run Code Online (Sandbox Code Playgroud)
我在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;
}
Run Code Online (Sandbox Code Playgroud)
然后在你的api电话中你想要访问你刚刚做的会话:
HttpContextWrapper httpContextWrapper = GetHttpContextWrapper();
var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];
Run Code Online (Sandbox Code Playgroud)
我也在我的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);
}
}
Run Code Online (Sandbox Code Playgroud)
您也可以创建一个自定义过滤器属性,您可以将其粘贴到您需要会话的api调用上,然后您可以像通常通过HttpContext.Current.Session ["SomeValue"]那样在api调用中使用session:
/// <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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.
我遵循@LachlanB方法,当请求中出现会话cookie时,会话确实可用.缺少的部分是第一次如何将Session 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);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
234294 次 |
| 最近记录: |