세션과 REST가 정확히 일치하지는 않지만 새로운 웹 API를 사용하여 세션 상태에 액세스 할 수 없습니까? HttpContext.Current.Session
항상 null입니다.
세션과 REST가 정확히 일치하지는 않지만 새로운 웹 API를 사용하여 세션 상태에 액세스 할 수 없습니까? HttpContext.Current.Session
항상 null입니다.
답변:
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 }
);
}
}
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);
}
}
이 솔루션에는 AJAX 호출을 위해 자바 스크립트에서 기본 URL을 가져올 수있는 추가 보너스가 있습니다.
<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);
})
);
WebForms
위의 작업을 수행하지만 대신 WebApiConfig.Register 함수를 변경하여 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 패키지를 후 다음 코드를 변경하십시오.
ConfigureServices 함수 내의 서비스 오브젝트에서 AddDistributedMemoryCache 및 AddSession 메소드를 호출하십시오 .
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
services.AddDistributedMemoryCache();
services.AddSession();
구성 기능에서 UseSession 에 대한 호출을 추가하십시오 .
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseSession();
app.UseMvc();
컨트롤러 내에서 맨 위에 using 문을 추가하십시오.
using Microsoft.AspNetCore.Http;
그런 다음 코드 내에서 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);
}
당신은 지금 칠 수 있어야합니다 :
http://localhost:1234/api/session/set/thisissomedata
이 URL로 이동하면 다음과 같은 결과가 나타납니다.
http://localhost:1234/api/session/get
닷넷 코어 내에서 세션 데이터에 액세스하는 방법에 대한 자세한 정보는 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state에서 확인 하십시오.
성능 문제
성능에 관한 아래 Simon Weaver의 답변을 읽으십시오. WebApi 프로젝트 내에서 세션 데이터에 액세스하는 경우 성능에 심각한 결과를 초래할 수 있습니다. ASP.NET은 동시 요청에 대해 200ms 지연을 강제하는 것으로 나타났습니다. 동시 요청이 많을 경우 이로 인해 혼란이 발생할 수 있습니다.
보안 문제
인증 된 사용자는 WebApi에서 액세스 권한이없는 데이터를 검색 할 수 없어야합니다.
ASP.NET 웹 API의 인증 및 권한 부여에 대한 Microsoft의 기사를 읽으십시오-https: //www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
Cross-Site Request Forgery 핵 공격 방지에 관한 Microsoft의 기사를 읽으십시오. (즉, AntiForgery.Validate 방법을 확인) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks
사용자 정의 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을 전혀 사용하지 않아야하는 매우 좋고 종종 간과 된 이유가 있습니다.
세션이 사용 중일 때 ASP.NET이 작동하는 방식 은 단일 클라이언트로부터받은 모든 요청 을 직렬화하는 것 입니다. 이제 객체 직렬화에 대해 이야기하는 것이 아니라 수신 된 순서대로 실행하고 다음을 실행하기 전에 각각이 완료되기를 기다리는 것입니다. 이는 두 요청이 각각 세션에 동시에 액세스하려고 할 때 불쾌한 스레드 / 경쟁 조건을 피하기위한 것입니다.
동시 요청 및 세션 상태
ASP.NET 세션 상태에 대한 액세스는 세션마다 배타적이므로 두 명의 다른 사용자가 동시에 요청하면 각 개별 세션에 대한 액세스가 동시에 부여됩니다. 그러나 동일한 세션에 대해 동일한 SessionID 값을 사용하여 두 개의 동시 요청을하는 경우 첫 번째 요청은 세션 정보에 독점적으로 액세스합니다. 두 번째 요청은 첫 번째 요청이 완료된 후에 만 실행됩니다.첫 번째 요청이 잠금 시간 종료를 초과하여 정보에 대한 독점 잠금이 해제되면 두 번째 세션도 액세스 할 수 있습니다. @ Page 지시문의 EnableSessionState 값이 ReadOnly로 설정되면 읽기 요청 -세션 정보만으로는 세션 데이터에 대한 독점 잠금이 발생하지 않습니다. 그러나 세션 데이터에 대한 읽기 전용 요청은 여전히 세션 데이터에 대한 읽기 / 쓰기 요청에 의해 설정된 잠금이 지워질 때까지 기다려야 할 수 있습니다.
그렇다면 이것이 Web API의 의미는 무엇입니까? 많은 AJAX 요청을 실행하는 애플리케이션이있는 경우 한 번에 하나만 실행할 수 있습니다. 요청이 느리면 완료 될 때까지 해당 클라이언트의 다른 모든 요청을 차단합니다. 일부 응용 프로그램에서는 성능이 매우 느려질 수 있습니다.
따라서 사용자 세션에서 무언가가 절대적으로 필요하고 WebApi에 대해 불필요한 성능 저하를 피하려면 MVC 컨트롤러를 사용해야합니다.
Thread.Sleep(5000)
WebAPI 메서드를 넣고 Session을 사용하면 쉽게 테스트 할 수 있습니다 . 요청을 5 개 실행하면 완료하는 데 총 25 초가 걸립니다. 세션이 없으면 총 5 초 이상 소요됩니다.
(동일한 추론이 SignalR에 적용됩니다).
REST는 상태 비 저장입니다. 세션을 사용하면 처리는 상태 저장 상태가되고 후속 요청은 세션의 상태를 사용할 수 있습니다.
세션을 재수 화하려면 상태를 연결하는 키를 제공해야합니다. 일반적인 asp.net 응용 프로그램에서 해당 키는 쿠키 (cookie-sessions) 또는 url 매개 변수 (cookieless sessions)를 사용하여 제공됩니다.
세션 잊어 버려 휴식이 필요한 경우 세션은 REST 기반 설계와 관련이 없습니다. 유효성 검사를 위해 세션이 필요한 경우 토큰을 사용하거나 IP 주소로 권한을 부여하십시오.
Mark, nerddinner MVC 예제 를 확인 하면 논리가 거의 동일합니다.
쿠키를 검색하여 현재 세션에서만 설정하면됩니다.
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 프로젝트 에서 빌릴 수있는 "SampleIdentity"클래스를 정의해야합니다 .
문제를 해결하려면
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
Global.asax.cs에서
마지막 하나는 지금 작동하지 않습니다, 이것을 가지고, 그것은 나를 위해 일했습니다.
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
);
Global.asax
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
private static bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_WebApiExecutionPath);
}
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);
}
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 호출을 고수 할 수있는 사용자 정의 필터 속성을 만들 수도 있습니다. 그런 다음 HttpContext.Current.Session [ "SomeValue"]를 통해 평소처럼 API 호출에서 세션을 사용할 수 있습니다.
/// <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;
}
}
}
}
도움이 되었기를 바랍니다.
@LachlanB 접근 방식을 따르고 실제로 세션 쿠키가 요청에있을 때 세션을 사용할 수있었습니다. 누락 된 부분은 세션 쿠키가 클라이언트에 처음으로 전송되는 방법입니다.
HttpSessionState 가용성을 활성화 할뿐만 아니라 새 세션이 생성 될 때 쿠키를 클라이언트로 보내는 HttpModule을 만들었습니다.
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);
}
}
예, 세션은 Rest API와 밀접한 관련이 없으며 이러한 관행을 피해야합니다. 그러나 요구 사항에 따라 모든 요청에서 클라이언트 서버가 상태 또는 데이터를 교환하거나 유지할 수 있도록 세션을 유지해야합니다. 따라서 REST 프로토콜을 위반하지 않고이를 달성하는 가장 좋은 방법은 JWT와 같은 토큰을 통해 통신하는 것입니다.
기본으로 돌아가서 단순하게 유지하고 세션 값을 숨겨진 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" />
자바 스크립트
$ (document) .ready (function () {
var sessionValue = $('#hBlah').val();
alert(sessionValue);
/* Now call your API with the session variable */}
}
[SessionState(SessionStateBehavior.Required)]
온ApiController
트릭 (또는 않는.ReadOnly
적절한을).