MVC 4 RedirectToAction没有看到自定义标题

时间:2015-10-06 15:50:23

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

如果您启动一个新的Web项目,并创建一个新的MVC4应用程序(子类为" WebApi",您可以将以下代码粘贴(覆盖HomeController.cs)以获取代码工作

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

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

我可以在第一个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. */
        }
    }
}

3 个答案:

答案 0 :(得分:4)

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

请注意,即使使用Cookie也是如此:响应附带&#34;设置此Cookie&#34;标题,以及所有后续请求将获得&#34;当前cookie&#34;报头中。

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

答案 1 :(得分:4)

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

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

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

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

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

  2. 重新构建您的应用程序,以便控制器不是呈现完整页面的位置。相反,将它们用作&#34;智能路由器&#34;调用子操作来执行实际工作。然后,您可以从任何控制器调用相同的子操作。但这仍有问题。

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

  4. 我可能还有其他一些选择,但至少你可以选择一些简单的方法,而且无法做到这一点。&#34;

答案 2 :(得分:0)

我可以通过以下(基本)方式执行类似用户要求的操作: