当前位置: 首页 > news >正文

C#中用HttpWebRequest中发送GET/HTTP/HTTPS请求 (转载)

C#中用HttpWebRequest中发送GET/HTTP/HTTPS请求 (转载)

https://www.cnblogs.com/OpenCoder/p/10384561.html

这个需求来自于我最近练手的一个项目,在项目中我需要将一些自己发表的和收藏整理的网文集中到一个地方存放,如果全部采用手工操作工作量大而且繁琐,因此周公决定利用C#来实现。在很多地方都需要验证用户身份才可以进行下一步操作,这就免不了POST请求来登录,在实际过程中发现有些网站登录是HTTPS形式的,在解决过程中遇到了一些小问题,现在跟大家分享。
通用辅助类
下面是我编写的一个辅助类,在这个类中采用了HttpWebRequest中发送GET/HTTP/HTTPS请求,因为有的时候需要获取认证信息(如Cookie),所以返回的是HttpWebResponse对象,有了返回的HttpWebResponse实例,可以获取登录过程中返回的会话信息,也可以获取响应流。
代码如下:

复制代码

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Net.Security;  
using System.Security.Cryptography.X509Certificates;  
using System.DirectoryServices.Protocols;  
using System.ServiceModel.Security;  
using System.Net;  
using System.IO;  
using System.IO.Compression;  
using System.Text.RegularExpressions;  

namespace BaiduCang  
{  
   /// <summary>  
   /// 有关HTTP请求的辅助类  
   /// </summary>  
   public class HttpWebResponseUtility  
   {  
       private static readonly string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";  
       /// <summary>  
       /// 创建GET方式的HTTP请求  
       /// </summary>  
       /// <param name="url">请求的URL</param>  
       /// <param name="timeout">请求的超时时间</param>  
       /// <param name="userAgent">请求的客户端浏览器信息,可以为空</param>  
       /// <param name="cookies">随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空</param>  
       /// <returns></returns>  
       public static HttpWebResponse CreateGetHttpResponse(string url,int? timeout, string userAgent,CookieCollection cookies)  
       {  
           if (string.IsNullOrEmpty(url))  
           {  
               throw new ArgumentNullException("url");  
           }  
           HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;  
           request.Method = "GET";  
           request.UserAgent = DefaultUserAgent;  
           if (!string.IsNullOrEmpty(userAgent))  
           {  
               request.UserAgent = userAgent;  
           }  
           if (timeout.HasValue)  
           {  
               request.Timeout = timeout.Value;  
           }  
           if (cookies != null)  
           {  
               request.CookieContainer = new CookieContainer();  
               request.CookieContainer.Add(cookies);  
           }  
           return request.GetResponse() as HttpWebResponse;  
       }  
       /// <summary>  
       /// 创建POST方式的HTTP请求  
       /// </summary>  
       /// <param name="url">请求的URL</param>  
       /// <param name="parameters">随同请求POST的参数名称及参数值字典</param>  
       /// <param name="timeout">请求的超时时间</param>  
       /// <param name="userAgent">请求的客户端浏览器信息,可以为空</param>  
       /// <param name="requestEncoding">发送HTTP请求时所用的编码</param>  
       /// <param name="cookies">随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空</param>  
       /// <returns></returns>  
       public static HttpWebResponse CreatePostHttpResponse(string url,IDictionary<string,string> parameters,int? timeout, string userAgent,Encoding requestEncoding,CookieCollection cookies)  
       {  
           if (string.IsNullOrEmpty(url))  
           {  
               throw new ArgumentNullException("url");  
           }  
           if(requestEncoding==null)  
           {  
               throw new ArgumentNullException("requestEncoding");  
           }  
           HttpWebRequest request=null;  
           //如果是发送HTTPS请求  
           if(url.StartsWith("https",StringComparison.OrdinalIgnoreCase))  
           {  
               ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);  
               request = WebRequest.Create(url) as HttpWebRequest;  
               request.ProtocolVersion=HttpVersion.Version10;  
           }  
           else 
           {  
               request = WebRequest.Create(url) as HttpWebRequest;  
           }  
           request.Method = "POST";  
           request.ContentType = "application/x-www-form-urlencoded";  
             
           if (!string.IsNullOrEmpty(userAgent))  
           {  
               request.UserAgent = userAgent;  
           }  
           else 
           {  
               request.UserAgent = DefaultUserAgent;  
           }  

           if (timeout.HasValue)  
           {  
               request.Timeout = timeout.Value;  
           }  
           if (cookies != null)  
           {  
               request.CookieContainer = new CookieContainer();  
               request.CookieContainer.Add(cookies);  
           }  
           //如果需要POST数据  
           if(!(parameters==null||parameters.Count==0))  
           {  
               StringBuilder buffer = new StringBuilder();  
               int i = 0;  
               foreach (string key in parameters.Keys)  
               {  
                   if (i > 0)  
                   {  
                       buffer.AppendFormat("&{0}={1}", key, parameters[key]);  
                   }  
                   else 
                   {  
                       buffer.AppendFormat("{0}={1}", key, parameters[key]);  
                   }  
                   i++;  
               }  
               byte[] data = requestEncoding.GetBytes(buffer.ToString());  
               using (Stream stream = request.GetRequestStream())  
               {  
                   stream.Write(data, 0, data.Length);  
               }  
           }  
           return request.GetResponse() as HttpWebResponse;  
       }  

       private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)  
       {  
           return true; //总是接受  
       }  
   }  
} 

复制代码

 

从上面的代码中可以看出POST数据到HTTP和HTTPS站点不同,POST数据到HTTPS站点的时候需要设置ServicePointManager类的ServerCertificateValidationCallback属性,并且在POST到https://passport.baidu.com/?login时还需要将HttpWebResquest实例的ProtocolVersion属性设置为HttpVersion.Version10(这个未验证是否所有的HTTPS站点都需要设置),否则在调用GetResponse()方法时会抛出“基础连接已经关闭: 连接被意外关闭。”的异常。

此外我们其实可以不用设置ServicePointManager类的ServerCertificateValidationCallback属性,因为ServerCertificateValidationCallback在ServicePointManager类中是个静态属性,设置了它相当于是对全局所有HttpWebRequest实例生效的,这样并不好。我们可以像下面这样针对每一个HttpWebRequest实例,设置ServerCertificateValidationCallback属性,这样才是最佳的做法,下面的示例代码基于.NET Core控制台项目:

复制代码

using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace NetCoreHttpWebRequestTest
{
    class RequestModel
    {
        public string Message
        {
            get;
            set;
        }
    }

    class ResponseModel
    {
        public string Message
        {
            get;
            set;
        }
    }

    class Program
    {
        protected static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true; //总是接受  
        }

        public static string GetResponseJson(string requestJson)
        {
            string url = "https://www.contoso.com";

            HttpWebRequest request = WebRequest.CreateHttp(url);

            //如果url是https协议
            if (url.ToLower().Trim().StartsWith("https"))
            {
                request.ProtocolVersion = HttpVersion.Version10;
                //在每个HttpWebRequest实例上设置ServerCertificateValidationCallback属性
                request.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
            }

            request.ContentType = "application/json; charset=utf-8";
            request.Method = "POST";
            request.Timeout = 120000;//2分钟响应超时
            request.ReadWriteTimeout = 180000;//3分钟下载超时

            using (StreamWriter sw = new StreamWriter(request.GetRequestStream(), Encoding.UTF8))
            {
                sw.Write(requestJson);
            }

            using (var response = request.GetResponse())
            {
                using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    return sr.ReadToEnd();
                }
            }
        }

        static void Main(string[] args)
        {
            RequestModel requestModel = new RequestModel();

            string responseText = GetResponseJson(JsonConvert.SerializeObject(requestModel));
            ResponseModel responseModel = JsonConvert.DeserializeObject<ResponseModel>(responseText);

            Console.WriteLine("Press any key to end...");
            Console.ReadKey();
        }
    }
}

复制代码

 

用法举例
这个类用起来也很简单:
(1)POST数据到HTTPS站点,用它来登录百度:

复制代码

string loginUrl = "https://passport.baidu.com/?login";  
string userName = "userName";  
string password = "password";  
string tagUrl = "http://cang.baidu.com/"+userName+"/tags";  
Encoding encoding = Encoding.GetEncoding("gb2312");  

IDictionary<string, string> parameters = new Dictionary<string, string>();  
parameters.Add("tpl", "fa");  
parameters.Add("tpl_reg", "fa");  
parameters.Add("u", tagUrl);  
parameters.Add("psp_tt", "0");  
parameters.Add("username", userName);  
parameters.Add("password", password);  
parameters.Add("mem_pass", "1");  
HttpWebResponse response = HttpWebResponseUtility.CreatePostHttpResponse(loginUrl, parameters, null, null, encoding, null);  
string cookieString = response.Headers["Set-Cookie"]; 

复制代码

(2)发送GET请求到HTTP站点
在cookieString中包含了服务器端返回的会话信息数据,从中提取了之后可以设置Cookie下次登录时带上这个Cookie就可以以认证用户的信息,假设我们已经登录成功并且获取了Cookie,那么发送GET请求的代码如下:

string userName = "userName";  
string tagUrl = "http://cang.baidu.com/"+userName+"/tags";  
CookieCollection cookies = new CookieCollection();//如何从response.Headers["Set-Cookie"];中获取并设置CookieCollection的代码略  
response = HttpWebResponseUtility.CreateGetHttpResponse(tagUrl, null, null, cookies);  

(3)发送POST请求到HTTP站点
以登录51CTO为例:

复制代码

string loginUrl = "http://home.51cto.com/index.php?s=/Index/doLogin";  
string userName = "userName";  
string password = "password";  

IDictionary<string, string> parameters = new Dictionary<string, string>();  
parameters.Add("email", userName);  
parameters.Add("passwd", password);  

HttpWebResponse response = HttpWebResponseUtility.CreatePostHttpResponse(loginUrl, parameters, null, null, Encoding.UTF8, null);  

复制代码

 

在这里说句题外话,CSDN的登录处理是由http://passport.csdn.net/ajax/accounthandler.ashx这个Handler来处理的。
总结
在本文只是讲解了在C#中发送请求到HTTP和HTTPS的用法,分GET/POST两种方式,为减少一些繁琐和机械的编码,周公将其封装为一个类,发送数据之后返回HttpWebResponse对象实例,利用这个实例我们可以获取服务器端返回的Cookie以便用认证用户的身份继续发送请求,或者读取服务器端响应的内容,不过在读取响应内容时要注意响应格式和编码,本来在这个类中还有读取HTML和WML内容的方法(包括服务器使用压缩方式传输的数据),但限于篇幅和其它方面的原因,此处省略掉了。如有机会,在以后的文章中会继续讲述这方面的内容。

 

 

 

 

 

相关文章:

  • System.Net.HttpWebRequest.GetRequestStream超时问题
  • System.Net.HttpWebRequest.GetResponse() 远程服务器
  • 【转载】HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法
  • UE4 AIController
  • [UE4]创建自定义AIController的方法(C++)
  • eclipse + pydev远程调试OpenStack
  • 调用shell jenkins不能自动结束
  • Unreal 第三方 Python平台
  • 【UE4_C++】<14-3>用户界面 UI和UMG——为UI创建屏幕尺寸自适应缩放
  • 虚幻4DPI自适应缩放规则解析
  • Eclipse中打开windows资源管理器或打开文件夹的设置!(实例)
  • 在Eclipse中快速定位当前文件所在位置
  • AssetBundle详解与优化
  • 苹果电脑远程管理/屏幕共享的客方设置
  • 使用UnrealPak.exe创建Pak文件
  • classpath对获取配置文件的影响
  • EOS是什么
  • Java知识点总结(JavaIO-打印流)
  • Linux下的乱码问题
  • mac修复ab及siege安装
  • Mac转Windows的拯救指南
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • PHP那些事儿
  • Python - 闭包Closure
  • Spring核心 Bean的高级装配
  • vagrant 添加本地 box 安装 laravel homestead
  • 初探 Vue 生命周期和钩子函数
  • 从重复到重用
  • 高程读书笔记 第六章 面向对象程序设计
  • 前嗅ForeSpider教程:创建模板
  • 使用putty远程连接linux
  • 提醒我喝水chrome插件开发指南
  • 微信小程序开发问题汇总
  • 小程序01:wepy框架整合iview webapp UI
  • C# - 为值类型重定义相等性
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • #define,static,const,三种常量的区别
  • #include<初见C语言之指针(5)>
  • #数学建模# 线性规划问题的Matlab求解
  • (第一天)包装对象、作用域、创建对象
  • (六)vue-router+UI组件库
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转载)(官方)UE4--图像编程----着色器开发
  • (转载)OpenStack Hacker养成指南
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .Net7 环境安装配置
  • @RequestBody与@ResponseBody的使用
  • @SpringBootApplication 包含的三个注解及其含义
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [Android] Amazon 的 android 音视频开发文档
  • [BZOJ] 2044: 三维导弹拦截
  • [BZOJ1053][HAOI2007]反素数ant