การเข้าถึงเซสชันโดยใช้ ASP.NET Web API


268

ฉันรู้ว่าเซสชันและส่วนที่เหลือไม่ได้จับมือกัน แต่ไม่สามารถเข้าถึงสถานะเซสชันโดยใช้ Web API ใหม่ได้หรือไม่ HttpContext.Current.Sessionเป็นโมฆะเสมอ


4
[SessionState(SessionStateBehavior.Required)]ในการApiControllerหลอกลวง (หรือ.ReadOnlyตามความเหมาะสม)
Roman Starkov

@RomanStarkov ไม่สามารถใช้งานได้ คุณใช้สภาพแวดล้อมแบบใด . NET Core
Bondolin

@ Bonolin ไม่นี่ไม่ใช่หลัก
Roman Starkov

@RomanStarkov MVC แล้วเหรอ? ฉันมีปัญหาในการค้นหา
Bondolin

@Bondolin SessionStateAttributeและใช่ MVC
Roman Starkov

คำตอบ:


336

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);
    }

}

โซลูชันนี้มีโบนัสเพิ่มที่เราสามารถดึง URL พื้นฐานใน javascript สำหรับการโทร 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);
   })
);

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);

Dot Net Core

เพิ่มแพ็คเกจMicrosoft.AspNetCore.Session NuGet จากนั้นทำการเปลี่ยนแปลงรหัสต่อไปนี้:

Startup.cs

เรียกวิธีการAddDistributedMemoryCacheและAddSessionบนวัตถุบริการภายในฟังก์ชัน ConfigureServices:

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();

SessionController.cs

ภายในตัวควบคุมของคุณเพิ่มคำสั่งการใช้ที่ด้านบน:

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

ข้อมูลเพิ่มเติมมากมายเกี่ยวกับการเข้าถึงข้อมูลเซสชันภายใน 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

อ่านบทความของ Microsoft เกี่ยวกับการหลีกเลี่ยงการโจมตีแฮกจากการปลอมแปลงคำขอ (ในระยะสั้นตรวจสอบวิธี AntiForgery.Validate) - https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks


7
สมบูรณ์ ง่ายและใช้งานได้ สำหรับผู้ที่ไม่ใช่ MVC เพียงเพิ่ม Application_PostAuthorizeRequest () ใน Global.ascx.cs
mhenry1384

1
ขอบคุณ @JCallico ฉันเดาว่าคนส่วนใหญ่เข้าสู่หน้า ASP.NET ก่อนซึ่งสร้างเซสชัน
Rocklan

3
ฉันจำเป็นต้องปรับเปลี่ยน IsWebApiRequest () เพื่อกลับสู่ true โดยที่ path เริ่มต้นด้วย WebApiConfig.UrlPrefix รวมถึง WebApiConfig.UrlPrefixRelative นอกเหนือจากนั้นทำงานได้ตามที่คาดไว้
gb2d

7
สิ่งหนึ่งที่ต้องพูดถึงเกี่ยวกับการแก้ไขนี้ เมื่อตั้งค่า SessionStateBehavior เป็น Required คุณกำลังติดขัดกับ webapi เนื่องจากคำขอทั้งหมดของคุณจะทำงานแบบซิงค์เนื่องจากล็อคในวัตถุเซสชัน คุณสามารถเรียกใช้เป็น SessionStateBehavior.Readonly แทน วิธีนี้จะไม่สร้างการล็อกบนวัตถุเซสชัน
Michael Kire Hansen

2
ระวังเมื่อตั้งค่าลักษณะการทำงานของสถานะเซสชันเป็น "จำเป็น" คำร้องขอที่มีสิทธิ์ในการเขียนจะล็อคเซสชันและป้องกันไม่ให้ HttpApplications หลายรายการวางไข่ต่อลูกค้า คุณควรตั้งค่าสถานะเซสชันเป็นระดับที่เหมาะสมสำหรับแต่ละเส้นทาง โปรดอ้างอิงคำตอบของฉันที่นี่: stackoverflow.com/a/34727708/1412787
Axel Wilczek

66

คุณสามารถเข้าถึงสถานะเซสชันโดยใช้ 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


14
อัปเดต: หากฟังก์ชัน API ของคุณอ่านจากเซสชันและไม่แก้ไขเซสชันอาจเป็นความคิดที่ดีที่จะใช้ IReadOnlySessionState แทน IRequiresSessionState สิ่งนี้ทำให้มั่นใจได้ว่าเซสชั่นจะไม่ถูกล็อคระหว่างการประมวลผลฟังก์ชั่น API
warrickh

6
ไม่ทำงานสำหรับฉันใน MVC 4 - เส้นทาง RouteHandler ไม่ได้เป็นคุณสมบัติสำหรับฉัน @ LachlanB ดูเหมือนจะมีสิ่งที่เหมาะกับฉัน
bkwdesign

3
ขอบคุณ @bkwdesign สำหรับการชี้ให้เห็นทางออกของ MVC คำตอบนี้เกี่ยวข้องกับ Web API เท่านั้น
warrickh

2
ดูเหมือนจะไม่สนับสนุนแอตทริบิวต์เส้นทาง คิด?
ทิม S

ดังที่ bkwdesign ชี้ให้เห็นสิ่งนี้ไม่ได้รับการสนับสนุนอีกต่อไป อย่างไรก็ตามมีวิธีการกำหนดพฤติกรรมสถานะเซสชันต่อเส้นทางโดยใช้ DataTokens: stackoverflow.com/a/34727708/1412787
Axel Wilczek

46

ทำไมต้องหลีกเลี่ยงการใช้งานเซสชันใน WebAPI

ประสิทธิภาพ, ประสิทธิภาพ, ประสิทธิภาพ!

มีเหตุผลที่ดีมากและมักถูกมองข้ามว่าทำไมคุณไม่ควรใช้เซสชันใน WebAPI เลย

วิธี ASP.NET ทำงานเมื่อเซสชันในการใช้งานคือการเป็นอันดับคำขอทั้งหมดที่ได้รับจากลูกค้าเดียว ตอนนี้ฉันไม่ได้พูดถึงการทำให้เป็นอันดับวัตถุ - แต่ให้พวกเขาตามลำดับที่ได้รับและรอให้แต่ละให้เสร็จสมบูรณ์ก่อนที่จะใช้ต่อไป นี่คือเพื่อหลีกเลี่ยงเงื่อนไขเธรด / การแข่งขันที่น่ารังเกียจหากสองคำขอแต่ละครั้งพยายามเข้าถึงเซสชันพร้อมกัน

คำขอพร้อมกันและสถานะเซสชัน

การเข้าถึงสถานะเซสชัน ASP.NET นั้นไม่รวมต่อเซสชันซึ่งหมายความว่าหากผู้ใช้สองคนที่แตกต่างกันทำการร้องขอพร้อมกันการเข้าถึงแต่ละเซสชันที่แยกต่างหากจะได้รับพร้อมกัน อย่างไรก็ตามหากมีการร้องขอที่เกิดขึ้นพร้อมกันสองครั้งสำหรับเซสชันเดียวกัน (โดยใช้ค่า SessionID เดียวกัน) คำขอแรกจะได้รับสิทธิ์ในการเข้าถึงข้อมูลเซสชันโดยเฉพาะ การร้องขอที่สองจะดำเนินการหลังจากการร้องขอครั้งแรกเสร็จสิ้น(เซสชันที่สองยังสามารถเข้าถึงได้หากการล็อกแบบเอกสิทธิ์เฉพาะบุคคลกับข้อมูลถูกปล่อยให้เป็นอิสระเนื่องจากการร้องขอแรกเกินกว่าการหมดเวลาการล็อก) ถ้าค่า EnableSessionState ใน @ หน้าไดเรกทีฟถูกกำหนดเป็น ReadOnly คำขอสำหรับอ่านอย่างเดียว ข้อมูลเซสชันไม่ส่งผลให้มีการล็อกแบบเอกสิทธิ์เฉพาะบุคคลในข้อมูลเซสชัน อย่างไรก็ตามการร้องขอการอ่านอย่างเดียวสำหรับข้อมูลเซสชันอาจยังคงต้องรอการล็อกที่ตั้งค่าโดยการร้องขอการอ่าน - เขียนเพื่อล้างข้อมูลเซสชัน

ดังนั้นสิ่งนี้หมายความว่าสำหรับ Web API หากคุณมีแอปพลิเคชันที่เรียกใช้คำขอ AJAX จำนวนมากจะมีเพียงหนึ่งแอปเท่านั้นที่สามารถเรียกใช้ได้ในแต่ละครั้ง หากคุณมีคำขอช้าลงมันจะปิดกั้นผู้อื่นทั้งหมดจากไคลเอนต์นั้นจนกว่าจะเสร็จสมบูรณ์ ในบางแอปพลิเคชันอาจทำให้ประสิทธิภาพการทำงานช้าลงอย่างเห็นได้ชัด

ดังนั้นคุณควรใช้คอนโทรลเลอร์ MVC หากคุณต้องการบางสิ่งบางอย่างจากเซสชันผู้ใช้และหลีกเลี่ยงการปรับประสิทธิภาพที่ไม่จำเป็นในการเปิดใช้งานสำหรับ WebApi

คุณสามารถทดสอบสิ่งนี้ได้ด้วยตัวเองง่ายๆเพียงแค่ใส่Thread.Sleep(5000)เมธอด WebAPI และเปิดใช้งานเซสชัน เรียกใช้ 5 คำขอไปและพวกเขาจะใช้เวลาทั้งหมด 25 วินาทีให้เสร็จสมบูรณ์ หากไม่มีเซสชันพวกเขาจะใช้เวลาทั้งหมดเพียง 5 วินาที

(เหตุผลเดียวกันนี้นำไปใช้กับ SignalR)


18
คุณสามารถแก้ไขได้โดยใช้ [SessionState (SessionStateBehavior.ReadOnly)] หากวิธีการของคุณอ่านจากเซสชันเท่านั้น
Rocklan

21

คุณพูดถูกแล้ว REST นั้นไร้สัญชาติ หากคุณใช้เซสชันการประมวลผลจะกลายเป็นคำร้องขอที่ตามมาจะสามารถใช้สถานะ (จากเซสชัน)

เพื่อให้เซสชันได้รับการคืนสถานะคุณจะต้องใส่รหัสเพื่อเชื่อมโยงรัฐ ในแอปพลิเคชัน asp.net ปกติที่ให้คีย์โดยใช้คุกกี้ (เซสชันคุกกี้) หรือพารามิเตอร์ url (เซสชันที่ไม่มีการปรุงอาหาร)

หากคุณต้องการเซสชั่นลืมส่วนที่เหลือเซสชั่นจะไม่เกี่ยวข้องในการออกแบบตามส่วนที่เหลือ หากคุณต้องการเซสชันสำหรับการตรวจสอบความถูกต้องให้ใช้โทเค็นหรืออนุญาตตามที่อยู่ IP


10
ฉันไม่แน่ใจเกี่ยวกับเรื่องนี้ ในตัวอย่างของ Microsoft พวกเขาแสดงโดยใช้แอตทริบิวต์อนุญาต ฉันได้ลองแล้วมันใช้งานได้กับ Forms Based Authentication Web API ตระหนักถึงสถานะการรับรองความถูกต้องซึ่งจะถูกส่งผ่านในคุกกี้การตรวจสอบสิทธิ์เริ่มต้น
ทำเครื่องหมาย

4
นี่คือตัวอย่างผมหมายถึง, code.msdn.microsoft.com/ASPNET-Web-API-JavaScript-d0d64dd7 ใช้ REST Web API ใหม่ที่ใช้การรับรองความถูกต้องของฟอร์ม
Mark

4
ฉันใช้แอตทริบิวต์ [อนุญาต] เรียบร้อยแล้วโดยไม่จำเป็นต้องระบุสถานะเซสชัน ฉันเพิ่งเขียนตัวจัดการข้อความการตรวจสอบเพื่อตั้งค่าตัวตน
Antony Scott

57
ทำเครื่องหมายคุณลงเพราะคุณไม่ได้ให้คำตอบกับปัญหาของเขาและอื่น ๆ อีกมากมาย Web Api เป็นเฟรมเวิร์กแบบอะซิงโครนัสที่ใช้งานได้ดีกับ ajax heavy web app ไม่มีใครบอกว่าคุณต้องเคารพผู้เช่า RESTful ทั้งหมดเพื่อรับผลประโยชน์จากการใช้เฟรมเวิร์ก Web API
Brian Ogden

3
@MarkS มีสิทธิ์แจ้งให้ทราบว่า Web API ไม่ควรทราบสถานะเซสชัน คำตอบเชิงลบยังคงเป็นคำตอบ ขึ้นโหวต
Antoine Meltzheim

20

ทำเครื่องหมายถ้าคุณตรวจสอบตัวอย่าง 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

คุณจะต้องกำหนดของคุณ "SampleIdentity" ชั้นซึ่งคุณสามารถยืมจากโครงการ nerddinner


คลาสข้อมูลเฉพาะตัวอยู่ใน NerdDinner_2.0 \ NerdDinner \ Models \ NerdIdentity.cs
mhenry1384

สิ่งนี้ใช้ไม่ได้สำหรับฉัน (ใน. NET 4) ฉันไม่เคยมีคุกกี้นั้น ใช้งานได้เฉพาะเมื่อคุณเปิด FormsAuthentication หรือไม่
mhenry1384

คุกกี้จะถูกสร้างขึ้นจริง ๆ หลังจากคุณตรวจสอบผ่านแบบฟอร์มการเข้าสู่ระบบ คุณสามารถปรับแต่งวิธี / เมื่อสร้างขึ้นได้ดูstackoverflow.com/questions/7217105แต่คุณยังต้องการให้ผู้ใช้รับรองความถูกต้องกับเว็บเซิร์ฟเวอร์ได้อย่างมีประสิทธิภาพ
JSancho

คำถามที่ถามสำหรับ HttpContext.Current.Session และคำตอบนี้ไม่ได้อธิบายอย่างชัดเจนถึงสิ่งที่ต้องทำ ดู @LachlanB คำตอบ
JCallico

14

ในการแก้ไขปัญหา:

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

ใน Global.asax.cs


4
คำเตือน! นี่จะเปิดใช้งานเซสชันสำหรับคำขอทั้งหมด สิ่งนี้อาจส่งผลเสียต่อประสิทธิภาพการทำงานหากแอปพลิเคชันของคุณใช้ทรัพยากรที่มีอยู่ในตัว
cgatian

@cgatian แก้ปัญหาทางเลือกใดคงที่ ?
Kiquenet

ฉันคิดว่าวิธีการที่ดีที่สุดคือสิ่งที่ @Treyphor แนะนำ อย่าเปิดใช้งานสำหรับคำขอทั้งหมด เส้นทางที่มี "/ api" หรือบางอย่างใน URL นอกจากนี้หากเป็นไปได้ให้ตั้งค่าสถานะเซสชันเป็นแบบอ่านอย่างเดียวสำหรับตัวควบคุม API ของคุณ
cgatian

10

อันสุดท้ายไม่ทำงานตอนนี้เอาอันนี้ใช้ได้กับฉัน

ใน 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


นี่เป็นวิธีที่ง่ายที่สุด แต่มีข้อผิดพลาดเล็กน้อยในโค้ดเพื่อให้ใช้งานไม่ได้ ฉันได้โพสต์โซลูชันอื่นตามโซลูชันนี้โปรดแก้ไขของคุณให้ตรงกับของฉัน
Rocklan

การแก้ไขเล็กน้อยในบรรทัด _WebApiExecutionPath จำเป็นต้องอ่านสตริงคงที่สาธารณะ _WebApiExecutionPath = "~ / api";
stephen ebichondo

8

ทำตามคำตอบของ 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);
    }

8

ฉันมีปัญหาเดียวกันนี้ใน 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 ของคุณที่คุณต้องการจากนั้นคุณสามารถใช้เซสชันในการเรียก API ของคุณเหมือนปกติผ่าน 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;
        }
      }
    }
  }

หวังว่านี่จะช่วยได้


6

ฉันได้ปฏิบัติตามวิธีการของ @LachlanB และแน่นอนว่าเซสชันนั้นสามารถใช้งานได้เมื่อมีการใช้คุกกี้เซสชันตามคำขอ ส่วนที่ขาดหายไปคือวิธีที่คุกกี้เซสชันถูกส่งไปยังลูกค้าเป็นครั้งแรก

ฉันสร้าง HttpModule ซึ่งไม่เพียง แต่เปิดใช้งานความพร้อมใช้งาน HttpSessionState แต่ยังส่งคุกกี้ไปยังไคลเอนต์เมื่อสร้างเซสชันใหม่

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);
    }
}

มันใช้งานได้ดี สิ่งนี้ทำให้เซสชันเหมือนกันระหว่างคำขอตราบใดที่ยังไม่หมดเวลา ไม่แน่ใจว่าฉันจะใช้มันเป็นผลิตภัณฑ์หรือไม่จนกว่าฉันจะหาวิธีที่ดีในการเปลี่ยนสถานะเซสชันระหว่างที่จำเป็นและอ่านอย่างเดียวเพื่อหยุดการบล็อกคำขอ แต่สิ่งนี้ทำให้ฉันมีเส้นทางเริ่มต้นที่ฉันต้องการ ขอบคุณ!
Derreck Dean

3

สิ่งหนึ่งที่ต้องพูดถึงในคำตอบของ @LachlanB

protected void Application_PostAuthorizeRequest()
    {
        if (IsWebApiRequest())
        {
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
        }
    }

หากคุณเว้นบรรทัด if (IsWebApiRequest())

เว็บไซต์ทั้งหมดจะมีปัญหาการโหลดหน้าช้าหากเว็บไซต์ของคุณผสมกับหน้าเว็บฟอร์ม


0

ใช่เซสชันไม่ได้ทำงานควบคู่กับ Rest API และเราควรหลีกเลี่ยงวิธีปฏิบัตินี้ แต่ตามความต้องการเราจำเป็นต้องรักษาเซสชั่นอย่างใดในเซิร์ฟเวอร์คำขอของลูกค้าทุกคนสามารถแลกเปลี่ยนหรือรักษาสถานะหรือข้อมูล ดังนั้นวิธีที่ดีที่สุดในการบรรลุเป้าหมายโดยไม่ทำลายโปรโตคอล REST ก็คือการสื่อสารผ่านโทเค็นอย่าง JWT

https://jwt.io/


-4

กลับไปสู่พื้นฐานทำไมไม่ให้เรียบง่ายและเก็บค่าเซสชันไว้ในค่า 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" />

จาวาสคริ

$ (เอกสาร) .ready (function () {

    var sessionValue = $('#hBlah').val();

    alert(sessionValue);

    /* Now call your API with the session variable */}

}


1
ถ้าแอปพลิเคชันใช้ทั้ง MVC และ WebAPI นอกจากนี้ยังมีเหตุผลบางอย่างที่ควรจัดเก็บที่ฝั่งเซิร์ฟเวอร์เช่นโทเค็นความปลอดภัยของ Sharepoint แทนการใช้ wrapper พิเศษสำหรับการจัดเก็บโทเค็นเช่นคอนเทนเนอร์สีฟ้าสีฟ้าบางครั้งก็จะใช้เซสชันที่เหมาะสมสำหรับข้อมูลประเภทนี้ บริบทความปลอดภัยของ Sharepoint ที่นำมาใช้ในเทมเพลตแอปใช้เซสชันเพื่อเก็บบริบทความปลอดภัยเหล่านี้และมีการถ่ายโอนข้อมูลเพียงเล็กน้อย (แท็กเซสชัน) แทนที่จะเป็นข้อมูลไม่กี่กิโลไบต์ มันจะน่ากลัวถ้าบริบทเหล่านี้จะมีขนาดเล็ก ...
คอนสแตนติ Isaev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.