Saya menyadari sesi dan REST tidak berjalan beriringan, tetapi apakah tidak mungkin untuk mengakses status sesi menggunakan Web API baru? HttpContext.Current.Session
selalu nol.
Saya menyadari sesi dan REST tidak berjalan beriringan, tetapi apakah tidak mungkin untuk mengakses status sesi menggunakan Web API baru? HttpContext.Current.Session
selalu nol.
Jawaban:
MVC
Untuk proyek MVC, lakukan perubahan berikut (WebForms dan Dot Net Core menjawab di bawah):
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);
}
}
Solusi ini memiliki bonus tambahan yang dapat kami ambil dari URL dasar dalam javascript untuk melakukan panggilan AJAX:
<body>
@RenderBody()
<script type="text/javascript">
var apiBaseUrl = '@Url.Content(ProjectNameSpace.WebApiConfig.UrlPrefixRelative)';
</script>
@RenderSection("scripts", required: false)
dan kemudian dalam file / kode Javascript kami, kami dapat membuat panggilan webapi kami yang dapat mengakses sesi:
$.getJSON(apiBaseUrl + '/MyApi')
.done(function (data) {
alert('session data received: ' + data.whatever);
})
);
WebForms
Lakukan hal di atas tetapi ubah fungsi WebApiConfig.Register untuk mengambil RouteCollection sebagai gantinya:
public static void Register(RouteCollection routes)
{
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: WebApiConfig.UrlPrefix + "/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Dan kemudian panggil yang berikut di Application_Start:
WebApiConfig.Register(RouteTable.Routes);
Dot Net Core
Tambahkan paket NuGet Microsoft.AspNetCore.Session dan kemudian buat perubahan kode berikut:
Panggil metode AddDistributedMemoryCache dan AddSession pada objek layanan di dalam fungsi ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
services.AddDistributedMemoryCache();
services.AddSession();
dan pada fungsi Konfigurasi tambahkan panggilan ke UseSession :
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseSession();
app.UseMvc();
Di dalam controller Anda, tambahkan pernyataan menggunakan di atas:
using Microsoft.AspNetCore.Http;
dan kemudian gunakan objek HttpContext.Session dalam kode Anda seperti:
[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);
}
Anda sekarang dapat menekan:
http://localhost:1234/api/session/set/thisissomedata
dan kemudian pergi ke URL ini akan menariknya:
http://localhost:1234/api/session/get
Banyak info lebih lanjut tentang mengakses data sesi dalam dot net core di sini: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state
Masalah Kinerja
Baca jawaban Simon Weaver di bawah ini tentang kinerja. Jika Anda mengakses data sesi di dalam proyek WebApi itu bisa memiliki konsekuensi kinerja yang sangat serius - Saya telah melihat ASP.NET memberlakukan penundaan 200 ms untuk permintaan bersamaan. Ini bisa bertambah dan menjadi bencana jika Anda memiliki banyak permintaan bersamaan.
Perhatian pada keamanan
Pastikan Anda mengunci sumber daya per pengguna - pengguna yang diautentikasi seharusnya tidak dapat mengambil data dari WebApi Anda yang tidak dapat mereka akses.
Baca artikel Microsoft tentang Otentikasi dan Otorisasi di ASP.NET Web API - https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
Baca artikel Microsoft tentang menghindari serangan hack Pemalsuan Permintaan Situs Web. (Singkatnya, periksa metode AntiForgery.Validate) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks
Anda dapat mengakses status sesi menggunakan RouteHandler khusus.
// 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;
}
}
Ditemukan di sini: http://techhasnoboundary.blogspot.com/2012/03/mvc-4-web-api-access-session.html
Kinerja, kinerja, kinerja!
Ada alasan yang sangat bagus, dan sering diabaikan mengapa Anda tidak harus menggunakan Sesi di WebAPI sama sekali.
Cara ASP.NET bekerja ketika Session sedang digunakan adalah untuk membuat cerita bersambung semua permintaan yang diterima dari satu klien . Sekarang saya tidak berbicara tentang serialisasi objek - tetapi menjalankannya dalam urutan yang diterima dan menunggu masing-masing untuk menyelesaikan sebelum menjalankan berikutnya. Ini untuk menghindari kondisi thread / ras yang tidak menyenangkan jika dua permintaan masing-masing mencoba mengakses Sesi secara bersamaan.
Permintaan Bersamaan dan Status Sesi
Akses ke status sesi ASP.NET bersifat eksklusif per sesi, yang berarti bahwa jika dua pengguna yang berbeda membuat permintaan secara bersamaan, akses ke setiap sesi terpisah diberikan secara bersamaan. Namun, jika dua permintaan bersamaan dibuat untuk sesi yang sama (dengan menggunakan nilai SessionID yang sama), permintaan pertama mendapat akses eksklusif ke informasi sesi. Permintaan kedua dieksekusi hanya setelah permintaan pertama selesai.(Sesi kedua juga bisa mendapatkan akses jika kunci eksklusif pada informasi dibebaskan karena permintaan pertama melebihi batas waktu kunci.) Jika nilai EnableSessionState di direktif @ Halaman diatur ke ReadOnly, permintaan untuk read-only informasi sesi tidak menghasilkan kunci eksklusif pada data sesi. Namun, permintaan baca-saja untuk data sesi mungkin masih harus menunggu kunci yang ditetapkan oleh permintaan baca-tulis untuk data sesi dihapus.
Jadi apa artinya ini untuk Web API? Jika Anda memiliki aplikasi yang menjalankan banyak permintaan AJAX maka hanya SATU yang akan dapat berjalan pada satu waktu. Jika Anda memiliki permintaan yang lebih lambat maka itu akan memblokir semua yang lain dari klien itu sampai selesai. Dalam beberapa aplikasi ini dapat menyebabkan kinerja yang sangat lambat.
Jadi Anda mungkin harus menggunakan pengontrol MVC jika Anda benar-benar membutuhkan sesuatu dari sesi pengguna dan menghindari hukuman kinerja yang tidak perlu untuk mengaktifkannya untuk WebApi.
Anda dapat dengan mudah menguji ini sendiri dengan hanya memasukkan Thread.Sleep(5000)
metode WebAPI dan mengaktifkan Sesi. Jalankan 5 permintaan untuk itu dan mereka akan membutuhkan total 25 detik untuk menyelesaikannya. Tanpa Sesi mereka akan mengambil total lebih dari 5 detik.
(Alasan yang sama ini berlaku untuk SignalR).
Ya, Anda benar, REST tidak memiliki kewarganegaraan. Jika Anda menggunakan sesi, pemrosesan akan menjadi stateful, permintaan berikutnya akan dapat menggunakan state (dari sesi).
Agar sesi dapat direhidrasi, Anda harus menyediakan kunci untuk mengaitkan negara. Dalam aplikasi asp.net normal, kunci tersebut disediakan dengan menggunakan cookie (sesi cookie) atau parameter url (sesi tanpa masak).
Jika Anda membutuhkan sesi, lupakan istirahat, sesi tidak relevan dalam desain berbasis REST. Jika Anda memerlukan sesi untuk validasi, gunakan token atau otorisasi berdasarkan alamat IP.
Mark, jika Anda memeriksa contoh MVC nerddinner logikanya hampir sama.
Anda hanya perlu mengambil cookie dan mengaturnya di sesi saat ini.
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
Anda harus mendefinisikan kelas "SampleIdentity" Anda, yang dapat Anda pinjam dari proyek nerddinner .
Untuk memperbaiki masalah:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
di Global.asax.cs
Yang terakhir tidak berfungsi sekarang, ambil yang ini, itu bekerja untuk saya.
di WebApiConfig.cs di 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);
}
keempat di sini: http://forums.asp.net/t/1773026.aspx/1
Sebagai lanjutan dari jawaban LachlanB, jika ApiController Anda tidak berada dalam direktori tertentu (seperti / api), Anda bisa menguji permintaan menggunakan RouteTable.Routes.GetRouteData, misalnya:
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);
}
Saya punya masalah yang sama di asp.net MVC, saya memperbaikinya dengan meletakkan metode ini di basis api controller saya bahwa semua pengendali api saya mewarisi dari:
/// <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;
}
Kemudian dalam panggilan api Anda bahwa Anda ingin mengakses sesi yang baru saja Anda lakukan:
HttpContextWrapper httpContextWrapper = GetHttpContextWrapper();
var someVariableFromSession = httpContextWrapper.Session["SomeSessionValue"];
Saya juga punya ini di file Global.asax.cs saya seperti orang lain telah diposting, tidak yakin apakah Anda masih membutuhkannya menggunakan metode di atas, tetapi ini dia hanya dalam kasus:
/// <summary>
/// The following method makes Session available.
/// </summary>
protected void Application_PostAuthorizeRequest()
{
if (HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/api"))
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Anda juga bisa membuat atribut filter khusus yang dapat Anda tempel di panggilan api yang Anda butuhkan sesi, kemudian Anda bisa menggunakan sesi dalam panggilan api Anda seperti yang biasa Anda lakukan melalui 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;
}
}
}
}
Semoga ini membantu.
Saya mengikuti pendekatan @LachlanB dan memang sesi itu tersedia ketika cookie sesi hadir atas permintaan. Bagian yang hilang adalah bagaimana cookie Sesi dikirim ke klien pertama kali?
Saya membuat HttpModule yang tidak hanya mengaktifkan ketersediaan HttpSessionState tetapi juga mengirimkan cookie ke klien ketika sesi baru dibuat.
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);
}
}
satu hal yang perlu disebutkan pada jawaban @LachlanB.
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
Jika Anda menghilangkan garis if (IsWebApiRequest())
Seluruh situs akan memiliki masalah kelambatan pemuatan halaman jika situs Anda dicampur dengan halaman formulir web.
Ya, sesi tidak berjalan seiring dengan API Istirahat dan juga kita harus menghindari praktik ini. Tetapi sesuai persyaratan kita perlu mempertahankan sesi entah bagaimana sehingga dalam setiap permintaan, klien dapat bertukar atau mempertahankan status atau data. Jadi, cara terbaik untuk mencapai ini tanpa melanggar protokol REST adalah berkomunikasi melalui token seperti JWT.
Kembali ke dasar mengapa tidak membuatnya sederhana dan menyimpan nilai Sesi dalam nilai html tersembunyi untuk diteruskan ke API Anda?
Pengendali
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
$ (dokumen) .ready (function () {
var sessionValue = $('#hBlah').val();
alert(sessionValue);
/* Now call your API with the session variable */}
}
[SessionState(SessionStateBehavior.Required)]
padaApiController
melakukan trik (atau.ReadOnly
jika perlu).