Запрос веб-страницы в c # подмене хоста

Мне нужно создать запрос на веб-страницу, которая будет доставляться на наши веб-сайты, но мне нужно также иметь возможность установить информацию заголовка узла. Я пробовал это с помощью HttpWebRequest, но информация заголовка только для чтения (или, по крайней мере, часть узла). Мне нужно сделать это, потому что мы хотим выполнить начальный запрос страницы, прежде чем пользователь сможет. У нас есть 10 веб-серверов с балансировкой нагрузки, поэтому нам нужно запросить файл с каждого из веб-серверов.

Я пробовал следующее:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Headers.Set("Host", "www.mywebsite.com");
WebResponse response = request.GetResponse();

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

11.12.2008 11:27:57
Этот вопрос очень тесно связан с stackoverflow.com/questions/323264/http-request-bypass-dns-net Может быть, кто-то должен закрыть его как дубликат?
Martin Brown 11.12.2008 11:55:57
9 ОТВЕТОВ

Хорошо, небольшое исследование обнаруживает это:

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=384456

Кажется, MS может что-то сделать с этим в какой-то момент.

0
27.08.2010 14:14:26
Я получаю исключение "ArgumentException: заголовок 'Host' не может быть изменен напрямую. Имя параметра: name". Я предполагаю, что он доступен только для чтения в классе
Xetius 11.12.2008 11:38:17
Разве не лучше было бы создать новый ответ, чем восстановить его, и полностью переписать старый?
Martin Brown 11.12.2008 13:04:42
да, возможно - неясно, что по поводу этикета здесь (кто-нибудь указывает?), но я упал на сторону сохранения истории, а не начинать заново. Я действительно задаюсь вопросом, должен ли я был сохранить неудачный ответ, чтобы наглядно показать, что он не работает.
annakata 11.12.2008 15:03:15
Проблема в том, что комментарии и голоса, которые все еще связаны с этим ответом, больше не имеют никакого смысла. Таким образом, по моему мнению, новый ответ был бы лучше.
Martin Brown 11.12.2008 15:41:49

Заголовок «Host» защищен и не может быть изменен программно. Я полагаю, что для обхода этой проблемы вы можете попытаться выполнить привязку через отражение к частному свойству InnerCollection объекта WebRequest и вызвав для него метод «Set» ar «Add», чтобы изменить заголовок Host. Я не пробовал этого, но после быстрого просмотра исходного кода в Reflector, я думаю, что это легко сделать. Но да, привязка к частным свойствам каркасных объектов не лучший способ для достижения цели. :) Используйте только если вы ДОЛЖНЫ.

редактировать: или, как другой парень упоминает в связанном вопросе, просто откройте сокет и выполните быстрый «GET» вручную. Это должно быть легко, если вам не нужно возиться с другими вещами, такими как куки или что-то еще, что предоставляет HttpWebRequest.

0
11.12.2008 11:58:01
РЕШЕНИЕ

Мне удалось найти более длинный маршрут с использованием сокетов. Я нашел ответ на странице MSDN для IPEndPoint:

string getString = "GET /path/mypage.htm HTTP/1.1\r\nHost: www.mysite.mobi\r\nConnection: Close\r\n\r\n";
Encoding ASCII = Encoding.ASCII;
Byte[] byteGetString = ASCII.GetBytes(getString);
Byte[] receiveByte = new Byte[256];
Socket socket = null;
String strPage = null;
try
{
    IPEndPoint ip = new IPEndPoint(IPAddress.Parse("10.23.1.93"), 80);
    socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(ip);
}
catch (SocketException ex)
{
    Console.WriteLine("Source:" + ex.Source);
    Console.WriteLine("Message:" + ex.Message);
}
socket.Send(byteGetString, byteGetString.Length, 0);
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);

while (bytes > 0)
{
    bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
    strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
}
socket.Close();
9
11.12.2008 13:18:19

Я знаю, что это старо, но я столкнулся с той же самой проблемой, и я нашел лучшее решение, чем использование сокетов или отражения ...

Что я сделал, так это создал новый класс, который отличается от WebHeaderCollection и обходит проверку того, что вы в него помещаете:

public class MyHeaderCollection:WebHeaderCollection
{
    public new void Set(string name, string value)
    {
        AddWithoutValidate(name, value);
    }
    //or
    public new string this[string name]
    {
        get { return base[name]; }
        set { AddWithoutValidate(name, value); }
    }
}

и вот как вы это используете:

    var http = WebRequest.Create("http://example.com/");
    var headers = new MyHeaderCollection();
    http.Headers = headers;

    //Now you can add/override anything you like without validation:
    headers.Set("Host", http.RequestUri.Host);
    //or
    headers["Host"] = http.RequestUri.Host;

Надеюсь, это поможет любому, кто ищет это!

2
15.02.2010 08:21:36
Это не работает в 3.5SP1 - коллекция, переданная в заголовки, не сохраняется, она копируется в новую коллекцию WebHeaderCollection, поэтому все заголовки проходят повторную проверку.
nitzmahone 3.06.2010 23:28:35

У меня была проблема, когда у URL DNS, который я использовал, было несколько разных IP-адресов, я хотел вызывать каждый адрес отдельно, используя одно и то же имя DNS в хосте - решение использует прокси:

string retVal = "";
            // Can't change the 'Host' header property because .NET protects it
            // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            // request.Headers.Set(HttpRequestHeader.Host, DEPLOYER_HOST);
            // so we must use a workaround
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Proxy = new WebProxy(ip);
            using (WebResponse response = request.GetResponse())
            {
                using (TextReader reader = new StreamReader(response.GetResponseStream()))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                        retVal += line;
                }
            }
            return retVal;

Заголовок хоста автоматически устанавливается из «url» .NET, а «ip» содержит фактический адрес веб-сервера, с которым вы хотите связаться (вы также можете использовать имя DNS здесь)

4
12.12.2010 11:02:09
Очевидно, что это не сработает, когда нам действительно нужно использовать прокси :)
dr. evil 13.12.2010 16:55:50

Хотя это очень поздний ответ, может быть, кто-то может получить выгоду от него

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://192.168.1.1"));
request.Headers.GetType().InvokeMember("ChangeInternal", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, request.Headers, new object[] {"Host","www.mysite.com"});

Отражение твой друг :)

11
26.09.2011 19:33:06
LB - это круто, я новичок в C # и не знаю, что за отражение и как сработал твой ответ для меня
xameeramir 26.05.2014 06:04:26
Примечание: это изменит хост, но будет перезаписано значением в URL, на который вы отправляете запрос.
user6038265 21.12.2016 07:59:03

Вы можете использовать мое решение для этой проблемы, оно размещено здесь:

Как установить пользовательский заголовок «Host» в HttpWebRequest?

Это может помочь вам отредактировать заголовок хоста и избежать использования прокси и прямых запросов сокетов.

0
23.05.2017 12:34:25

Я знаю, что это старый вопрос, но в наши дни вы можете сделать.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.1.5/filename.htm");
request.Host = "www.mywebstite.com";
WebResponse response = request.GetResponse();
2
18.06.2015 08:18:51

Necromancing.
Для тех, кто все еще на .NET 2.0,
на самом деле это довольно легко, если вы знаете, как.

Проблема в том, что вы не можете установить заголовок хоста, потому что среда не позволит вам изменить значение во время выполнения. (.net Framework 4.0+ позволит вам переопределить хост в httpwebrequest).

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

Если dns-name не существует, что, честно говоря, единственный случай, когда вы хотите сделать это в первую очередь, вы не можете установить его, потому что .NET не может разрешить его, и вы не можете переопределить .NET DNS resolver.

Но вы можете настроить веб-прокси с тем же IP-адресом, что и у сервера назначения.

Итак, если ваш сервер IP 28.14.88.71:

public class myweb : System.Net.WebClient
{
    protected override System.Net.WebRequest GetWebRequest(System.Uri address)
    {
        System.Net.WebRequest request = (System.Net.WebRequest)base.GetWebRequest(address);
        //string host = "redmine.nonexistantdomain.com";

        //request.Headers.GetType().InvokeMember("ChangeInternal",
        //    System.Reflection.BindingFlags.NonPublic |
        //    System.Reflection.BindingFlags.Instance |
        //    System.Reflection.BindingFlags.InvokeMethod, null,
        //    request.Headers, new object[] { "Host", host }
        //);

        //server IP and port
        request.Proxy = new System.Net.WebProxy("http://28.14.88.71:80");

        // .NET 4.0 only
        System.Net.HttpWebRequest foo = (System.Net.HttpWebRequest)request;
        //foo.Host = host;

        // The below reflection-based operation is not necessary, 
        // if the server speaks HTTP 1.1 correctly
        // and the firewall doesn't interfere
        // https://yoursunny.com/t/2009/HttpWebRequest-IP/
        System.Reflection.FieldInfo horribleProxyServicePoint = (typeof(System.Net.ServicePoint))
            .GetField("m_ProxyServicePoint", System.Reflection.BindingFlags.NonPublic |
            System.Reflection.BindingFlags.Instance);

        horribleProxyServicePoint.SetValue(foo.ServicePoint, false);
        return foo; // or return request; if you don't neet this
    }


    }

и вуаля, сейчас

myweb wc = new myweb();
string str = wc.DownloadString("http://redmine.netexistantdomain.com");

и вы получите правильную страницу обратно, если 28.14.88.71 - это веб-сервер с виртуальным хостингом на основе имен (на основе http-host-header).

Теперь у вас есть правильный ответ на оригинальный вопрос, как для WebRequest, так и для WebClient. Я думаю, что использование пользовательских сокетов для этого было бы неправильным подходом, особенно когда нужно использовать SSL, и когда реальное решение так просто ...

0
21.12.2016 08:09:05