2015-10-06 5 views
1

Если вы запустите новый веб-проект и создаете новое приложение MVC4 (с подтипом как «WebApi», вы можете вставить код ниже (переписывание HomeController.cs), чтобы получить код для работы.MVC 4 RedirectToAction не видит пользовательский заголовок

у меня есть приложение MVC4 (с WebAPI).

Я пытаюсь установить пользовательский-заголовок в методе контроллера MVC, а затем сделать RedirectToAction. обычай заголовка не видел во втором методе mvc-контроллера

Я могу установить файл cookie в первом контроллере mvc-controller- метод и увидеть его во втором методе mvc-controller (после RedirectToAction).

Есть ли способ увидеть пользовательский заголовок, который я установил во втором методе mvc-controller после 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. */ 
     } 
    } 
} 

ответ

4

заголовки ответа никогда не копируются автоматически на запросы - поэтому установка каких-либо пользовательских заголовков на ответ не будет влиять на следующий запрос, выданный для обработки 302 редиректа.

Обратите внимание, что это дело даже с кукисами: ответ приходит с заголовком «set this cookie», и все последующие запросы получат заголовок «current cookies».

Если у вас есть собственный клиент, вы можете обрабатывать 302 вручную (это невозможно, если вы используете браузер в качестве клиента).

4

Как еще один ответ, ответные заголовки - это этот ответ, а не следующий. Перенаправление не является действием на стороне сервера. Переадресация поручает клиенту выполнить совершенно новый запрос и, конечно, в новом запросе, заголовки ответов для старого запроса отсутствуют. Таким образом, у return RedirectToAction("MyHomeControllerAlternateActionResult"); не будет заголовков этого ответа, когда браузер инициирует новый запрос.

При попытке решить эту проблему можно подумать о попытке сохранить данные на следующей стороне запроса, например, через куки-файл или в явную переменную сеанса или неявно с помощью ViewBag/ViewData/TempData , Тем не менее, я не рекомендую это, так как использование состояния сеанса сильно влияет на производительность на больших/высокопроизводительных веб-сайтах, плюс есть другие негативные и тонкие побочные эффекты, которые вы можете столкнуться в будущем. Например, если у человека есть два окна браузера, открытые для одного и того же веб-сайта, они не могут надежно выполнять разные действия, так как данные сеанса для одного окна могут быть переданы другому. Избегайте использования сеанса в максимально возможной степени в дизайне вашего веб-сайта - я обещаю, что это поможет вам в будущем.

Немного лучший способ, хотя и все еще с его проблемами, заключается в перенаправлении на URL с параметрами querystring, содержащими полезную нагрузку. И вместо всего набора данных вы можете предоставить ключ, который можно извлечь из сеанса (если он также связан с их IP-адресом и большой, как GUID или два вместе). Однако, полагаясь на состояние сеанса, все еще не идеально, как указано ранее.

Вместо этого рассмотрите возможность перенаправления на стороне сервера, например дочерние действия.Если вы обнаружите, что трудно, потому что вы хотите назвать это главным контроллер у вас есть несколько вариантов:

  1. Если вы используете инъекции зависимостей, добавить параметр регулятор тока (спасая его от конструкторы и используя его в методе запроса), который является желаемым контроллером, к которому вы хотите «перенаправить». Вы можете напрямую вызвать этот контроллер. Это может быть не идеальным (так как все вызовы этого контроллера также должны обновить копию этого), но это делает работы. Пытаться к новому контроллеру вручную также может работать, но по причинам, которые я не полностью помню, я думаю, что это может привести к дополнительным проблемам. В любом случае этот метод может дать проблемы с доступом к контексту HttpRequest и другим контекстным объектам правильно, хотя это может быть обработано вокруг.

  2. Rearchitect your application, чтобы контроллеры не были местом, где отображаются полные страницы. Вместо этого используйте их как «интеллектуальные маршрутизаторы», которые вызывают дочерние действия для выполнения реальной работы. Затем вы можете вызвать те же детские действия от любого контроллера. Но это все еще имеет проблемы.

  3. Возможно, лучший способ - добавить пользовательскую логику маршрутизации через фильтры действий или другие средства (выполнить поиск в Интернете!), Чтобы в первую очередь попал правильный контроллер! Это может быть не всегда возможно, но иногда необходимость перенаправления на другую среднюю процедуру контроллера фактически указывает на большую проблему с дизайном. Сосредоточение внимания на том, как вызвать знание того, какой контроллер попадет в доступный ранее в конвейере (например, во время маршрутизации), может выявить проблемы архитектуры и может выявить вероятные решения для них.

Возможны другие варианты, о которых я не думал, но по крайней мере у вас есть несколько альтернатив простому «никакому способу сделать это».

Смежные вопросы