简体   繁体   English

c# HttpClient 带代理

[英]c# HttpClient with proxy

I execute a lot of request to some the resources with HttpClient .我使用HttpClient对一些资源执行了很多请求。 To avoid licks I use it as single instance.为了避免舔我将它用作单个实例。 Something like that... I want to use proxy, so how I can use different proxies for each request?类似的东西......我想使用代理,那么我如何为每个请求使用不同的代理?

Thanks!谢谢!

public class Program
{
    private static HttpClient Client = new HttpClient();
    public static void Main(string[] args)
    {
        Console.WriteLine("Starting connections");
        for(int i = 0; i<10; i++)
        {
            var result = Client.GetAsync("http://aspnetmonsters.com").Result;
            Console.WriteLine(result.StatusCode);
        }
        Console.WriteLine("Connections done");
        Console.ReadLine();
    }

}

You will need to implement IWebProxy.您将需要实现 IWebProxy。

here is a very row sample.这是一个非常行的示例。

First implement IWebProxy首先实现 IWebProxy

public class MyProxy : IWebProxy {
public MyProxy() {  credentials = new NetworkCredential( user, password ); }
private NetworkCredential credentials;
public ICredentials Credentials
{
    get = > credentials;
    set = > throw new NotImplementedException();
}
private Uri proxyUri;
public Uri GetProxy( Uri destination )
{
    return proxyUri; // your proxy Uri
}
public bool IsBypassed( Uri host )
{
    return false;
}
private const string user = "yourusername";
private const string password = "password";}

Then provide it to handler in HttpClient然后将其提供给 HttpClient 中的处理程序

public class MyHttpClient {
internal static HttpResult httpMethod( ... )
{
    var _client = client();
    try
    {
        var message = new HttpRequestMessage( method, url );
        message.Content = new StringContent( content, Encoding.UTF8, "application/json" );
        var result = _client.SendAsync( message ).Result;// handle result
    }
    catch( Exception e ){}
}
private static HttpClient client()
{
    var httpClientHandler = new HttpClientHandler() { Proxy = new MyProxy() };
    var httpClient = new MyClient( new Uri( "baseurl" ), httpClientHandler );
    return httpClient;

Ah, I misread the question.啊,我看错了这个问题。
It's not about how to use a random IWebProxy with HttpClientHandler, but how to work around being unable to reset the proxy property of the same HttpClientHandler once the first request has started.这不是关于如何将随机 IWebProxy 与 HttpClientHandler 一起使用,而是如何解决在第一个请求开始后无法重置同一 HttpClientHandler 的代理属性的问题。

The problem is that you can't reset the proxy of the HttpClientHandler...问题是你不能重置HttpClientHandler的代理...

System.InvalidOperationException: 'This instance has already started one or more requests. System.InvalidOperationException: '这个实例已经开始了一个或多个请求。
Properties can only be modified before sending the first request.'只能在发送第一个请求之前修改属性。

But it's still rather easy.但这仍然相当容易。

  • Proxy property of HttpClientHandler takes an object that implements IWebProxy. HttpClientHandler 的 Proxy 属性接受一个实现 IWebProxy 的对象。
  • IWebProxy interface has a method GetProxy that return the Uri of the proxy. IWebProxy 接口有一个 GetProxy 方法,它返回代理的 Uri。
  • So you can make your own class that implements this interface and control how it returns the Uri of the proxy with GetProxy.因此,您可以创建自己的类来实现该接口,并使用 GetProxy 控制它如何返回代理的 Uri。
  • You can make it wrap another IWebProxy, and in GetProxy it would return GetProxy of the inner IWebProxy.您可以让它包装另一个 IWebProxy,并在 GetProxy 中返回内部 IWebProxy 的 GetProxy。
  • This way, you won't have to change the Proxy property of the HttpClientHandler, you can just change the inner IWebProxy.这样,您不必更改 HttpClientHandler 的 Proxy 属性,只需更改内部 IWebProxy。

Implementation:执行:

public class WebProxyService
  : System.Net.IWebProxy
{
    protected System.Net.IWebProxy m_proxy;


    public System.Net.IWebProxy Proxy
    {
        get { return this.m_proxy ??= System.Net.WebRequest.DefaultWebProxy; }
        set { this.m_proxy = value; }
    }

    System.Net.ICredentials System.Net.IWebProxy.Credentials
    {
        get { return this.Proxy.Credentials; }
        set { this.Proxy.Credentials = value; }
    }


    public WebProxyService()
    { } // Constructor 

    public WebProxyService(System.Net.IWebProxy proxy)
    {
        this.Proxy = proxy;
    } // Constructor 


    System.Uri System.Net.IWebProxy.GetProxy(System.Uri destination)
    {
        return this.Proxy.GetProxy(destination);
    }

    bool System.Net.IWebProxy.IsBypassed(System.Uri host)
    {
        return this.Proxy.IsBypassed(host);
    }


}

And then usage goes like this:然后用法是这样的:

public class AlternatingProxy 
{


    public static async System.Threading.Tasks.Task Test()
    {
        string url = "http://aspnetmonsters.com";
       
        System.Net.WebProxy[] proxies = new[] {
            null,
            new System.Net.WebProxy("104.238.172.20", 8080),
            new System.Net.WebProxy("104.238.167.193", 8080),
            new System.Net.WebProxy("136.244.102.38", 8080),
            new System.Net.WebProxy("95.179.202.40", 8080)
        };

        System.Random rnd = new System.Random();
        WebProxyService proxyService = new WebProxyService();
        
        using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient(
          new System.Net.Http.HttpClientHandler { UseProxy = true, Proxy = proxyService }
          ))
        {
            // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
            hc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148");
            hc.DefaultRequestHeaders.Add("Accept-Language", "fr-FR, fr;q=0.9, en;q=0.8, it;q=0.7, *;q=0.5");
            hc.DefaultRequestHeaders.Add("Referer", "https://www.baidu.com");
            hc.DefaultRequestHeaders.ConnectionClose = true; 

            for (int i = 0; i < 10; ++i)
            {
                proxyService.Proxy = proxies[rnd.Next(proxies.Length)];
                string response = await hc.GetStringAsync(url);
            }
        }

    } // End Task Test  


} // End Class TestMe 

Edit:编辑:

If you want to use a singleton, maybe this is an idea:如果您想使用单例,也许这是一个想法:

public class WebProxyService : System.Net.IWebProxy {公共类 WebProxyService : System.Net.IWebProxy {

    protected System.Net.IWebProxy m_proxy;


    public System.Net.IWebProxy Proxy
    {
        get { return this.m_proxy ??= System.Net.WebRequest.DefaultWebProxy; }
        set { this.m_proxy = value; }
    }

    System.Net.ICredentials System.Net.IWebProxy.Credentials
    {
        get { return this.Proxy.Credentials; }
        set { this.Proxy.Credentials = value; }
    }


    public WebProxyService()
    { } // Constructor 


    public WebProxyService(System.Net.IWebProxy proxy)
    {
        this.Proxy = proxy;
    } // Constructor 


    protected System.Func<System.Net.WebProxy>[] proxies = new System.Func<System.Net.WebProxy>[] {
            delegate(){ return new System.Net.WebProxy("104.238.172.20", 8080); },
            delegate (){ return new System.Net.WebProxy("104.238.167.193", 8080);},
            delegate(){ return new System.Net.WebProxy("136.244.102.38", 8080);},
            delegate(){ return new System.Net.WebProxy("95.179.202.40", 8080);}
        };


    System.Uri System.Net.IWebProxy.GetProxy(System.Uri destination)
    {
        return proxies[RandomGen2.Next(proxies.Length)]().GetProxy(destination);
    }


    bool System.Net.IWebProxy.IsBypassed(System.Uri host)
    {
        return this.Proxy.IsBypassed(host);
    }



    private static class RandomGen2
    {
        private static System.Random _global = new System.Random();

        [System.ThreadStatic]
        private static System.Random _local;

        public static int Next(int maxValue)
        {
            System.Random inst = _local;
            if (inst == null)
            {
                int seed;
                lock (_global) seed = _global.Next();
                _local = inst = new System.Random(seed);
            }
            return inst.Next(maxValue);
        }
    }


} // End Class WebProxyService 

Edit 2:编辑2:

It's still not thread-safe if you change the proxy.如果您更改代理,它仍然不是线程安全的。
Therefore, using a fixed immutable proxy-list and blocking the set-property.因此,使用固定的不可变代理列表并阻止 set-property。
This way, it should be thread-safe.这样,它应该是线程安全的。

public class WebProxyService
      : System.Net.IWebProxy
{


    protected System.Net.IWebProxy[] m_proxyList;

    public System.Net.IWebProxy Proxy
    {
        get
        {
            // https://devblogs.microsoft.com/pfxteam/getting-random-numbers-in-a-thread-safe-way/
            if (this.m_proxyList != null)
                return this.m_proxyList[ThreadSafeRandom.Next(this.m_proxyList.Length)];
            
            return System.Net.WebRequest.DefaultWebProxy;
        }
        set
        {
            throw new System.InvalidOperationException("It is not thread-safe to change the proxy-list.");
        }
    }

    System.Net.ICredentials System.Net.IWebProxy.Credentials
    {
        get { return this.Proxy.Credentials; }
        set { this.Proxy.Credentials = value; }
    }


    public WebProxyService()
    {
    } // Constructor 


    public WebProxyService(System.Net.IWebProxy[] proxyList)
    {
        this.m_proxyList = proxyList;
    } // Constructor 


    System.Uri System.Net.IWebProxy.GetProxy(System.Uri destination)
    {
        return this.Proxy.GetProxy(destination);
    }


    bool System.Net.IWebProxy.IsBypassed(System.Uri host)
    {
        return this.Proxy.IsBypassed(host);
    }


} // End Class WebProxyService 

Old answer: ---- 老答案:----

Using a proxy with HttpClient in ASP.NET-Core is actually quite simple.在 ASP.NET-Core 中使用带有 HttpClient 的代理实际上非常简单。
All you need to do is set the handler in the HttpClient-constructor.您需要做的就是在 HttpClient 构造函数中设置处理程序。
Then set the proxy-property of the handler for each request.然后为每个请求设置处理程序的代理属性。
Like this:像这样:

 public class Program { public static async System.Threading.Tasks.Task Main(string[] args) { string url = "http://aspnetmonsters.com"; System.Net.WebProxy[] proxies = new[] { null, new System.Net.WebProxy("104.238.172.20", 8080), new System.Net.WebProxy("104.238.167.193", 8080), new System.Net.WebProxy("136.244.102.38", 8080), new System.Net.WebProxy("95.179.202.40", 8080) }; System.Random rnd = new System.Random(); using (System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler() { Proxy = new System.Net.WebProxy("http://127.0.0.1:8888"), UseProxy = true, }) { using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient(handler)) { System.Console.WriteLine("Starting connections"); for (int i = 0; i < 10; i++) { handler.Proxy = proxies[rnd.Next(proxies.Length)]; await hc.GetAsync(url); // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent hc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"); hc.DefaultRequestHeaders.Add("Accept-Language", "fr-FR, fr;q=0.9, en;q=0.8, it;q=0.7, *;q=0.5"); hc.DefaultRequestHeaders.Add("Referer", "https://www.baidu.com"); using (System.Net.Http.HttpResponseMessage response = await hc.GetAsync(url)) { // using (var fs = new System.IO.MemoryStream()) // { await response.Content.CopyToAsync(fs); } byte[] ba = await response.Content.ReadAsByteArrayAsync(); } // End Using response } // Next i System.Console.WriteLine("Ending connections"); } // End Using hc } // End Using handler System.Console.WriteLine("--- Press any key to continue --- "); System.Console.ReadKey(); } // End Task Main } // End Class Program

So basically to be able to change proxies you will need a reference on the HttpClientHandler .因此,基本上为了能够更改代理,您需要在HttpClientHandler上进行参考。
A simple example can be found here: C# use proxy with HttpClient request一个简单的例子可以在这里找到: C# use proxy with HttpClient request
and another one here: Simple C# .NET 4.5 HTTPClient Request Using Basic Auth and Proxy另一个在这里: Simple C# .NET 4.5 HTTPClient Request Using Basic Auth and Proxy

I would suggest to keep the HttpClientHandler on a private field and use the reference to change the proxy everytime you need it.我建议将HttpClientHandler保留在私有字段上,并在每次需要时使用引用来更改代理。
Keep in mind though that if you need to use different proxies simultaneously you would need to have multiple instances of the HttpClientHandler class.但请记住,如果您需要同时使用不同的代理,您将需要拥有HttpClientHandler类的多个实例。

If you need me to make a sample code for that.如果您需要我为此制作示例代码。 Ping me.平我。

Thank you.谢谢你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM