MVC 4 RedirectToAction没有看到自定义报头

本文关键字:自定义 报头 RedirectToAction MVC | 更新日期: 2023-09-27 18:16:21

如果你启动一个新的Web项目,并创建一个新的MVC4应用程序(子类型为"WebApi"),你可以粘贴下面的代码(覆盖homeconcontroller .cs),以使代码工作。

我有一个MVC4应用程序(与WebApi)。

我试图在MVC控制器方法中设置一个自定义头,然后做一个RedirectToAction。在第二个mvc-controller-方法中看不到自定义头。

我可以在第一个mvc-controller-method中设置一个cookie,并在第二个mvc-controller-method中看到它(在RedirectToAction之后)。

是否有一种方法可以看到我在第二个mvc-controller-method中设置的自定义头RedirectToAction ?

谢谢。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace MyMvc4WebApiProjectNamespace.Controllers
{
    public class HomeController : Controller
    {
        private const string CustomCookieName = "CustomCookieName";
        private const string CustomHeaderName = "X-CustomHeaderName";
        private const string IISExpressRootUrl = "http://localhost:55937/"; /* open up the project properties and go to the web tab and find the iis-express area to get the correct value for your environment */
        public ActionResult Index()
        {
            IEnumerable<string> webApiValues = null;
            string value1 = null;
            string value2 = null;
            HttpClientHandler handler = new HttpClientHandler
            {
                UseDefaultCredentials = true,
                PreAuthenticate = true
            };

            using (var client = new HttpClient(handler))
            {
                string valuesUri = IISExpressRootUrl + "api/Values";
                webApiValues = client
                            .GetAsync(valuesUri)
                            .Result
                            .Content.ReadAsAsync<IEnumerable<string>>().Result;
                if (null != webApiValues)
                {
                    value1 = webApiValues.ElementAt(0);
                    value2 = webApiValues.ElementAt(1);
                }
                else
                {
                    throw new ArgumentOutOfRangeException("WebApi call failed");
                }
            }

            HttpCookie customCookie = new HttpCookie(CustomCookieName, "CustomCookieValue_ThisShowsUpIn_MyHomeControllerAlternateActionResult_Method");
            Response.Cookies.Add(customCookie);
            HttpContext.Response.AppendHeader(CustomHeaderName, "CustomHeaderValue_This_Does_Not_Show_Up_In_MyHomeControllerAlternateActionResult_Method");
            //Response.AppendHeader(CustomHeaderName, value2);
            return RedirectToAction("MyHomeControllerAlternateActionResult");
        }
        public ActionResult MyHomeControllerAlternateActionResult()
        {
            IEnumerable<string> webApiReturnValues = null;

            CookieContainer cookieContainer = new CookieContainer();
            foreach (string cookiename in Request.Cookies)
            {
                if (cookiename.Equals(CustomCookieName, StringComparison.OrdinalIgnoreCase))
                {
                    var cookie = Request.Cookies[cookiename];
                    cookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, "localhost"));
                }
            }
            if (cookieContainer.Count < 1)
            {
                throw new ArgumentOutOfRangeException("CookieContainer did not find the cookie I was looking for");
            }
            else
            {
                Console.WriteLine("This is what actually happens.  It finds the cookie.");
            }
            HttpClientHandler handler = new HttpClientHandler
            {
                UseCookies = true,
                UseDefaultCredentials = true,
                PreAuthenticate = true,
                CookieContainer = cookieContainer
            };

            using (var client = new HttpClient(handler))
            {
                bool customHeaderWasFound = false;
                if (null != this.Request.Headers)
                {
                    if (null != this.Request.Headers[CustomHeaderName])
                    {
                        IEnumerable<string> headerValues = this.Request.Headers.GetValues(CustomHeaderName);
                        client.DefaultRequestHeaders.Add(CustomHeaderName, headerValues);
                        customHeaderWasFound = true;
                    }
                }
                /*I wouldn't expect it to be in the below, but I looked for it just in case */
                if (null != this.Response.Headers)//
                {
                    if (null != this.Response.Headers[CustomHeaderName])
                    {
                        IEnumerable<string> headerValues = this.Response.Headers.GetValues(CustomHeaderName);
                        client.DefaultRequestHeaders.Add(CustomHeaderName, headerValues);
                        customHeaderWasFound = true;
                    }
                }
                if (!customHeaderWasFound)
                {
                    Console.WriteLine("This is what actually happens.  No custom-header found.  :(     ");
                }
                string valuesUri = IISExpressRootUrl + "api/Values";
                webApiReturnValues = client
                            .GetAsync(valuesUri)
                            .Result
                            .Content.ReadAsAsync<IEnumerable<string>>().Result;
                if (null == webApiReturnValues)
                {
                    throw new ArgumentOutOfRangeException("WebApi call failed");
                }
            }
            return View(); /* this will throw a "The view 'MyHomeControllerAlternateActionResult' or its master was not found or no view engine supports the searched locations" error, but that's not the point of this demo. */
        }
    }
}

MVC 4 RedirectToAction没有看到自定义报头

响应头永远不会自动复制到请求中-因此在响应中设置任何自定义头不会影响下一个处理302重定向的请求。

请注意,即使是cookie也是如此:响应带有"set this cookie"报头,所有后续请求将获得"current cookies"报头。

如果你有自己的客户端,你可以手动处理302(如果你使用浏览器作为客户端是不可能的)。

正如另一个答案所述,响应头是关于这个响应的,而不是下一个。重定向不是服务器端操作。重定向指示客户端执行一个全新的请求,当然,在新请求中,旧请求的响应头不存在。因此,当浏览器发起新请求时,return RedirectToAction("MyHomeControllerAlternateActionResult");保证没有此响应的标头。

在尝试解决这个问题时,可能会考虑尝试将数据持久化到下一个请求服务器端,例如通过cookie或显式会话变量,或通过使用ViewBag/ViewData/TempData隐式地。然而,我不建议这样做,因为在大型/高使用率的网站中大量使用会话状态会影响性能,而且还有其他负面和微妙的副作用,您可能会遇到。例如,如果一个人有两个浏览器窗口打开到同一个网站,他们就不能可靠地执行不同的操作,因为一个窗口的会话数据最终可能会被提供给另一个窗口。在你的网站设计中尽量避免使用会话,我保证这对你将来的发展是有益的。

一种稍微好一点的方法(尽管仍然存在问题)是重定向到带有querystring参数的URL,其中包含有效负载。而且,您可以提供一个可以从会话中提取的密钥,而不是整个数据集(只要它也绑定到它们的IP地址,并且像GUID或两个一样大)。然而,依赖会话状态仍然不像前面所说的那样理想。

可以考虑使用服务器端重定向,比如子操作。如果你觉得很难,因为你想调用的是一个主控制器,你有几个选择:

  1. 如果你使用依赖注入,给当前控制器添加一个参数(从构造函数中保存并在request方法中使用),这是你想要"重定向"到的控制器。然后你可以直接调用那个控制器。这可能不是理想的(因为对该控制器的所有调用都必须创建该控制器的副本),但它确实可以工作。尝试手动更新其他控制器也可以工作,但由于我不完全记得的原因,我认为这可能会带来一些额外的问题。在任何情况下,这个方法都可以正确访问HttpRequest上下文和其他上下文对象,尽管这个可以被解决。

  2. 重新构建您的应用程序,使控制器不是呈现完整页面的地方。相反,将它们用作调用子操作来执行实际工作的"智能路由器"。然后,您可以从任意控制器调用相同的子操作。

  3. 也许最好的方法是通过动作过滤器或其他方式(搜索web!)添加自定义路由逻辑,以便首先击中正确的控制器!这可能并不总是可能的,但有时需要在过程中间重定向到另一个控制器实际上指出了一个更大的设计问题。关注如何在管道中更早地(例如在路由过程中)提供要命中哪个控制器的知识,可以揭示架构问题,并可以揭示可能的解决方案。

可能还有我没有想到的其他选择,但至少你有一些简单的"没有办法这样做"的选择。

我能够以以下(基本)方式做类似于用户请求的事情:

  • 在重定向中,添加自定义查询字符串参数
  • 创建一个自定义模块,检查该参数并附加自定义头(阅读http://dotnetlionet.blogspot.com/2015/06/how-to-add-httpmodule-in-mvc5.html关于如何做自己的模块)通过这种方式,我可以让我的自定义标题被拾取