Exchange学习:EWS 通过流通知和拉取通知订阅Exchange新邮件提醒

摘要:
EWS通知的主要形式是订阅。通常,每个邮箱都有一个订阅。您还可以订阅邮箱订阅中的一些或所有文件夹。您可以决定订阅什么类型的通知以及接收什么类型的事件,然后可以创建订阅。EWS流式通知和拉式通知之间的区别在于,流式通知取决于服务器上挂起的获取请求,以保持流式订阅连接打开,因此在连接活动期间发生的任何事件都将立即流到客户端。连接到期后,客户端再次发送获取请求。这可能导致在获取GetEvents响应时没有通知。
原理

EWS 通知以订阅的形式进行。 通常,每个邮箱一个订阅,在邮箱订阅内你还可以订阅一些或所有文件夹。 你可以决定订阅什么种类的通知(流式、拉取、推送)以及接收什么种类的事件(NewMail、Created、Deleted、Modified等),然后可以创建订阅。 然后 EWS 事件会非同步地从邮箱服务器发送到客户端。

EWS流式通知和拉取通知区别

流式通知依赖于一个在服务器挂起的 get 请求来保持流式订阅连接打开,这样任何在连接活动时发生的事件都会马上流给客户端。 多个通知可以在单个连接周期内发送,在至多 30 分钟的间隔期过期前,连接都会保持打开。 连接过期后,客户端再次发送 get 请求。 下面显示了流式订阅和流式通知工作的原理。

流式通知概述

拉取通知依靠客户端以一定间隔请求客户端管理的通知。 这可能会在获得 GetEvents 响应时未收到通知。 下面显示了拉取订阅和拉取通知工作的原理。

拉取通知概述

拉取通知代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Microsoft.Exchange.WebServices.Data;

namespace SubscribeToPullNotifications
{
    class Program
    {
        static ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
        static PullSubscription Subscription;
        static void Main(string[] args)
        {
            System.Net.ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
            SubscribePullNotifications(WellKnownFolderName.Inbox);
            Timer t = new Timer();
            t.Elapsed += t_Elapsed;
            t.Interval = 5000;
            t.Start();
            Console.Read();
        }

        static void t_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("程序运行中");
            GetPullNotifications();
        }
        public static void SubscribePullNotifications(FolderId folderId)
        {
            service.Url = new Uri("https://xxx.xxx.xxx.xxx/EWS/Exchange.asmx");
            service.Credentials = new NetworkCredential("xxx@xxx.xxx", "xxx");
            Subscription = service.SubscribeToPullNotifications(new FolderId[] { folderId }, 1440, null, EventType.NewMail, EventType.Created, EventType.Deleted, EventType.Moved);
        }
        public static void GetPullNotifications()
        {
            IEnumerable<ItemEvent> itemEvents = Subscription.GetEvents().ItemEvents;
            IEnumerable<FolderEvent> folderEvents = Subscription.GetEvents().FolderEvents;
            foreach (ItemEvent itemEvent in itemEvents)
            {
                switch (itemEvent.EventType)
                {
                    case EventType.Copied:
                        Console.WriteLine("ItemEvent Copied");
                        break;
                    case EventType.Created:
                        Console.WriteLine("ItemEvent Created");
                        break;
                    case EventType.Deleted:
                        Console.WriteLine("ItemEvent Deleted");
                        break;
                    case EventType.FreeBusyChanged:
                        Console.WriteLine("ItemEvent FreeBusyChanged");
                        break;
                    case EventType.Modified:
                        Console.WriteLine("ItemEvent Modified");
                        break;
                    case EventType.Moved:
                        Console.WriteLine("ItemEvent Moved");
                        break;
                    case EventType.NewMail:
                        Console.WriteLine("ItemEvent New mail");
                        Console.WriteLine(itemEvent.ItemId.UniqueId);
                        EmailMessage emailMessage = EmailMessage.Bind(service, itemEvent.ItemId.UniqueId);
                        Console.WriteLine(emailMessage.Subject);
                        Console.WriteLine(emailMessage.Body.Text);
                        Console.WriteLine(emailMessage.From);
                        Console.WriteLine(emailMessage.DateTimeReceived);
                        Console.WriteLine(string.Join(";", emailMessage.ToRecipients.Select(x => x.Address).ToArray()));
                        Console.WriteLine(string.Join(",", emailMessage.CcRecipients.Select(u => u.Address).ToArray()));
                        break;
                    case EventType.Status:
                        Console.WriteLine("ItemEvent Status");
                        break;
                    default:
                        break;
                }

                foreach (var folderEvent in folderEvents)
                {
                    switch (folderEvent.EventType)
                    {
                        case EventType.Copied:
                            Console.WriteLine("FolderEvent Copied");
                            break;
                        case EventType.Created:
                            Console.WriteLine("FolderEvent Created");
                            break;
                        case EventType.Deleted:
                            Console.WriteLine("FolderEvent Deleted");
                            break;
                        case EventType.FreeBusyChanged:
                            Console.WriteLine("FolderEvent FreeBusyChanged");
                            break;
                        case EventType.Modified:
                            Console.WriteLine("FolderEvent Modified");
                            break;
                        case EventType.Moved:
                            Console.WriteLine("FolderEvent Moved");
                            break;
                        case EventType.NewMail:
                            Console.WriteLine("FolderEvent NewMail");
                            break;
                        default:
                            break;
                    }
                }
            }
        }

    }
}

流通知:

        public void SyncGet()
        {
            _service = InitService(_service);
            // Subscribe to pull notifications in the Inbox.
            PullSubscription subscription = _service.SubscribeToPullNotifications(
                new FolderId[] { WellKnownFolderName.Inbox }, 30, null,
                EventType.NewMail, EventType.Created, EventType.Deleted,
                EventType.Modified, EventType.Moved, EventType.Copied, EventType.FreeBusyChanged);

            // Call GetEvents to retrieve events from the server. 
            GetEventsResults events = subscription.GetEvents();
        }

        public void SyncEmail()
        {
            _service = InitService(_service);
            // Subscribe to streaming notifications in the Inbox. 
            var StreamingSubscription = _service.SubscribeToStreamingNotifications(
                new FolderId[] { WellKnownFolderName.Inbox },
                EventType.NewMail,
                EventType.Created,
                EventType.Deleted,
                EventType.Modified,
                EventType.Moved,
                EventType.Copied,
                EventType.FreeBusyChanged);

            // Create a streaming connection to the service object, over which events are returned to the client.
            // Keep the streaming connection open for 30 minutes.
            StreamingSubscriptionConnection connection = new StreamingSubscriptionConnection(_service, 30);
            connection.AddSubscription(StreamingSubscription);
            connection.OnNotificationEvent += OnNotificationEvent;
            connection.OnDisconnect += OnDisconnect;
            connection.Open();
        }


        private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
        {
            SyncEmail();
        }

        private void OnNotificationEvent(object sender, NotificationEventArgs args)
        {
            if (args != null)
            {
                foreach (NotificationEvent notificationEvent in args.Events)
                {
                    if (notificationEvent is ItemEvent)
                    {
                        ItemEvent itemEvent = (ItemEvent)notificationEvent;
                        Console.WriteLine(notificationEvent.EventType.ToString());
                        Item item = Item.Bind(_service, itemEvent.ItemId);
                        switch (notificationEvent.EventType)
                        {
                            case EventType.Moved:
                                Console.WriteLine(item.Subject);
                                break;
                            case EventType.NewMail:
                                Console.WriteLine(item.Subject);
                                break;
                            case EventType.Created:
                                Console.WriteLine($"创建:{item.Subject}");break;
                            case EventType.Deleted:
                                Console.WriteLine($"删除:{item.Subject}");
                                break;
                            default:
                                break;
                        }
                    }

                }
            }
        }

另外事件分为ItemEvents和FolderEvents,具体区别可以看下图:

Exchange学习:EWS 通过流通知和拉取通知订阅Exchange新邮件提醒第3张

 现实中,多个通知(甚至是多个同类型的通知)可以由单一用户操作创建。 比如,在文件夹移动操作的情况中,三个文件夹事件被创建:一个关于文件夹修改,一个关于旧文件夹,还有一个关于新文件夹。 因为单一操作可以出现多个事件,所以要创建一个等待事件,这样同步只有在操作完成时才会进行,而不是在操作中多次进行。

如果遇到证书问题则需要在程序中加入:

            System.Net.ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
  
private static bool CertificateValidationCallBack(
 object sender,
 System.Security.Cryptography.X509Certificates.X509Certificate certificate,
 System.Security.Cryptography.X509Certificates.X509Chain chain,
 System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            // If the certificate is a valid, signed certificate, return true.
            if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
            {
                return true;
            }

            // If there are errors in the certificate chain, look at each error to determine the cause.
            if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
            {
                if (chain != null && chain.ChainStatus != null)
                {
                    foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
                    {
                        if ((certificate.Subject == certificate.Issuer) &&
                            (status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
                        {
                            // Self-signed certificates with an untrusted root are valid. 
                            continue;
                        }
                        else
                        {
                            if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
                            {
                                // If there are any other errors in the certificate chain, the certificate is invalid,
                                // so the method returns false.
                                return false;
                            }
                        }
                    }
                }

                // When processing reaches this line, the only errors in the certificate chain are 
                // untrusted root errors for self-signed certificates. These certificates are valid
                // for default Exchange server installations, so return true.
                return true;
            }
            else
            {
                // In all other cases, return false.
                return false;
            }
        }

注意这个不要在正式环境使用。

参考自:https://docs.microsoft.com/zh-cn/exchange/client-developer/exchange-web-services/notification-subscriptions-mailbox-events-and-ews-in-exchange

免责声明:文章转载自《Exchange学习:EWS 通过流通知和拉取通知订阅Exchange新邮件提醒》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇html页面通过http访问mysql数据库中的内容,实现用户登录的功能ubuntu环境下部署SVN自动更新总结下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

在Exchange 2013中重置用户密码

添加各种功能后需要重新登录管理台 Exchange的ECP内如何重置用户的密码。 在Exchange Server 2013中,可以由用户从用户自己的OWA(Outlook Web Access)设置页面或由EAC(Exchange管理中心)的管理员更改用户邮箱密码。从用户的OWA设置更改密码相当简单。但是,如果您想从EAC重置特定用户的密码,则可能会在W...

spring cloud gateway 日志打印

从api请求中获取访问的具体信息,是一个很常见的功能,这几天在研究springcloud,使用到了其中的gateway,刚好将研究的过程结果都记录下来 0. Version <parent> <groupId>org.springframework.boot</groupId> <artifact...

使用spring-rabbit测试RabbitMQ消息确认(发送确认,接收确认)

1、首先是rabbitmq的配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-in...

rabbitMQ日常管理(转)

原文:http://blog.sina.com.cn/s/blog_790c59140102x5vk.html 一、网页登录方法 http://127.0.0.1:15672/ 用户名和密码默认为guest/guest 用java代码去连接rabbitmq用的端口是5672 二、rabbitMQ基本概念 RabbitMQ是一个开源的AMQP实现,服务器端用...

Springcloud之gateway配置及swagger集成

前言 关于引入gateway的好处我网上找了下: 性能:API高可用,负载均衡,容错机制。 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。 日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。 缓存:数据缓存。监控:记录请求响应数据,api耗时分析,性能监控。 限流:流量控制...

高并发处理思路与手段(三):消息队列

一、消息队列在实际场景中的使用     流程A在处理时没有在当前线程同步的处理完而是直接发送了一条消息A1到队列里,然后消息队列过了一段时间(可能是几毫秒 几秒 几分钟)这个消息开始被处理,消息处理的过程就相当于流程A被处理;当然这只是一个简单的模型下面我们套用实际的场景来看一下,比如下单成功后发送短信提醒;如果没有消息队列我们会选择同步调用...