
Skill
migrating-mvc-httpcontext
migrate ASP.NET HttpContext to Core
Description
Migrates ASP.NET Framework HttpContext, Request, and Response usage to ASP.NET Core equivalents. Use when projects reference HttpContext.Current, HttpRequest.InputStream, HttpResponse.Write, HttpServerUtility, Request.ServerVariables, Request.Files, Response.End, Response.AddHeader, or ClaimsPrincipal.Current. Also triggers for "replace HttpContext.Current", "inject IHttpContextAccessor", "async response writing", "request body reading", or when assessment signals include UsesHttpContextCurrent, UsesHttpContext, UsesResponseWrite, UsesRequestInputStream, UsesCustomHeaders.
SKILL.md
ASP.NET MVC HttpContext and Request/Response Migration
Overview
Migrate HttpContext.Current, HttpRequest, HttpResponse, and HttpServerUtility usage from ASP.NET Framework to ASP.NET Core. The static HttpContext.Current accessor is removed in Core — all context access flows through dependency injection or controller base class properties. Response writing is async-only in Kestrel, and request reading APIs have changed types and behavior.
Adapter precedence: If the
aspnet-system-web-adaptersskill is loaded, its guidance takes precedence over HttpContext.Current, Request Access, Response Access, and ClaimsPrincipal.Current sections during scaffold and migrate task phases.
Workflow
Track progress across these steps:
Migration Progress:
- [ ] Step 1: Inventory HttpContext usage patterns
- [ ] Step 2: Eliminate HttpContext.Current
- [ ] Step 3: Migrate request access patterns
- [ ] Step 4: Migrate response access patterns
- [ ] Step 5: Replace HttpServerUtility calls
- [ ] Step 6: Migrate ClaimsPrincipal.Current
- [ ] Step 7: Remove legacy references
Step 1: Inventory HttpContext Usage Patterns
Scan the project for all HttpContext-related usage:
HttpContext.Current— static access in controllers, services, libraries, and helpersHttpContext.Current.Request/.Response— direct request/response property accessHttpContext.Current.Server—HttpServerUtilitycalls (MapPath,HtmlEncode,UrlEncode)HttpContext.Current.User/ClaimsPrincipal.Current— identity accessHttpContext.Current.Items— per-request storageRequest.InputStream,Request.Files,Request.ServerVariables— changed APIsResponse.Write(),Response.End(),Response.AddHeader()— removed or changed methods
Categorize each usage site by location: controller, service/library, middleware, or static helper. Location determines the migration strategy.
Step 2: Eliminate HttpContext.Current
HttpContext.Current does not exist in ASP.NET Core. The replacement depends on where the access occurs.
In controllers — use the HttpContext property directly:
Before:
public class ProductController : Controller
{
public ActionResult Index()
{
var userId = HttpContext.Current.User.Identity.Name;
HttpContext.Current.Items["LastAccess"] = DateTime.UtcNow;
return View();
}
}
After:
public class ProductController : Controller
{
public IActionResult Index()
{
var userId = HttpContext.User.Identity?.Name;
HttpContext.Items["LastAccess"] = DateTime.UtcNow;
return View();
}
}
In services and libraries — inject IHttpContextAccessor:
Before:
public class AuditService
{
public string GetCurrentUser()
{
return HttpContext.Current.User.Identity.Name;
}
}
After:
public class AuditService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public AuditService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string GetCurrentUser()
{
return _httpContextAccessor.HttpContext?.User.Identity?.Name ?? string.Empty;
}
}
Register the accessor in Program.cs:
builder.Services.AddHttpContextAccessor();
Threading concern:
IHttpContextAccessorusesAsyncLocal<T>. In singleton services, always access.HttpContextat call time — never cache theHttpContextreference in a field. A cached reference may belong to a completed request and produce null-reference or cross-request data leaks.
In static helpers or libraries with no DI — this requires an architectural change. Pass HttpContext (or the specific value needed) as a method parameter from the call site. Avoid wrapping IHttpContextAccessor in a static accessor because it reintroduces the same coupling.
Step 3: Migrate Request Access Patterns
Request APIs changed types and behavior between Framework and Core:
QueryString — NameValueCollection → IQueryCollection:
Before:
string page = Request.QueryString["page"];
After:
string page = Request.Query["page"].ToString();
IQueryCollection indexer returns StringValues; call .ToString() or use .FirstOrDefault() for a single value.
Form data — async reading required:
Before:
string name = Request.Form["name"];
After:
var form = await Request.ReadFormAsync();
string name = form["name"].ToString();
In controller actions, prefer model binding ([FromForm] attribute) over manual form reading.
Request body (InputStream) — Stream → HttpRequest.Body:
Before:
using (var reader = new StreamReader(Request.InputStream))
{
string body = reader.ReadToEnd();
}
After:
using var reader = new StreamReader(Request.Body);
string body = await reader.ReadToEndAsync();
Enable request buffering if the body must be read multiple times:
Request.EnableBuffering();
File uploads — HttpFileCollection → IFormFileCollection:
Before:
HttpPostedFileBase file = Request.Files["upload"];
file.SaveAs(Server.MapPath("~/uploads/" + file.FileName));
After:
IFormFile file = Request.Form.Files["upload"];
using var stream = new FileStream(Path.Combine(uploadPath, file.FileName), FileMode.Create);
await file.CopyToAsync(stream);
ServerVariables — no direct equivalent. Map to specific properties:
Request.ServerVariables[key] | ASP.NET Core Equivalent |
|---|---|
REMOTE_ADDR | HttpContext.Connection.RemoteIpAddress |
SERVER_NAME | Request.Host.Host |
SERVER_PORT | Request.Host.Port |
HTTP_HOST | Request.Headers["Host"] |
PATH_INFO | Request.Path |
QUERY_STRING | Request.QueryString.Value |
CONTENT_TYPE | Request.ContentType |
CONTENT_LENGTH | Request.ContentLength |
Step 4: Migrate Response Access Patterns
Response.Write() — sync writing is not supported by Kestrel:
Before:
Response.Write("<html><body>Hello</body></html>");
Response.Write(jsonString);
After:
await Response.WriteAsync("<html><body>Hello</body></html>");
await Response.WriteAsync(jsonString);
In controller actions, prefer returning IActionResult (e.g., Content(), Json(), View()) instead of writing directly to the response stream.
Response.End() — removed, no direct equivalent:
Before:
Response.Write(data);
Response.End();
After — in middleware, return without calling next(). In controllers, return an IActionResult:
await Response.WriteAsync(data);
return; // In middleware — do not call next()
To forcefully abort a connection (rare), use HttpContext.Abort().
Response.AddHeader() / AppendHeader() — use Headers.Append():
Before:
Response.AddHeader("X-Custom-Header", "value");
Response.AppendHeader("Cache-Control", "no-cache");
After:
Response.Headers.Append("X-Custom-Header", "value");
Response.Headers.Append("Cache-Control", "no-cache");
Headers must be set before writing to the response body. In Framework this was loosely enforced; in Core, writing headers after the response has started throws an InvalidOperationException.
Response.Redirect() — compatible but behavior differs:
Before:
Response.Redirect("~/home"); // Redirect + Response.End()
Response.Redirect("~/home", false); // Redirect without ending
After:
Response.Redirect("/home"); // Sets 302, does NOT abort
return; // Must explicitly return
Core's Redirect does not end the response or call Response.End(). Always return from the action or middleware after calling Redirect() to prevent further processing.
Response buffering — Core does not buffer responses by default. For scenarios requiring buffering (e.g., setting headers after partial writes), use Response.StartAsync() awareness or enable response buffering middleware.
Step 5: Replace HttpServerUtility Calls
HttpContext.Current.Server (HttpServerUtility) has no single replacement in Core. Replace method by method:
Server. Method | ASP.NET Core Replacement |
|---|---|
Server.MapPath("~/path") | IWebHostEnvironment.ContentRootPath or .WebRootPath + Path.Combine() |
Server.HtmlEncode(s) | System.Net.WebUtility.HtmlEncode(s) or HtmlEncoder.Default.Encode(s) |
Server.HtmlDecode(s) | System.Net.WebUtility.HtmlDecode(s) |
Server.UrlEncode(s) | System.Net.WebUtility.UrlEncode(s) or Uri.EscapeDataString(s) |
Server.UrlDecode(s) | System.Net.WebUtility.UrlDecode(s) or Uri.UnescapeDataString(s) |
Server.Transfer(url) | No equivalent — rewrite as redirect or internal rewrite middleware |
Server.Execute(url) | No equivalent — refactor to call the target logic directly |
Server.GetLastError() | IExceptionHandlerFeature in exception handler middleware |
Server.ScriptTimeout | RequestTimeoutOptions or CancellationToken with timeout |
For MapPath, inject IWebHostEnvironment:
Before:
string path = HttpContext.Current.Server.MapPath("~/Content/data.json");
After:
public class DataService
{
private readonly IWebHostEnvironment _env;
public DataService(IWebHostEnvironment env)
{
_env = env;
}
public string GetDataPath()
{
return Path.Combine(_env.WebRootPath, "Content", "data.json");
}
}
Step 6: Migrate ClaimsPrincipal.Current
ClaimsPrincipal.Current is a static thread-based accessor that does not work reliably in ASP.NET Core's async pipeline.
In controllers — use the User property:
Before:
var claims = ClaimsPrincipal.Current.Claims;
After:
var claims = User.Claims;
In services — inject IHttpContextAccessor:
Before:
public class PermissionService
{
public bool HasRole(string role)
{
return ClaimsPrincipal.Current.IsInRole(role);
}
}
After:
public class PermissionService
{
private readonly IHttpContextAccessor _accessor;
public PermissionService(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public bool HasRole(string role)
{
return _accessor.HttpContext?.User.IsInRole(role) ?? false;
}
}
Custom ClaimsPrincipal factories that set ClaimsPrincipal.Current should be replaced with claims transformation. Register an IClaimsTransformation implementation:
builder.Services.AddTransient<IClaimsTransformation, CustomClaimsTransformation>();
Step 7: Remove Legacy References
Remove all legacy HttpContext artifacts:
- Remove
using System.Webstatements that are no longer needed - Remove any custom
HttpContextBase/HttpContextWrapperabstractions - Remove
HttpContext.Currentshim or facade classes - Search for remaining references to
HttpContext.Current,ClaimsPrincipal.Current,HttpServerUtility,Request.ServerVariables,Response.Write((sync overload), andResponse.End()
Success Criteria
- No references to
HttpContext.Currentremain in the project IHttpContextAccessorregistered and injected where services needHttpContext- No singleton services cache
HttpContextreferences in fields - All
Request.InputStreamreads converted to asyncRequest.Bodyreads - All
Response.Write()calls converted toawait Response.WriteAsync()orIActionResult Response.End()calls removed and replaced with return statements orHttpContext.Abort()HttpServerUtilitycalls replaced with Core equivalentsClaimsPrincipal.Currentreplaced withUserproperty orIHttpContextAccessor- No
Request.ServerVariablesaccess remains - Headers set before writing to response body
- Project builds without errors
More skills from the upgrade-agent-plugins repository
View all 19 skillsconverting-to-cpm
migrate .NET projects to Central Package Management
Jul 18.NETMicrosoftMigrationcreating-winforms-custom-controls
create custom WinForms controls
Jul 3.NETWindowsWinUImanaging-winforms-high-dpi-layout
design high-DPI responsive WinForms layouts
Jul 18DesignDesktopUI ComponentsWindowsmanaging-winforms-mvvm
implement MVVM pattern in WinForms
Jul 18.NETWindowsWinUImigrating-aspnet-framework-to-core
migrate ASP.NET Framework to Core
Jul 3.NETASP.NET CoreBackendMigrationmigrating-documentdb-to-cosmos
migrate DocumentDB to Cosmos DB
Jul 3AzureCosmos DBDatabaseMigration
More from Microsoft
View publisherplaywright-trace
inspect Playwright trace files
playwright
Apr 6DebuggingPlaywrightTestingrushstack-best-practices
manage Rush monorepos with best practices
rushstack
Apr 6EngineeringLocal DevelopmentMicrosoftProject Management +1azure-ai-agents-persistent-dotnet
build AI agents with Azure .NET SDK
skills
Jul 3.NETAgentsAzureLLMazure-ai-anomalydetector-java
build anomaly detection applications with Java
skills
May 13AnalyticsAzureData AnalysisJava +2azure-ai-contentsafety-java
build content moderation applications with Azure AI
skills
Jul 7AI InfrastructureAzureJavaSecurityazure-ai-contentsafety-py
detect harmful content with Azure AI Content Safety
skills
Jul 18AzureComplianceLLMMicrosoft +2