RedirectToAction with Non-English Characters in Parameters and Authentication Causes Error #7529
Description
By default Asp.Net Core encodes non-english characters in view.
Let's create a very simple controller:
public class HomeController : Controller
{
public IActionResult Index(Parameters param)
{
return View(param);
}
public IActionResult ToIndex(Parameters param)
{
return RedirectToAction("Index", param);
}
public class Parameters
{
public String Name { get; set; }
}
}
and a very simple View, which greets a user:
@model TestNonEngLetters.Controllers.HomeController.Parameters
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
Hello, @Model.Name!
</body>
</html>
If Name
contains non-english characters (e.g. Jörn) it gets encoded:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
Hello, Jörn!
</body>
</html>
If we don't want this kind of encoding there is a way to turn it off. We need to add this line of code to Startup.cs
:
services.Configure<WebEncoderOptions>(options => options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All));
Now our html becomes pretty good:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
Hello, Jörn!
</body>
</html>
Our Startup.cs
looks like this:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
services.AddMvc();
services.Configure<WebEncoderOptions>(options => options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Let's invoke our ToIndex
action, that must redirect us back to Index
:
public IActionResult ToIndex(Parameters param)
{
return RedirectToAction("Index", param);
}
So we invoke page http://localhost:2616/Home/ToIndex?Name=Jörn
and receive expected 302 response with location=/?Name=J%C3%B6rn
http header.
Everything works just fine... until we add authentication to our project. See the commented line in Startup.cs
?
//services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
Let's uncomment it and invoke our ToIndex
action again.
And what we get?
InvalidOperationException: Invalid non-ASCII or control character in header: 0x00F6
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameHeaders.ThrowInvalidHeaderCharacter(char ch)
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameHeaders.ValidateHeaderCharacters(string headerCharacters)
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameHeaders.ValidateHeaderCharacters(StringValues headerValues)
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameResponseHeaders.SetValueFast(string key, StringValues value)
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(string key, StringValues value)
Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.Redirect(string location, bool permanent)
Microsoft.AspNetCore.Mvc.Internal.RedirectToActionResultExecutor.Execute(ActionContext context, RedirectToActionResult result)
Microsoft.AspNetCore.Mvc.RedirectToActionResult.ExecuteResult(ActionContext context)
Microsoft.AspNetCore.Mvc.ActionResult.ExecuteResultAsync(ActionContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeResultAsync>d__19.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResultFilterAsync>d__24.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResourceFilter>d__22.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeFilterPipelineAsync>d__17.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeAsync>d__15.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Builder.RouterMiddleware+<Invoke>d__4.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()
it looks like now, when authentication module is in place, asp.net core is not able to set location
header correctly.
So, maybe we have chosen the wrong way to deal with encoding of non-english characters or is it a bug in framework itself?