[ASP.NET Core 6 MVC] 使用 Session

這篇是參考自 C# Corner : How To Use Sessions In ASP.NET Core 的教學,不過文中是 .NET 5,所以和 .NET 6 設定上有點差異,再加上和過去 .NET 4 使用 namespace 後就可以無腦使用 Session 不太一樣,就做了這個筆記,以下範例使用 Visual Studio 2022,建立專案為 ASP.NET Core Web 應用程式 (Model-View-Controller)使用 .NET 6.0 架構。


下載 Session 套件

在方案總管視窗對「相依性(Dependencies)」滑鼠右鍵,選擇「管理 NuGet 套件」:

在「瀏覽」標籤頁中搜尋「Session」,應該最多人下載的是 Microsoft 的「Microsoft.AspNetCore.Session」:

依版本點選「安裝」(這裡我選最新穩定版 2.2.0):

若有變更警告點選「OK」,授權提示點選「I Accept」:

裝好後剛剛的頁面應該會顯示目前版本,且原本的「安裝」變成「解除安裝」,就可以關掉 NuGet 分頁了。


設定 Program.cs

再來要設定 Program.cs 設定檔,至方案總管視窗雙擊此檔案:

原本檔案的最上面是:

  var builder = WebApplication.CreateBuilder(args);

  // Add services to the container.
  builder.Services.AddControllersWithViews();
  
  var app = builder.Build();

在此給容器加入 Session service:

  var builder = WebApplication.CreateBuilder(args);

  // Add services to the container.
  builder.Services.AddControllersWithViews();
  builder.Services.AddDistributedMemoryCache();       // 加入記憶體供 Session 使用
  builder.Services.AddSession(options =>
  {
    options.IdleTimeout = TimeSpan.FromMinutes(1);    // 設定 Session Expire 時間
  });

  var app = builder.Build();
  
  app.UseSession();                                   // 加人 pipe line


使用 Session ,存取字串和整數

要利用 Session 存取字串可以使用 SetString() 和 GetString() 方法,而存取整數的是 SetInt32() 和 GetInt32(),方法在 HttpContext.Session 下,例如下面我用範本的 Home/Privacy 與 Home/ 兩個 Action 和 View,在 HomeController.cs 中的這兩個 Action 中加入以下程式碼:

  namespace DemoSession.Controllers
  {
    public class HomeController : Controller
    {
      public IActionResult Privacy()
      {
        // 將這兩個值存入 Session,key 分別為 "Greeting" 和 ".NET"
        HttpContext.Session.SetString("Greeting", "Hello World!!!");
        HttpContext.Session.SetInt32(".NET", 6);
        return View();
      }
      public IActionResult Index()
      {
        // 從 Session 中用 key 取出數值,賦值給 ViewBag 帶往 View 去
        ViewBag.Message = HttpContext.Session.GetString("Greeting");
        ViewBag.Version = HttpContext.Session.GetInt32(".NET");
        return View();
      }
    }
  }

而 Index 的 View 加入從 ViewBag 取出的值:

  <p>
    @ViewBag.Message                  <!-- 顯示從 Session 取出的字串 -->
  </p>
  <p>
    此版本為 .NET @ViewBag.Version     <!-- 顯示從 Session 取出的整數 -->
  </p>

執行應用程式,route 預設打開 Home/Index,會看到因為 ViewBag 的兩個變數都是空的,所以呈現缺項的檢視頁面:

如果點選範本提供的上方 Layout 中的「Privacy」連結,經過 Controller 的 Action 看到的 Privacy View :

檢視頁面沒有東西,因為我們也沒有修改他,點擊此頁是為了測試經過 Privacy 的 Action 時會做的,也就是將變數存入 Session ,這時再點擊上方的「Home」連結回到 Home/Index:

就會看到 Home/Index 出現 ViewBag 的兩個變數了,會有這兩個變數的原因是因為剛剛走過 Privacy 的 Action,在 Session 存入變數,然後在 Index 的 Action 從 Session 取出變數,賦予給 ViewBag ,使 Index 的 View 能夠呈現。

  • HttpContext.Session.SetString(key, value) 存入字串
  • HttpContext.Session.GetString(key) 取出字串
  • HttpContext.Session.SetInt(key, value) 存入整數
  • HttpContext.Session.GetInt(key) 取出整數


用 Json 序列化使 Session 存取物件

如果要存入 Session 的不只是字串和整數,可以利用 Newtonsoft 的 Json 序列化,首先要在 NuGet 管理套件下載 Newtonsoft.Json:

引用 Newtonsoft.Json 命名空間後,可使用 JsonConvert.SerializeObject(物件) 將物件轉成字串,使用 JsonConvert.DeserializeObject<類別>(字串) 將字串轉成相對應的物件。為了示範物件轉換,我在 ~/Models 寫了一個 Student.cs 類別,有字串、浮點數、日期的屬性與建構子:

  namespace DemoSession.Models
  {
    public class Student
    {
      public string Name { get; set; }
      public double Height { get; set; }
      public string Major { get; set; }
      public DateTime Birthday { get; set; }
      public Student(string name, double height, string major, DateTime birthday)
      {
        Name = name;
        Height = height;
        Major = major;
        Birthday = birthday;
      }
    }
  }

而 HomeController.cs 在 Privacy Action 實體化一個 List,新增四個 Student 物件,將他序列化放進 Session 中,而 Index Action 將資料從 Session 取出來,一個是字串給 ViewBag.StudentsString,另一個反序列化轉回 List 物件,為了處理空值有一些判斷,但不是這篇的重點,轉成物件給 ViewBag.StudentObject:

  using DemoSession.Models;
  using Microsoft.AspNetCore.Mvc;
  using Newtonsoft.Json;
  
  namespace DemoSession.Controllers
  {
    public class HomeController : Controller
    {
      public IActionResult Privacy()
      {
        // 實體化集合物件
        List<Student> students = new List<Student>();
        students.Add(new Student("May", 158.5, "ComputerScience", new DateTime(2005,5,5)));
        students.Add(new Student("Johnny", 184.0, "Mathematics", new DateTime(1997, 10, 24)));
        students.Add(new Student("Eddie", 181.0, "Biology", new DateTime(2000, 1, 28)));
        students.Add(new Student("Venom", 179.5, "Mathematics", new DateTime(1999, 7, 12)));
        // 將物件序列化成字串,以 "stuList" 為 key 存到 Session 中
        HttpContext.Session.SetString("stuList", JsonConvert.SerializeObject(students));
        return View();
      }

      public IActionResult Index()
      {
        // 從 Session 以 "stuList" 為 key 取出字串資料
        string StudentsString = HttpContext.Session.GetString("stuList");
        ViewBag.StudentsString = StudentsString;
        
        // 若不為空值,將字串反序列化回集合物件
        ViewBag.StudentsObject = string.IsNullOrEmpty(StudentsString) ? 
              null : JsonConvert.DeserializeObject<List<Student>>(StudentsString);
        return View();
      }
    }
  }

Index 的檢視中,將 Controller 帶過去的 ViewBag 資料顯示出來,一個是字串的 ViewBag.StudentsString,另一個是確定非空時用 foreach 遍尋出來用表格呈現的 ViewBag.StudentsObject,兩者都是來自 Session。index.cshtml 程式碼如下:

  <p>
    字串資料: @ViewBag.StudentsString 
  </p>
  <p>
    <table class="table table-striped">
      <tr>
        <th>Name</th>
        <th>Height</th>
        <th>Major</th>
        <th>Birthday</th>
      </tr>
  @{
    if(ViewBag.StudentsObject != null)
    {
      foreach(Student student in ViewBag.StudentsObject)
      {
      <tr>
        <td>@student.Name</td>
        <td>@student.Height</td>
        <td>@student.Major</td>
        <td>@student.Birthday</td>
      </tr>
      }
    }
  }
    
    </table>
</p>

執行應用程式,先到 Home/Index 是沒有東西的,此時 Session 還沒有資料:

點擊 Home/Privacy 再回到 Home/Index 就會因為 Session 有資料,使得 List<Student> 物件以表格呈現,成功在 Session 傳遞物件:

  • Newtonsoft.Json.JsonConvert.SerializeObject(object) 序列化物件成字串
  • Newtonosft.Json.JsonConvert.DeserializeObject<class>(string) 反序列化成物件


將序列化寫成擴充方法

可以將以上序列化及反序列化的方法寫成 HttpContext.Session 的靜態擴充方法,在專案新增一個資料夾 ~/Extensions/,新增一個類別 SessionExtension.cs,程式碼:

  using Newtonsoft.Json;

  namespace DemoSession.Extensions
  {
    public static class SessionExtension
    {
      public static void SetObject<T>(this ISession session, string key, T value)
      {
        session.SetString(key, JsonConvert.SerializeObject(value));
      }

      public static T GetObject<T>(this ISession session, string key)
      {
        var value = session.GetString(key);
        return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
      }
    }
  }

使用時就可直接 HttpContext.Session.SetObject<>() 與 GetObject<>():

  using DemoSession.Models;
  using DemoSession.Extensions;

  namespace DemoSession.Controllers
  {
    public class HomeController : Controller
    {
      public IActionResult Index()
      {
        // 從 Session 中用 "stuList" 的 key 取出物件
        ViewBag.StudentsObject = HttpContext.Session.GetObject<List<Student>>("stuList");
        return View();
      }

      public IActionResult Privacy()
      {            
        List<Student> students = new List<Student>();
        students.Add(new Student("May", 158.5, "ComputerScience", new DateTime(2005,5,5)));
        students.Add(new Student("Johnny", 184.0, "Mathematics", new DateTime(1997, 10, 24)));
        students.Add(new Student("Eddie", 181.0, "Biology", new DateTime(2000, 1, 28)));
        students.Add(new Student("Venom", 179.5, "Mathematics", new DateTime(1999, 7, 12)));
        // 將物件以 "stuList" 的 key 存入 Session
        HttpContext.Session.SetObject<List<Student>>("stuList", students);
        return View();
      }
    }
  }

可更簡潔存取 Session 中的物件。

留言