【问题标题】:MVC 4 RedirectToAction does not see Custom HeaderMVC 4 RedirectToAction 看不到自定义标头
【发布时间】:2015-10-06 15:50:23
【问题描述】:

如果您开始一个新的 Web 项目,并创建一个新的 MVC4 应用程序(子类型为“WebApi”,您可以将以下代码粘贴到(覆盖 HomeController.cs)中以使代码工作。

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

我正在尝试在 MVC 控制器方法中设置自定义标头,然后执行 RedirectToAction。在第二个 mvc-controller-method 中没有看到 custom-header。

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

有没有办法在 RedirectToAction 之后查看我在第二个 mvc-controller-method 中设置的自定义标头?

谢谢。

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. */
        }
    }
}

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-mvc-4 asp.net-web-api


    【解决方案1】:

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

    请注意,即使使用 cookie 也是如此:响应带有“设置此 cookie”标头,所有后续请求都将获得“当前 cookie”标头。

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      2. 重新架构您的应用程序,使控制器不是呈现整个页面的地方。相反,将它们用作调用子操作来执行实际工作的“智能路由器”。然后,您可以从 any 控制器调用相同的子操作。但这仍然有问题。

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

      可能还有其他我没有想到的选择,但至少你有一些替代简单的“没办法做到这一点”。

      【讨论】:

        【解决方案3】:

        我能够以以下(基本)方式执行类似于用户请求的操作:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-11-09
          • 2018-09-26
          • 1970-01-01
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          • 2012-11-09
          相关资源
          最近更新 更多