0

I am using RedirectToAction method of the base API controller. The first controller needs to modify the request headers (which I seem to be able to do) but the change is not retained.

This is a simplified implementation of what I am trying to do.

   [HttpGet]
        public IActionResult Get(string str)
    {
      // The request comes with a request header with key "Authorization" and value "ABC"

     HttpContext.Request.Headers.Remove("Authorization");
     HttpContext.Request.Headers.Add("Authorization", "XYZ");
        return RedirectToAction("B");
    }

    [HttpGet]
    public IActionResult B()
    {

        var value = HttpContext.Request.Headers.First(x => x.Key == "Authorization");   // I want this to be ""XYZ" , but it remains "ABC"
        return Ok();
    }

Any ideas on how I can get the second Action to use the Request header I updated in the first Action.

Edit:

All our Controllers / Actions are Authenticated using Core middleware JWT Auth Policy Only Action one allows Anonymous access. Action one is called from an internal related solution with a "Code" (Which is used to create a JWT token) . All other Actions require the JWT for Authentication.

The "Code" and JWT Token have independent expires and also hold some data.

The info in the "Code" is deemed more up to date. Action one can be called with both a "Code" and a JWT token.

The redirect was my way of flushing out old JWT token.

When action one is called with a valid "Code" a new JWT is created. I then want to redirect to a controller that will validate the JWT token (using middleware).

I know I can do this manually, but trying to trigger core middleware.

jps
  • 20,041
  • 15
  • 75
  • 79
SANM2009
  • 1,918
  • 2
  • 12
  • 30
  • 1
    You need it change the response header.... Not the request headers... – Chetan Jul 25 '19 at 13:17
  • That is not what I require. The response of Action B needs to be returned, but the first Action needs to make some changes based on what the request came with. – SANM2009 Jul 25 '19 at 13:28
  • This is not possible. HTTP is a stateless protocol, and as such, each request is independent. A redirect is just returning a 301, 302, or 307 with a `Location` header. That's it. The request-response cycle is complete. It is up to the client, then to make a new request to the URL in the `Location` header, which web browsers just happen to do automatically. However, that's a brand new request, which doesn't know or care that a redirect happened or even that there's ever been communication with this server before. – Chris Pratt Jul 25 '19 at 15:02

1 Answers1

1

RedirectToAction returns a 302 response to the browser, which tells the browser to start a new request to the new URL. So your code in B() is hit after the browser initiated a whole new request. So the Request object you're looking at in B() is completely different than the one you modified in Get().

That's clearly not what you want to do. Instead of redirecting, just return B():

[HttpGet]
public IActionResult Get(string str)
{
    // The request comes with a request header with key "Authorization" and value "ABC"

    HttpContext.Request.Headers.Remove("Authorization");
    HttpContext.Request.Headers.Add("Authorization", "XYZ");
    return B();
}

But something does feel wrong about rewriting the request headers. It's kind of like rewriting history. Another way is to use HttpContext.Items to store a value:

[HttpGet]
public IActionResult Get(string str)
{
    HttpContext.Items["Authorization"] = "XYZ";
    return B();
}

[HttpGet]
public IActionResult B()
{
    var value = HttpContext.Items["Authorization"] as string ?? HttpContext.Request.Headers.First(x => x.Key == "Authorization");
    return Ok();
}

The values in HttpContext.Items exist for the life of a single request.

If you must redirect, then you can use TempData to hold data until it's read in the next request:

[HttpGet]
public IActionResult Get(string str)
{
    TempData["Authorization"] = "XYZ";
    return RedirectToAction("B");
}

[HttpGet]
public IActionResult B()
{
    if (TempData.Contains("Authorization")) {
        HttpContext.Request.Headers.Remove("Authorization");
        HttpContext.Request.Headers.Add("Authorization", TempData["Authorization"]);
    }
    var value = HttpContext.Request.Headers.First(x => x.Key == "Authorization");
    return Ok();
}
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Both suggestions are good and I almost thought I have cracked it but there are issues with both. If I don't use redirect then the action method doesn't execute like a controller action (i.e. it is a authenticated method but without the redirecttoaction it is accessible without authentication) – SANM2009 Jul 25 '19 at 13:55
  • As for the second option, the header needs to be in the request, because I am using core middleware to authenticate a JWT token, which core looks for in the autherization header. – SANM2009 Jul 25 '19 at 13:56
  • I added another option to my answer, where you can store data for the user across requests. Still doesn't seem ideal. I feel like there's a better way to do what you're trying to do - but I don't know why you're trying to do this, so I can't say. – Gabriel Luci Jul 25 '19 at 14:04
  • I'll update the question. I didn't want to maker the question too complex, but it seems my requirement is getting complex. – SANM2009 Jul 25 '19 at 14:10
  • You still could use the `TempData` method, but you would need to move it further up the pipeline, before the authorization middleware fires (like in another, custom middleware that you add before authorization). See [here](https://stackoverflow.com/a/45337366/1202807) for how to access `TempData` in middleware. – Gabriel Luci Jul 25 '19 at 14:58
  • Otherwise, I think you have to involve the client code. It has to be aware that it's getting a new token and send it with a new request. – Gabriel Luci Jul 25 '19 at 14:59
  • Thanks, I have accepted your answers because tempdata option would work, however I have decided not to use the approach, because it would make the code too complex. – SANM2009 Aug 06 '19 at 22:16