Oturumun ve REST'in el ele gitmediğinin farkındayım, ancak yeni Web API'sını kullanarak oturum durumuna erişmek mümkün değil mi? HttpContext.Current.Session
her zaman sıfırdır.
Oturumun ve REST'in el ele gitmediğinin farkındayım, ancak yeni Web API'sını kullanarak oturum durumuna erişmek mümkün değil mi? HttpContext.Current.Session
her zaman sıfırdır.
Yanıtlar:
MVC
Bir MVC projesi için aşağıdaki değişiklikleri yapın (WebForms ve Dot Net Core aşağıdadır):
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 }
);
}
}
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);
}
}
Bu çözüm, AJAX çağrıları yapmak için javascript'te temel URL'yi getirebileceğimiz ek bir bonusa sahiptir:
<body>
@RenderBody()
<script type="text/javascript">
var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)';
</script>
@RenderSection("scripts", required: false)
ve sonra Javascript dosyalarımızda / kodumuzda oturuma erişebilen webapi çağrılarımızı yapabiliriz:
$.getJSON(apiBaseUrl + '/MyApi')
.done(function (data) {
alert('session data received: ' + data.whatever);
})
);
WebForms
Yukarıdakileri yapın ancak bunun yerine bir RouteCollection almak için WebApiConfig.Register işlevini değiştirin:
public static void Register(RouteCollection routes)
{
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Ve sonra da Application_Start içinde aşağıdakileri çağırın:
WebApiConfig.Register(RouteTable.Routes);
Nokta Net Çekirdek
Microsoft.AspNetCore.Session NuGet paketini ekleyin ve aşağıdaki kod değişikliklerini yapın:
ConfigureServices işlevi içindeki hizmetler nesnesinde AddDistributedMemoryCache ve AddSession yöntemlerini çağırın :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
services.AddDistributedMemoryCache();
services.AddSession();
ve Yapılandır işlevinde UseSession'a bir çağrı ekleyin :
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseSession();
app.UseMvc();
Denetleyicinize, en üste bir using ifadesi ekleyin:
using Microsoft.AspNetCore.Http;
ve sonra kodunuzda HttpContext.Session nesnesini aşağıdaki gibi kullanın:
[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);
}
şimdi vurabiliyor olmalısınız:
http://localhost:1234/api/session/set/thisissomedata
ve sonra bu URL’ye gitmek URL’yi çıkarır:
http://localhost:1234/api/session/get
Dot net core içindeki oturum verilerine erişme hakkında daha fazla bilgi burada: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state
Performans Endişeleri
Simon Weaver'ın performansla ilgili aşağıdaki cevabını okuyun. Bir WebApi projesi içinde oturum verilerine erişiyorsanız, çok ciddi bir performans sonucu olabilir - ASP.NET'in eşzamanlı istekler için 200 ms gecikme uyguladığını gördüm. Bu, birçok eşzamanlı isteğiniz varsa toplanabilir ve felaket olabilir.
Güvenlik endişeleri
Kullanıcı başına kaynakları kilitlediğinizden emin olun; kimliği doğrulanmış bir kullanıcının WebApi'nizden erişimi olmayan verileri alamaması gerekir.
Microsoft'un ASP.NET Web API'sındaki Kimlik Doğrulama ve Yetkilendirme hakkındaki makalesini okuyun - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
Microsoft'un Siteler Arası İstek Sahteciliği saldırılarını önleme hakkındaki makalesini okuyun. (Kısacası, AntiForgery.Validate yöntemine göz atın) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks
Özel bir RouteHandler kullanarak oturum durumuna erişebilirsiniz.
// 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;
}
}
Burada bulundu: http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html
Performans, performans, performans!
Session'ı WebAPI'de hiç kullanmamanız için çok iyi ve genellikle gözden kaçan bir neden var.
Oturum kullanılırken ASP.NET'in çalışması, tek bir istemciden alınan tüm istekleri serileştirmektir . Şimdi nesne serileştirmesinden bahsetmiyorum - ancak bunları alınan sırayla çalıştırıyorum ve bir sonraki çalıştırmadan önce her birinin tamamlanmasını bekliyorum. Bu, iki istek aynı anda Oturum'a erişmeye çalışırsa, kötü iş parçacığı / yarış koşullarından kaçınmak içindir.
Eşzamanlı İstekler ve Oturum Durumu
ASP.NET oturum durumuna erişim oturum başına özeldir, yani iki farklı kullanıcı eşzamanlı istekte bulunursa, her ayrı oturuma aynı anda erişim izni verilir. Ancak, aynı oturum için (aynı SessionID değerini kullanarak) iki eşzamanlı istek yapılırsa, ilk istek oturum bilgilerine özel erişim elde eder. İkinci istek, yalnızca ilk istek tamamlandıktan sonra yürütülür.(İkinci istek, ilk istek kilit zaman aşımını aştığı için bilgilerdeki özel kilit serbest bırakıldığında da erişebilir.) @ Sayfa yönergesindeki EnableSessionState değeri ReadOnly olarak ayarlanmışsa, salt okunur isteği oturum bilgileri, oturum verilerinde özel bir kilit oluşturmaz. Ancak, oturum verileri için salt okunur isteklerin, oturum verilerinin temizlenmesi için bir okuma-yazma isteği tarafından ayarlanan bir kilidi beklemesi gerekebilir.
Peki bu Web API için ne anlama geliyor? Çok sayıda AJAX isteği çalıştıran bir uygulamanız varsa, o zaman bir seferde yalnızca BİR çalıştırılabilir. Daha yavaş bir isteğiniz varsa, bu istemciden diğer tüm işlemleri tamamlanıncaya kadar engeller. Bazı uygulamalarda bu çok belirgin bir şekilde durgun performansa neden olabilir.
Bu yüzden, kesinlikle kullanıcı oturumundan bir şeye ihtiyacınız varsa ve WebApi için etkinleştirmenin gereksiz performans cezasından kaçınmanız durumunda bir MVC denetleyicisi kullanmalısınız.
Thread.Sleep(5000)
Bir WebAPI yöntemi koyarak ve oturumu etkinleştirerek bunu kendiniz kolayca test edebilirsiniz . 5 istek çalıştırın ve bu işlemlerin tamamlanması toplam 25 saniye sürecektir. Oturum olmadan toplam 5 saniyeden biraz fazla zaman alacaktır.
(Aynı mantık SignalR için de geçerlidir).
Haklısın, REST vatansız. Bir oturum kullanırsanız, işlem durum bilgisi olur, sonraki istekler durumu (oturumdan) kullanabilir.
Bir oturumun rehidre edilmesi için, durumu ilişkilendirmek için bir anahtar sağlamanız gerekir. Normal bir asp.net uygulamasında bu anahtar bir çerez (çerez oturumları) veya url parametresi (çerezsiz oturumlar) kullanılarak sağlanır.
Bir oturumu unutmanız gerekiyorsa, oturumlar REST tabanlı tasarımlarda önemsizdir. Doğrulama için bir oturuma ihtiyacınız varsa, bir jeton kullanın veya IP adreslerine göre yetkilendirin.
Mark, nerddinner MVC örneğini kontrol ederseniz mantık hemen hemen aynıdır.
Çerezi almanız ve geçerli oturumda ayarlamanız yeterlidir.
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
Nerddinner projesinden ödünç alabileceğiniz "SampleIdentity" sınıfınızı tanımlamanız gerekir .
Sorunu çözmek için:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
Global.asax.cs içinde
Sonuncusu şu anda çalışmıyor, bunu al, benim için çalıştı.
App_Start şirketinde 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
);
Global.asax
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
private static bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_WebApiExecutionPath);
}
dördüncü burada: http://forums.asp.net/t/1773026.aspx/1
LachlanB'nin cevabından sonra, ApiController'ınız belirli bir dizinde (/ api gibi) oturmuyorsa, bunun yerine örneğin RouteTable.Routes.GetRouteData öğesini kullanarak isteği test edebilirsiniz:
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 de aynı sorunu vardı, ben tüm api denetleyicileri miras temel api denetleyicime bu yöntemi koyarak sabit:
/// <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;
}
Sonra api çağrınızda sadece oturuma erişmek istediğinizi yapın:
HttpContextWrapper httpContextWrapper = GetHttpContextWrapper();
var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];
Ayrıca, diğer insanlar gibi Global.asax.cs dosyamda var, yukarıdaki yöntemi kullanarak yine de ihtiyacınız olup olmadığından emin değilim, ama burada sadece durum:
/// <summary>
/// The following method makes Session available.
/// </summary>
protected void Application_PostAuthorizeRequest()
{
if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/api"))
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Ayrıca, oturuma ihtiyacınız olan api çağrılarınıza yapıştırabileceğiniz özel bir filtre niteliği de oluşturabilirsiniz, daha sonra normalde HttpContext.Current.Session ["SomeValue"] ile yaptığınız gibi api çağrınızda oturumu kullanabilirsiniz:
/// <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;
}
}
}
}
Bu yardımcı olur umarım.
@LachlanB yaklaşımını izledim ve gerçekten de oturum çerezi istek üzerine mevcut olduğunda oturum kullanıma sunuldu. Eksik kısım Oturum çerezi istemciye ilk kez nasıl gönderilir?
Sadece HttpSessionState kullanılabilirliğini etkinleştirmekle kalmayıp aynı zamanda yeni bir oturum oluşturulduğunda çerezi istemciye gönderen bir HttpModule oluşturdum.
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);
}
}
@LachlanB'nin cevabında bir şeyden bahsetmek gerekiyor.
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Çizgiyi atlarsanız if (IsWebApiRequest())
Siteniz web form sayfalarıyla karıştırılmışsa, tüm sitenin sayfa yükleme yavaşlığı sorunu olacaktır.
Evet, oturum Rest API ile el ele gitmez ve bu uygulamalardan kaçınmalıyız. Ancak gereksinimlere göre, her istekte istemci sunucunun durum veya veri alışverişi yapabileceği veya koruyabileceği şekilde oturumu bir şekilde sürdürmemiz gerekir. Bu nedenle, REST protokollerini bozmadan bunu başarmanın en iyi yolu JWT gibi jetonla iletişim kurmaktır.
Temel bilgilere geri dönmek neden basit kalmasın ve Oturum değerini API'nıza aktarmak için gizli bir html değerinde saklamıyor?
kontrolör
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
$ (belge) .ready (function () {
var sessionValue = $('#hBlah').val();
alert(sessionValue);
/* Now call your API with the session variable */}
}
[SessionState(SessionStateBehavior.Required)]
üzerindeApiController
hile (veya yok.ReadOnly
olduğunda).