Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 589|回复: 0

记Outlook插件与Web页面交互的各种坑 (含c# HttpWebRequest 连接https 的完美解决方法)

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-8-26 15:47:03 | 显示全部楼层 |阅读模式

    1) 方案一,  使用Web Service  基础功能没问题, 只是在连接https (ssh) 网站时, 需要针对https进行开发 (即http 和https 生成两套接口, 不太容易统一 ).   后来改为使用web页面交互(实质是.ashx) 结果也遇到了不少问题.

     
    2) 方案二, 使用  HttpWebRequest     HttpWebRequest  这东西get数据很容易, POST却很麻烦, 最终代码如下:
     
     1 public class WebApiClient
     2     {
     3         public string Url { get; set; }
     4         private CookieContainer Cookies = new CookieContainer();
     5          
     6           
     7         public static string Get( string url )
     8         {
     9             //this code can fix https after .net 4.6
    10             if (url.StartsWith("https", StringComparison.CurrentCultureIgnoreCase))
    11             {
    12                 System.Net.ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
    13             }
    14             System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)WebRequest.Create(url);
    15             request.Method = "GET";
    16             request.ContentType = "application/x-www-form-urlencoded";
    17              
    18             System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
    19             System.IO.Stream s = response.GetResponseStream();
    20             using (StreamReader reader = new StreamReader(s))
    21             { 
    22                 string strValue = reader.ReadToEnd();
    23                 return strValue;
    24             }
    25         }
    26         private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
    27         {
    28             return true;  
    29         }
    30         public Result<DataTable> CallWebApi(string action, string json )
    31         {
    32             return CallWebApi(action, json, null, null);
    33         }
    34         public Result<DataTable> CallWebApi(string action, string json, string fileName, byte[] fileData)
    35         {
    36             string responseContent;
    37             var memStream = new MemoryStream();
    38             var webRequest = (HttpWebRequest)WebRequest.Create(Url); 
    39             var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); 
    40             var beginBoundary = Encoding.ASCII.GetBytes("--" + boundary + "\r\n");
    41             //var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); 
    42             var endBoundary = Encoding.ASCII.GetBytes("--" + boundary + "--\r\n"); 
    43             webRequest.Method = "POST"; 
    44             //webRequest.Timeout = timeOut;
    45             webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
    46             if (!string.IsNullOrEmpty(fileName))
    47             {
    48                 const string filePartHeader =
    49                "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
    50                 "Content-Type: application/octet-stream\r\n\r\n";
    51                 var header = string.Format(filePartHeader, "file1", fileName);
    52                 var headerbytes = Encoding.UTF8.GetBytes(header);
    53                 memStream.Write(beginBoundary, 0, beginBoundary.Length);
    54                 memStream.Write(headerbytes, 0, headerbytes.Length);
    55                 memStream.Write(fileData, 0, fileData.Length);
    56             }
    57             
    58             
    59             var stringKeyHeader = "\r\n--" + boundary +
    60                                    "\r\nContent-Disposition: form-data; name=\"{0}\"" +
    61                                    "\r\n\r\n{1}\r\n";
    62             var bytes = Encoding.UTF8.GetBytes(string.Format(stringKeyHeader, "action", action));
    63             memStream.Write(bytes, 0, bytes.Length);
    64             bytes = Encoding.UTF8.GetBytes(string.Format(stringKeyHeader, "json", json));
    65             memStream.Write(bytes, 0, bytes.Length);
    66              
    67             memStream.Write(endBoundary, 0, endBoundary.Length);
    68             webRequest.ContentLength = memStream.Length;
    69             var requestStream = webRequest.GetRequestStream();
    70             memStream.Position = 0;
    71             var tempBuffer = new byte[memStream.Length];
    72             memStream.Read(tempBuffer, 0, tempBuffer.Length);
    73             memStream.Close();
    74             requestStream.Write(tempBuffer, 0, tempBuffer.Length);
    75             requestStream.Close();
    76             var httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
    77             using (var httpStreamReader = new StreamReader(httpWebResponse.GetResponseStream(),
    78                                                             Encoding.GetEncoding("utf-8")))
    79             {
    80                 responseContent = httpStreamReader.ReadToEnd();
    81             } 
    82             httpWebResponse.Close();
    83             webRequest.Abort();
    84             try
    85             {
    86                 return JsonConvert.DeserializeObject<Result<DataTable>>(responseContent);
    87             }
    88             catch (Exception ex)
    89             {
    90                 throw new ApplicationException("Parse server result error: " + responseContent, ex);
    91             } 
    92         }
    93          
    94     }
    View Code


     

    POST数据的麻烦, 封装一下可以忍了, 但是连接HTTPS的网站时, 有的网站可以, 有的网站会出错: System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
       at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
       at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
       --- End of inner exception stack trace ---
       at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
       at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
       at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
       at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
       at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
       at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
       at System.Net.ConnectStream.WriteHeaders(Boolean async)
       --- End of inner exception stack trace ---
       at System.Net.HttpWebRequest.GetResponse()
     
    如果是普通的桌面程序 , 在.net 4.6以上版本时, Create  HttpWebRequest 对象前添加
    1. if(url.StartsWith("https",StringComparison.CurrentCultureIgnoreCase))
    2. {
    3. System.Net.ServicePointManager.ServerCertificateValidationCallback=newRemoteCertificateValidationCallback(CheckValidationResult);
    4. }
    1. privatestatic bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain,SslPolicyErrors errors)
    2. {
    3. returntrue;
    4. }
     
    有的web服务器要求较新的HTTPS的安装协议, 这种情况下.net4.0以下的版本不支持, 因此要升级到.net4.6以上, 并且在创建HttpWebRequest对象前加入如下设置代码:
    1. ServicePointManager.SecurityProtocol=SecurityProtocolType.Tls|SecurityProtocolType.Tls11|SecurityProtocolType.Tls12|SecurityProtocolType.Ssl3;
    这样就可以最大程度的兼容各种安全协议了. 有的时候发现我们的程序挑.net framework的版本, 大都是由这个原因引起的.
    解决了HttpWebRequest连接HTTPS的问题之后, 该方案成为了功能最强的方案(重要是能较好的支持文件的上传)
     
    方案三 使用WebBrowser控件 .
     
    初步试了一下, get https的页面没有问题, post 的略有麻烦, 网上查到的方法是截获post事件进行处理:

    WebBrowser 其实是对 ActiveX 控件 SHDocVw 的封装,而这个SHDocVw的很多底层调用WebBrowser控件并没有提供实现,我们需要直接操作 SHDoceVw 控件来实现这些高级调用。操作方法如下:
    1、在 windows/system32 目录下找到 shdocvw.dll 这个动态库,将其添加到引用中

    2、在 Form1_Load 中添加如下语句

    SHDocVw.WebBrowser wb = (SHDocVw.WebBrowser)webBrowser1.ActiveXInstance; 
    wb.BeforeNavigate2 += new DWebBrowserEvents2_BeforeNavigate2EventHandler(WebBrowser_BeforeNavigate2);

    3、添加如下成员函数

    private void WebBrowser_BeforeNavigate2(object pDisp, ref object URL, ref object Flags, 
    ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
    {
    string postDataText = System.Text.Encoding.ASCII.GetString(PostData as byte[]);
    }


    完成上述3步后,你post 数据时, 就会响应 BeforeNavigate2 事件,postDataText 中就是你post的数据。你也可以修改PostData,对这些数据进行转换或加密。


    感觉会比较麻烦, 其实还有一种方法,就是利用WebBrowser的DocumentText属性注入脚本(执行脚本), 然后利用Ajax的方式和web服务器进行交互. 之后利用JS和C#互操作来完成相应的功能.
    互操作的C#类上需要加上这个设置:
    1. [System.Runtime.InteropServices.ComVisibleAttribute(true)]
    并且运行此代码:
    1. webBrowser1.ObjectForScripting=this;
    C#里调用JS方法:
    1. webBrowser1.Document.InvokeScript("jsFunction",newstring[]{‘ssss’});
     
    JS里调用C#方法:
    1. window.external.CSharpFunction(‘呵呵’);

    这个方法也有些麻烦的地方, 而且不支持文件的上传.

     





    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-29 11:54 , Processed in 0.067114 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表