Click here to Skip to main content
15,884,388 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Dear readers,

For a project I need to expose a third party API partly to the public. To do this I thought I could easily get in incoming request, reroute it to the API, get response back and deliver response to the client. Pretty simple concept in theory, but somehow problematic. Partly the proxy works but when I access the proxy via browser some data look the same while others look differently (json instead of XML) and some I can't even display (gzip chunked).

Please help (sounds really desperate and I am),
Rémy Samulski

What I have tried:

I have build a webform that will take the request, build a new HttpRequestMessage and send it via HttpClient to the API. I receive the HttpResponseMessage, pull out the content (here the GZIP part goes wrong) and send it back through the HttpResponse. The code below is all that is needed to replicate using online OData API. The following URL's seem to work similar in browser through Proxy or directly (in nice XML representation):
/Proxy2/
/Proxy2/$metadata

But these give different results:
/Proxy2/Customers (gives JSON but directly gives XML in plain ASCII, no XML representation)
/Proxy2/Employees (gives error but directly gives XML in plain ASCII)

public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        GlobalConfiguration.Configure(WebApiConfig.Register);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        if ((Request.RawUrl.ToLower() + "/").StartsWith("/proxy2/") == true)
        {
            Context.RewritePath("~/_Proxy2.aspx", false);
        }
    }
}

public partial class _Proxy2 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string url = Request.RawUrl.Replace("/Proxy2", "").TrimStart('/');

        System.Net.Http.HttpRequestMessage httpRequestMessage =
            new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, "https://services.odata.org/V4/Northwind/Northwind.svc/" + url);
        Business.Http.AddHeaders(Request, httpRequestMessage);

        System.Net.Http.HttpResponseMessage httpResponseMessage;
        System.IO.Stream contentStream;
        using (System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient())
        {
            httpResponseMessage = httpClient.SendAsync(httpRequestMessage).Result;
            contentStream = httpResponseMessage.Content.ReadAsStreamAsync().Result;
        }

        Response.Clear();
        Response.ClearHeaders();
        Business.Http.AddHeaders(Response, httpResponseMessage);

        contentStream.CopyTo(Response.OutputStream);
        Response.End();
    }
}

public class Http
{
    public static void AddHeaders(HttpRequest httpRequest, System.Net.Http.HttpRequestMessage httpRequestMessage)
    {
        foreach (string key in httpRequest.Headers.Keys)
        {
            string values = httpRequest.Headers[key];
            System.Diagnostics.Debug.WriteLine("Request " + key + " : " + values);
            switch (key)
            {
                case "Host":
                    //Removed due to SSL problems
                    break;
                default:
                    bool found = false;
                    IEnumerable<string> valuesCurrent;
                    if (httpRequestMessage.Headers.TryGetValues(key, out valuesCurrent) == true)
                    {
                        foreach (var item in valuesCurrent)
                        {
                            if (item.ToLower() == values.ToLower())
                            {
                                found = true;
                                break;
                            }
                        }
                    }
                    if (found == false)
                    {
                        httpRequestMessage.Headers.Add(key, values);
                    }
                    break;
            }
        }
        System.Diagnostics.Debug.WriteLine("");
        Business.Reflection.DebugShowAllProperties(httpRequest);
        System.Diagnostics.Debug.WriteLine("");
        Business.Reflection.DebugShowAllProperties(httpRequestMessage);
        System.Diagnostics.Debug.WriteLine("");
    }

    public static void AddHeaders(HttpResponse httpResponse, System.Net.Http.HttpResponseMessage httpResponseMessage)
    {
        httpResponse.CacheControl = httpResponseMessage.Headers.CacheControl.ToString();

        foreach (KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> item in httpResponseMessage.Headers)
        {
            AddHeaders(httpResponse, item);
        }
        System.Diagnostics.Debug.WriteLine("");
        foreach (KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> item in httpResponseMessage.Content.Headers)
        {
            AddHeaders(httpResponse, item);
        }

        System.Diagnostics.Debug.WriteLine("");
        Business.Reflection.DebugShowAllProperties(httpResponse);
        System.Diagnostics.Debug.WriteLine("");
        Business.Reflection.DebugShowAllProperties(httpResponseMessage);
        System.Diagnostics.Debug.WriteLine("");
    }

    private static void AddHeaders(HttpResponse httpResponse, KeyValuePair<string, System.Collections.Generic.IEnumerable<string>> keyValuePair)
    {
        string key = keyValuePair.Key;
        string values = "";
        foreach (string stringValue in keyValuePair.Value)
        {
            if (values.Contains("," + stringValue) == false)
            {
                values += "," + stringValue;
            }
        }
        values = values.Trim(',');
        System.Diagnostics.Debug.WriteLine("Response " + key + " : " + values);
        switch (key)
        {
            case "Content-Type":
                httpResponse.ContentType = values;
                break;
            default:
                httpResponse.Headers.Add(key, values);
                break;
        }
    }
}

public class Reflection
{
    public static void DebugShowAllProperties(object obj)
    {
        var properties = GetProperties(obj);

        System.Diagnostics.Debug.WriteLine(obj.GetType().FullName);

        foreach (var p in properties)
        {
            string name = p.Name;
            string value = "NULL";
            try
            {
                object valueObject = p.GetValue(obj, null);

                if (valueObject != null)
                {
                    value = valueObject.ToString();
                }
            }
            catch
            {

            }
            System.Diagnostics.Debug.WriteLine(name + " : " + value);
        }

    }

    private static System.Reflection.PropertyInfo[] GetProperties(object obj)
    {
        return obj.GetType().GetProperties();
    }
}
Posted
Updated 18-Feb-19 3:56am

1 solution

Run Postman against your service API and see what it gives you. Try to duplicate exactly what your proxy is generating (look specifically at the HTTP headers). You can verify this in Fiddler. Are you sending content-type, and if so what is it set to? Are you sending an 'accept' header in the request? You should specify what you want back (Json/xml). Test this with postman.
 
Share this answer
 
Comments
LiQuick 19-Feb-19 3:24am    
Hi Derek,
Thx for your suggestions. When I visit my proxy, Chrome wil send the below (I have removed a few to be brief) headers. So yes the browser will send multiple possible content types it can handle.
Request Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Request Accept-Encoding : gzip, deflate, br
Request Accept-Language : nl-NL,nl;q=0.9,en;q=0.8,en-US;q=0.7
Request User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36

I have copied these headers into the HttpRequestMessage so it will mimic the browser request:
System.Net.Http.HttpRequestMessage
Version : 1.1
Content : NULL
Method : GET
RequestUri : https://services.odata.org/V4/Northwind/Northwind.svc/Customers
Headers : Connection: Keep-Alive
Accept: text/html, application/xhtml+xml, application/xml; q=0.9, image/webp, image/apng, */*; q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: nl-NL, nl; q=0.9, en; q=0.8, en-US; q=0.7
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36
Upgrade-Insecure-Requests: 1

So in my point of view their should be no difference between a direct request to the OData service and through my HttpClient. I'll try to sniff out, as you recommended the difference between my proxy and the webbrowser.

Thx
LiQuick 19-Feb-19 4:04am    
PS. Today at work (yesterday I was at home) a part of the data that was different between direct request and proxy request are the same. This makes me more stressed.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900