42

I need to convert Microsoft.AspNetCore.Http.HttpRequest from an AspNetCore context to an HttpRequestMessage to pass to an HttpClient. Is there a simple way of achieve this? Or any hint to implement this would be very helpful.

Update I want to convert the request into a message but I want to change the target url, I want just to redirect the request into another server.

yosbel
  • 1,711
  • 4
  • 20
  • 32
  • That seems like a strange question. Can you explain what you're trying to accomplish? Do you need to extract some info from the current request before passing it to another api? – Métoule Aug 18 '17 at 15:37
  • 1
    The thing is that we have a TestServer in AspNetCore hosted in the application, we want to redirect some requests made to the Application to the internal TestServer. To achieve this the only way we have found is to implement a middle-ware to get the Request from the Context and pass it to the HttpClient proxy from the TestServer. We also would like to achieve the opposite: convert the HttpRespondeMessage to an HttpResponse. – yosbel Aug 18 '17 at 16:02
  • This does not really make muchs sense. The `HttpRequest` you receive on your ASP.NET Core app is targeted at that specific host. If you were to request the same object with a HttpClient, you would just hit your own application again. What exactly are you trying to achieve? Do you want to pass on data to a different server, or what are you trying to do? – poke Aug 19 '17 at 10:37
  • @poke Exactly that, I want to make the same request, but yes, changing the target url, just passing the same request to another target, like a proxy in certain way. – yosbel Aug 21 '17 at 14:13
  • I've had the a similar problem between .NET 4.5. HttpRequestMessage and HttpWebRequest and the approach I used was to just wire up all the properties between the objects, which, in theory, should work just fine. – Daniel Leach Dec 08 '17 at 13:17
  • Have you seen the Proxy middleware that does this? https://github.com/aspnet/Proxy – Tratcher Jan 28 '18 at 04:14
  • @Tratcher The solution I found was inspired on that middleware. – yosbel Mar 01 '18 at 22:59

3 Answers3

44

Try Web API Compatibility Shim

HttpRequestMessageFeature hreqmf = new HttpRequestMessageFeature(httpRequest.HttpContext);
HttpRequestMessage httpRequestMessage = hreqmf.HttpRequestMessage;

Or you could get inspired by Microsoft.AspNetCore.Proxy

These extensions to httpContext may come in handy.

Gustavo Mori
  • 8,319
  • 3
  • 38
  • 52
16

I ended up doing this:

public static class RequestTranscriptHelpers
{
    public static HttpRequestMessage ToHttpRequestMessage(this HttpRequest req)
        => new HttpRequestMessage()
            .SetMethod(req)
            .SetAbsoluteUri(req)
            .SetHeaders(req)
            .SetContent(req)
            .SetContentType(req);

    private static HttpRequestMessage SetAbsoluteUri(this HttpRequestMessage msg, HttpRequest req)
        => msg.Set(m => m.RequestUri = new UriBuilder
        {
            Scheme = req.Scheme,
            Host = req.Host.Host,
            Port = req.Host.Port.Value,
            Path = req.PathBase.Add(req.Path),
            Query = req.QueryString.ToString()
        }.Uri);

    private static HttpRequestMessage SetMethod(this HttpRequestMessage msg, HttpRequest req)
        => msg.Set(m => m.Method = new HttpMethod(req.Method));

    private static HttpRequestMessage SetHeaders(this HttpRequestMessage msg, HttpRequest req)
        => req.Headers.Aggregate(msg, (acc, h) => acc.Set(m => m.Headers.TryAddWithoutValidation(h.Key, h.Value.AsEnumerable())));

    private static HttpRequestMessage SetContent(this HttpRequestMessage msg, HttpRequest req)
        => msg.Set(m => m.Content = new StreamContent(req.Body));

    private static HttpRequestMessage SetContentType(this HttpRequestMessage msg, HttpRequest req)
        => msg.Set(m => m.Content.Headers.Add("Content-Type", req.ContentType), applyIf: req.Headers.ContainsKey("Content-Type"));

    private static HttpRequestMessage Set(this HttpRequestMessage msg, Action<HttpRequestMessage> config, bool applyIf = true)
    {
        if (applyIf)
        {
            config.Invoke(msg);
        }

        return msg;
    }
}



  public static class ResponseTranscriptHelpers
    {
        public static async Task FromHttpResponseMessage(this HttpResponse resp, HttpResponseMessage msg)
        {
            resp.SetStatusCode(msg)
                .SetHeaders(msg)
                .SetContentType(msg);

            await resp.SetBodyAsync(msg);
        }

        private static HttpResponse SetStatusCode(this HttpResponse resp, HttpResponseMessage msg)
            => resp.Set(r => r.StatusCode = (int)msg.StatusCode);

        private static HttpResponse SetHeaders(this HttpResponse resp, HttpResponseMessage msg)
            => msg.Headers.Aggregate(resp, (acc, h) => acc.Set(r => r.Headers[h.Key] = new StringValues(h.Value.ToArray())));

        private static async Task<HttpResponse> SetBodyAsync(this HttpResponse resp, HttpResponseMessage msg)
        {
            using (var stream = await msg.Content.ReadAsStreamAsync())
            using (var reader = new StreamReader(stream))
            {
                var content = await reader.ReadToEndAsync();

                return resp.Set(async r => await r.WriteAsync(content));
            }
        }

        private static HttpResponse SetContentType(this HttpResponse resp, HttpResponseMessage msg)
            => resp.Set(r => r.ContentType = msg.Content.Headers.GetValues("Content-Type").Single(), applyIf: msg.Content.Headers.Contains("Content-Type"));

        private static HttpResponse Set(this HttpResponse msg, Action<HttpResponse> config, bool applyIf = true)
        {
            if (applyIf)
            {
                config.Invoke(msg);
            }

            return msg;
        }
    }

I haven't tried @Strenkor En'Triel response since I found this solution first, but I will try it.

yosbel
  • 1,711
  • 4
  • 20
  • 32
  • 3
    I feel the coder of this snippet was trying to obfuscate the code as much as possible! I have never seen a simple code written in such complicated way. Please check how other people at Microsoft simply write it: https://github.com/aspnet/Proxy/blob/master/src/Microsoft.AspNetCore.Proxy/ProxyAdvancedExtensions.cs – Mohammad Nikravan Jan 25 '19 at 20:56
  • 3
    What the author has done is break the code up so that it can be used in a variety of ways. This this, in my opinion, beautiful code and reads quite nicely. – Joshcodes Mar 10 '20 at 21:58
1

Both IHttpRequestMessageFeature and Microsoft.AspNetCore.Proxy are discontinued. IHttpRequestMessageFeature has been removed since ASP.NET Core 3.0.

Microsoft now has a new reverse proxy nuget package Microsoft.ReverseProxy based on ASP.NET Core infrastructure. As per their docs, IHttpProxy for direct proxying scenario can:

serve as the core proxy adapter between incoming AspNetCore and outgoing System.Net.Http requests. It handles the mechanics of creating a HttpRequestMessage from a HttpContext, sending it, and relaying the response.

Here is an example.

weichch
  • 9,306
  • 1
  • 13
  • 25