ASP.NET 的 Request 物件的生命週期很短,只會出現在一個 Http Request,當需要跨物件傳遞資料時,比如 HttpModule、HttpHandler、Page 、Controller,可以善用 Request 物件來存放短暫的狀態。
既然可以傳遞物件,那麼我們也可以在 Request 初始化的時候,將所需要的物件注入至 Request 裡面,然後再到到 Page / Controller 取出來用;在不同的專案範本可以使用的 Request 物件都不太一樣,接下來分享我已經知道的寫法。
開發環境
- VS IDE 2019
- IIS Host ASP.NET 4.8 WebForm / MVC5 / Web API
- OWIN Host Web API
- ASP.NET Core 3.1
存放狀態的方式是 key-value,型別為 IDictionary or IDictionary<string,object>
System.Web.HttpContext.Items
- .NET 2.0 的時代用 HttpContext.Items (IDictionary Type),到了 .NET 3.5 之後可以改用 HttpContextBase.Items。
- HttpContextWrapper 則是用來把 HttpContext 變成 HttpContextBase。
- HttpContextBase 是一個抽象,可以讓我們更好測試 [Unit Test] 使用 HttpContextBase 取代 HttpContext.Current,提高可測試性
- HttpContext.Items 是 IDictionary 型別,Key 對應 Object。
- 適用 ASP.NET WebForm、ASP.NET MVC。
從 Global.asax 注入 Member 物件至 HttpContext.Items
public class Global : HttpApplication
{
protected void Application_BeginRequest()
{
var member = new Member {Id = Guid.NewGuid(), Name = Name.FullName()};
var key = member.GetType().FullName;
HttpContext.Current.Items[key] = member;
}
}
在 Default.aspx.cs 取出,這裡我用了 HttpContextWrapper 把 HttpContext 轉成 HttpContextBase
public partial class Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
{
return;
}
var httpContext = new HttpContextWrapper(HttpContext.Current);
var key = typeof(Member).FullName;
var member = httpContext.Items[key] as Member;
this.Id_Label.Text = member.Id.ToString();
this.Name_Label.Text = member.Name;
}
}
System.Web.HttpContextBase.Items
- Controller 依賴 HttpContextBase 非 HttpContext
- 適用 ASP.NET MVC5
在 ActionFilterAttribute 注入 Member 物件至 HttpContextBase.Items
public class InjectionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//初始化物件
var member = new Member {Id = Guid.NewGuid(), Name =Faker.Name.FullName()};
var key = member.GetType().FullName;
//注入到 HttpContextBase
filterContext.RequestContext.HttpContext.Items[key] = member;
}
}
在 Controller 取出 Member 物件
public class HomeController : Controller
{
// GET: Default
public ActionResult Index()
{
var key = typeof(Member).FullName;
var member = this.HttpContext.Items[key];
return View(member);
}
}
System.Net.Http.HttpRequestMessage.Properties
- HttpRequestMessage.Properties["key"] 是 IDictionary<string,object> 型別
- 適用 ASP.NET Web API
/ ASP.NET Core Web API
在 ActionFilterAttribute 注入 Member 物件至 HttpRequestMessage.Properties["key"]
public class InjectionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
//初始化物件
var member = new Member {Id = Guid.NewGuid(), Name = Faker.Name.FullName()};
var key = member.GetType().FullName;
//注入到 HttpRequestMessage
filterContext.Request.Properties[key] = member;
}
}
在 ApiController 取出 Member 物件
public class DefaultController : ApiController
{
// GET: api/Default
public IHttpActionResult Get()
{
var key = typeof(Member).FullName;
var member = this.Request.Properties[key] as Member;
return this.Ok(member);
}
}
Microsoft.Owin.IOwinContext.Environment
- IOwinContext.Environment 是 IDictionary<string,object> 型別
- IOwinContext.Set/Get 骨子裡面是控制 IOwinContext.Environment
- 適用 OWIN
app.Use Middleware 注入 Member 物件至 IOwinContext.Environment["key"]
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new {id = RouteParameter.Optional}
);
app.Use(async (owinContext, next) =>
{
var member = new Member
{
Id = Guid.NewGuid(),
Name = Name.FullName()
};
//owinContext.Set(member.GetType().FullName, member);
owinContext.Environment[member.GetType().FullName] = member;
await next();
});
app.UseErrorPage()
.UseWebApi(config);
}
}
在 ApiController ,用 IOwinContext.Get 取出 Member 物件
public class DefaultController : ApiController
{
// GET: api/Default
public IHttpActionResult Get()
{
var key = typeof(Member).FullName;
var member = this.Request.GetOwinContext().Get<Member>(key);
return this.Ok(member);
}
}
Microsoft.AspNetCore.Http.HttpContext.Items
- HttpContext.Items 是 IDictionary<object,object> 型別
- 適用 ASP.NET Core
app.Use Middleware 注入 Member 物件至 HttpContext.Items
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (owinContext, next) =>
{
var member = new Member
{
Id = Guid.NewGuid(),
Name = Faker.Name.FullName()
};
var key = member.GetType().FullName;
owinContext.Items[key] = member;
await next();
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
}
在 Controller 用 HttpContext.Items 取出 Member
[ApiController]
[Route("api/[controller]")]
public class DefaultController : ControllerBase
{
[HttpGet]
public async Task<ActionResult<Member>> Get()
{
var key = typeof(Member).FullName;
var member = this.HttpContext.Items[key] as Member;
return this.Ok(member);
}
}
範例位置
https://github.com/yaochangyu/sample.dotblog/tree/master/Lab.AspNetPassParameter
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET