简体   繁体   English

MVC 4 RedirectToAction看不到自定义标题

[英]MVC 4 RedirectToAction does not see Custom Header

If you start a new Web Project, and create a new MVC4 application (with sub-kind as "WebApi", you can paste the below code in (overwriting HomeController.cs) to get the code to work. 如果您启动一个新的Web项目并创建一个新的MVC4应用程序(子类型为“ WebApi”),则可以将以下代码粘贴到(覆盖HomeController.cs)中,以使该代码起作用。

I have a MVC4 application (with WebApi). 我有一个MVC4应用程序(带有WebApi)。

I am trying to set a custom-header in a MVC controller method and then do a RedirectToAction. 我试图在MVC控制器方法中设置自定义标头,然后执行RedirectToAction。 The custom-header is not seen in the second mvc-controller-method. 在第二个mvc-controller-method中看不到自定义标头。

I am able to set a cookie in the first mvc-controller-method and see it in the second mvc-controller-method (after a RedirectToAction). 我能够在第一个mvc-controller-method中设置一个cookie ,并在第二个mvc-controller-method中(在RedirectToAction之后)看到它。

Is there a way to see the custom-header I set in the second mvc-controller-method after a RedirectToAction ? 有没有办法查看我在RedirectToAction之后的第二个mvc-controller-method中设置的自定义标头?

Thanks. 谢谢。

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

Response headers are never copied automatically to requests - so setting any custom headers on response will not impact next request issued to handle 302 redirect. 响应标头永远不会自动复制到请求-因此,在响应上设置任何自定义标头都不会影响为处理302重定向而发出的下一个请求。

Note that it is the case even with cookies: response comes with "set this cookie" header, and all subsequent request will get "current cookies" header. 请注意,即使使用cookie也是如此:响应带有“设置此cookie”标头,所有后续请求都将获得“当前cookie”标头。

If you have your own client you may be able to handle 302 manually (not possible if you are using browser as client). 如果您有自己的客户端,则可以手动处理302(如果将浏览器用作客户端,则不可能)。

As another answer stated, response headers are about this response, not the next one. 如另一个答案所述,响应头是关于响应的,而不是下一个响应。 Redirecting is not a server-side action. 重定向不是服务器端的操作。 A redirect instructs the client to perform a completely new request, and of course in a new request, the response headers for the old request are not present. 重定向指示客户端执行全新请求,当然,在新请求中,不存在旧请求的响应标头。 So return RedirectToAction("MyHomeControllerAlternateActionResult"); 因此, return RedirectToAction("MyHomeControllerAlternateActionResult"); is guaranteed to not have this response's headers when the browser initiates the new request. 当浏览器启动新请求时,可以确保没有此响应的标头。

In trying to solve this problem, one might think of trying to persist the data to the next request server-side, such as through a cookie or in an explicit session variable, or implicitly via use of ViewBag/ViewData/TempData. 在尝试解决此问题时,可能会想到尝试将数据持久化到下一个请求服务器端,例如通过cookie或在显式会话变量中,或通过使用ViewBag / ViewData / TempData隐式地。 However, I don't recommend this as using session state heavily has performance implications in large/high-usage web sites, plus there are other negative and subtle side-effects that you may run into down the road. 但是,我不建议这样做,因为在大型/高使用率的网站中大量使用会话状态会对性能产生影响,此外,您还可能会遇到其他负面和微妙的副作用。 For example, if a person has two browser windows open to the same web site, they can't be doing different actions reliably, as the session data for one window can end up being served to the other one. 例如,如果一个人有两个浏览器窗口打开到同一个网站,则他们不能可靠地执行不同的操作,因为一个窗口的会话数据最终可能会被提供给另一个窗口。 Avoid session usage as much as possible in your web site design—I promise this will benefit you down the road. 在您的网站设计中尽可能避免使用会话-我保证这将使您受益。

A slightly better way, though still with its problems, is to redirect to a URL with querystring parameters containing a payload. 一种更好的方法(尽管仍然存在问题)是使用包含有效负载的querystring参数重定向到URL。 And, instead of the whole set of data, you can provide a key that can be pulled from the session (as long as it's also bound to their IP address and is large like a GUID or two together). 而且,您可以提供一个可以从会话中拉出的密钥,而不是整个数据集(只要它也绑定到其IP地址并且很大,就像一个GUID或两个GUID一样)。 However, relying on session state is still not ideal as stated before. 但是,如上所述,依靠会话状态仍然不是理想的选择。

Instead, consider using server-side redirection such as child actions. 相反,请考虑使用服务器端重定向,例如子操作。 If you find that hard because what you want to call is a main controller you have a few options: 如果由于要调用的是主控制器而感到困难,则可以选择以下几种方法:

  1. If you're using dependency injection, add a parameter to the current controller (saving it from the constructor and using it in the request method) that is the desired controller you want to "redirect" to. 如果您正在使用依赖项注入,请向当前控制器添加一个参数(将其从构造函数中保存并在request方法中使用),它是您要“重定向”到的所需控制器。 You can then call that controller directly. 然后,您可以直接调用该控制器。 This may not be ideal (as all calls to this controller also have to new up a copy of that one), but it does work. 这可能并不理想(因为对该控制器的所有调用也必须重新创建该控制器的一个副本),但是它确实起作用。 Trying to new up the other controller manually can also work, but for reasons I don't fully remember, I think this can give some additional problems. 尝试手动更新另一个控制器也可以,但是由于我不完全记得的原因,我认为这会带来一些其他问题。 In any case, this method can give issues accessing the HttpRequest context and other context objects correctly, though this can be worked around. 在任何情况下,尽管可以解决此问题,但该方法可能会给正确访问HttpRequest上下文和其他上下文对象带来问题。

  2. Rearchitect your application so that controllers are not the place where full pages are rendered. 重新设计您的应用程序,以便控制器不是呈现整个页面的地方。 Instead, use them as "smart routers" that call child actions to perform the real work. 而是将它们用作“智能路由器”,以调用子操作来执行实际工作。 Then, you can call the same child actions from any controller. 然后,您可以从任何控制器调用相同的子动作。 But this still has problems. 但这仍然有问题。

  3. Perhaps the best way is to add custom routing logic through action filters or other means (search the web!) so that the correct controller is hit in the first place! 也许最好的方法是通过动作过滤器或其他方式(搜索网络!)添加自定义路由逻辑,以便首先找到正确的控制器! This may not always be possible, but sometimes the need to redirect to another controller mid-procedure actually points to a larger design problem. 这可能并不总是可能的,但是有时在中间过程中需要重定向到另一个控制器实际上会指出更大的设计问题。 Focusing on how to cause the knowledge of which controller to hit to be available earlier in the pipeline (such as during routing) can reveal architecture problems, and can reveal likely solutions to them. 集中精力于如何使有关哪个控制器被命中的知识在管道中较早(例如在路由过程中)可用可以揭示体系结构问题,并可以揭示可能的解决方案。

There may be other options that I haven't thought of, but at least you have a few alternatives to the simple "no way to do that." 我可能没有想到其他选择,但是至少您可以使用一些简单的“没有办法做到”的选择。

I was able to do something similar like what the user is requesting in the following (rudimentary) way: 我可以通过以下(基本)方式执行类似用户要求的操作:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM