Empezar a trabajar con aplicaciones de cliente de EWS

Cree su primera aplicación utilizando Servicios Web Exchange (EWS) de Exchange.

EWS es un servicio completo que las aplicaciones pueden usar para acceder a casi toda la información almacenada en un Exchange Online, Exchange Online como parte de Office 365 o buzón de Exchange local. EWS usa protocolos web estándar para proporcionar acceso a un servidor Exchange; bibliotecas como la API administrada de EWS encapsulan las operaciones de EWS para proporcionar una interfaz orientada a objetos. Después de ejecutar los ejemplos de este artículo, tendrá un conocimiento básico de lo que puede hacer con EWS.

Puede llamar a operaciones de EWS desde cualquier sistema operativo o lenguaje, ya que las solicitudes y respuestas de EWS usan el protocolo SOAP. Los ejemplos de este artículo se escriben con C# y usan los objetos HttpWebRequest y HttpWebResponse de .NET Framework; sin embargo, la parte importante del código es el XML que se usa para realizar la solicitud de EWS y la respuesta XML devuelta desde el servidor. Los ejemplos de código enfatizan las transacciones XML y no procesan el XML.

Nota:

Vamos a eliminar la posibilidad de utilizar la autenticación básica en Exchange Online para EWS, a partir de octubre de 2022 Deprecación de la autenticación básica en Exchange Online. En su lugar, debería utilizar la autenticación OAuth. Autenticar una aplicación EWS mediante OAuth

Necesitará un servidor de Exchange

Si ya tiene una cuenta de buzón de Exchange, puede omitir este paso. En caso contrario, dispone de las opciones siguientes para configurar un buzón de Exchange para su primera aplicación de EWS:

Después de haber comprobado que puede enviar y recibir correo electrónico desde el servidor Exchange, está listo para configurar el entorno de desarrollo. Puede usar Outlook Web App para comprobar que puede enviar correo electrónico.

También debe conocer la dirección URL del punto de conexión de EWS para su servidor. En una aplicación de producción, se usaría Autodiscover para determinar la dirección URL de EWS. Los ejemplos de este artículo usan la dirección URL del punto de conexión EWS de Office 365, https://outlook.office365.com/EWS/Exchange.asmx. La sección Próximos pasos contiene vínculos a más información sobre Autodiscover cuando esté listo.

Si está probando la aplicación mediante un servidor Exchange que tiene el certificado autofirmado predeterminado, necesitará crear un método de validación de certificados que cumpla los requisitos de seguridad de su organización.

Configurar el entorno de desarrollo

Las herramientas que usa para crear su primera aplicación de EWS dependen del sistema operativo y el idioma que use, y son principalmente una cuestión de gustos. Si quiere seguir los ejemplos de C# de este artículo, necesitará:

  • Cualquier versión de Visual Studio que admita la versión 4.0 de .NET Framework.
  • Una conexión a Internet que su equipo de desarrollo pueda usar para ponerse en contacto con el servidor Exchange. Si puede usar Outlook Web App con un nombre DNS en lugar de una dirección IP para conectarse al servidor Exchange, ya está preparado.

Crear la primera aplicación de EWS

La aplicación de EWS que creará muestra dos escenarios típicos de uso de EWS:

  1. Obtener información de un buzón de Exchange y mostrar esa información al usuario.
  2. Realizar una acción, como enviar un correo electrónico, y comprobar la respuesta para ver si la acción se realizó correctamente.

Comencemos.

Configurar la solución

En primer lugar, cree una nueva solución de aplicación de consola mediante Visual Studio. Cuando la solución esté lista, cree un nuevo objeto denominado Tracing.cs. Use este objeto para escribir información tanto en la consola como en un archivo de registro para que pueda revisar los resultados después de ejecutar el código. Pegue el siguiente código en el archivo Tracing.cs:

using System;
using System.IO;
namespace Microsoft.Exchange.Samples.EWS
{
  class Tracing
  {
    private static TextWriter logFileWriter = null;
    public static void OpenLog(string fileName)
    {
      logFileWriter = new StreamWriter(fileName);
    }
    public static void Write(string format, params object[] args)
    {
      Console.Write(format, args);
      if (logFileWriter != null)
      {
        logFileWriter.Write(format, args);
      }
    }
    public static void WriteLine(string format, params object[] args)
    {
      Console.WriteLine(format, args);
      if (logFileWriter != null)
      {
        logFileWriter.WriteLine(format, args);
      }
    }
    public static void CloseLog()
    {
      logFileWriter.Flush();
      logFileWriter.Close();
    }
  }
}

Luego abra el archivo Program.cs. Colocará el resto del código del ejemplo en este archivo.

En primer lugar, configure el shell del programa. El programa hará lo siguiente:

  1. Crear un archivo de registro para que la solicitud y la respuesta se puedan escribir en el disco para estudio posterior.
  2. Obtener la dirección de correo electrónico y la contraseña de la cuenta a la que usted tendrá acceso.
  3. Llamar a los métodos de ejemplo.

Reemplazar el método Main en el archivo Program.cs por el código siguiente.

    static void Main(string[] args)
    {
      // Start tracing to console and a log file.
      Tracing.OpenLog("./GetStartedWithEWS.log");
      Tracing.WriteLine("EWS sample application started.");
      var isValidEmailAddress = false;
      Console.Write("Enter an email address: ");
      var emailAddress = Console.ReadLine();
      
        isValidEmailAddress = (emailAddress.Contains("@") && emailAddress.Contains("."));
      if (!isValidEmailAddress)
      {
        Tracing.WriteLine("Email address " + emailAddress + " is not a valid SMTP address. Closing program.");
        return;
      }
      SecureString password = GetPasswordFromConsole();
      if (password.Length == 0)
      {
        Tracing.WriteLine("Password empty, closing program.");
      }
      NetworkCredential userCredentials = new NetworkCredential(emailAddress, password);
      // These are the sample methods that demonstrate using EWS.
      // ShowNumberOfMessagesInInbox(userCredentials);
      // SendTestEmail(userCredentials);
     
      Tracing.WriteLine("EWS sample application ends.");
      Tracing.CloseLog();
      Console.WriteLine("Press enter to exit: ");
      Console.ReadLine();
    }
    // These method stubs will be filled in later.
    private static void ShowNumberOfMessagesInInbox(NetworkCredential userCredentials)
    {
    }
    private static void SendTestEmail(NetworkCredential userCredentials)
    {
    }

Lo último que debe hacer es agregar el GetPasswordFromConsole método estático. Este método devuelve un objeto SecureString que contiene una contraseña escrita en la consola.

    private static SecureString GetPasswordFromConsole()
    {
      SecureString password = new SecureString();
      bool readingPassword = true;
      Console.Write("Enter password: ");
      while (readingPassword)
      {
        ConsoleKeyInfo userInput = Console.ReadKey(true);
        switch (userInput.Key)
        {
          case (ConsoleKey.Enter):
            readingPassword = false;
            break;
          case (ConsoleKey.Escape):
            password.Clear();
            readingPassword = false;
            break;
          case (ConsoleKey.Backspace):
            if (password.Length > 0)
            {
              password.RemoveAt(password.Length - 1);
              Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
              Console.Write(" ");
              Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
            }
            break;
          default:
            if (userInput.KeyChar != 0)
            {
              password.AppendChar(userInput.KeyChar);
              Console.Write("*");
            }
            break;
        }
      }
      Console.WriteLine();
      password.MakeReadOnly();
      return password;
    }

Obtener el número de nuevos mensajes en la Bandeja de entrada

Una operación común en una aplicación de EWS es obtener información sobre los mensajes de correo electrónico, las citas, las reuniones y las carpetas en donde se almacenan. En este ejemplo se obtiene el número de mensajes que hay en la Bandeja de entrada de una cuenta y se muestra el número total de mensajes y el número de mensajes no leídos. Esto demuestra las siguientes acciones comunes para aplicaciones de EWS:

  • Realizar una solicitud EWS al servidor Exchange.
  • Analizar la respuesta XML devuelta para obtener la información solicitada.
  • Manejar los mensajes de error y las excepciones comunes.

Agregue el código siguiente al ShowNumberOfMessagesInInbox método que se ha creado como código auxiliar después del método main. Cuando se ejecute la aplicación, se imprimirá el número de mensajes que hay en la Bandeja de entrada de la cuenta y el número de mensajes no leídos de la Bandeja de entrada. Después de ejecutar la aplicación, puede abrir el archivo GetStartedWithEWS.log para ver la solicitud XML que se envió al servidor Exchange y la respuesta que devolvió el servidor.

      /// This is the XML request that is sent to the Exchange server.
      var getFolderSOAPRequest =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<soap:Envelope xmlns:soap=\"https://schemas.xmlsoap.org/soap/envelope/\"\n" +
"   xmlns:t=\"https://schemas.microsoft.com/exchange/services/2006/types\">\n" +
"<soap:Header>\n" +
"    <t:RequestServerVersion Version=\"Exchange2007_SP1\" />\n" +
"  </soap:Header>\n" +
"  <soap:Body>\n" +
"    <GetFolder xmlns=\"https://schemas.microsoft.com/exchange/services/2006/messages\"\n" +
"               xmlns:t=\"https://schemas.microsoft.com/exchange/services/2006/types\">\n" +
"      <FolderShape>\n" +
"        <t:BaseShape>Default</t:BaseShape>\n" +
"      </FolderShape>\n" +
"      <FolderIds>\n" +
"        <t:DistinguishedFolderId Id=\"inbox\"/>\n" +
"      </FolderIds>\n" +
"    </GetFolder>\n" +
"  </soap:Body>\n" +
"</soap:Envelope>\n";
      // Write the get folder operation request to the console and log file.
      Tracing.WriteLine("Get folder operation request:");
      Tracing.WriteLine(getFolderSOAPRequest);
      var getFolderRequest = WebRequest.CreateHttp(Office365WebServicesURL);
      getFolderRequest.AllowAutoRedirect = false;
      getFolderRequest.Credentials = userCredentials;
      getFolderRequest.Method = "POST";
      getFolderRequest.ContentType = "text/xml";
      var requestWriter = new StreamWriter(getFolderRequest.GetRequestStream());
      requestWriter.Write(getFolderSOAPRequest);
      requestWriter.Close();
      try
      {
        var getFolderResponse = (HttpWebResponse)(getFolderRequest.GetResponse());
        if (getFolderResponse.StatusCode == HttpStatusCode.OK)
        {
          var responseStream = getFolderResponse.GetResponseStream();
          XElement responseEnvelope = XElement.Load(responseStream);
          if (responseEnvelope != null)
          {
            // Write the response to the console and log file.
            Tracing.WriteLine("Response:");
            StringBuilder stringBuilder = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(stringBuilder, settings);
            responseEnvelope.Save(writer);
            writer.Close();
            Tracing.WriteLine(stringBuilder.ToString());
            // Check the response for error codes. If there is an error, throw an application exception.
            IEnumerable<XElement> errorCodes = from errorCode in responseEnvelope.Descendants
                                               ("{https://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode")
                                               select errorCode;
            foreach (var errorCode in errorCodes)
            {
              if (errorCode.Value != "NoError")
              {
                switch (errorCode.Parent.Name.LocalName.ToString())
                {
                  case "Response":
                    string responseError = "Response-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(responseError);
                  case "UserResponse":
                    string userError = "User-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(userError);
                }
              }
            }
            // Process the response.
            IEnumerable<XElement> folders = from folderElement in
                                              responseEnvelope.Descendants
                                              ("{https://schemas.microsoft.com/exchange/services/2006/messages}Folders")
                                            select folderElement;
            foreach (var folder in folders)
            {
              Tracing.Write("Folder name:     ");
              var folderName = from folderElement in
                                 folder.Descendants
                                 ("{https://schemas.microsoft.com/exchange/services/2006/types}DisplayName")
                               select folderElement.Value;
              Tracing.WriteLine(folderName.ElementAt(0));
              Tracing.Write("Total messages:  ");
              var totalCount = from folderElement in
                                 folder.Descendants
                                   ("{https://schemas.microsoft.com/exchange/services/2006/types}TotalCount")
                               select folderElement.Value;
              Tracing.WriteLine(totalCount.ElementAt(0));
              Tracing.Write("Unread messages: ");
              var unreadCount = from folderElement in
                                 folder.Descendants
                                   ("{https://schemas.microsoft.com/exchange/services/2006/types}UnreadCount")
                               select folderElement.Value;
              Tracing.WriteLine(unreadCount.ElementAt(0));
            }
          }
        }
      }
      catch (WebException ex)
      {
        Tracing.WriteLine("Caught Web exception:");
        Tracing.WriteLine(ex.Message);
      }
      catch (ApplicationException ex)
      {
        Tracing.WriteLine("Caught application exception:");
        Tracing.WriteLine(ex.Message);
      }

Enviar un mensaje de correo electrónico

Otra operación común para una aplicación de EWS consiste en enviar mensajes de correo electrónico o convocatorias de reunión. En este ejemplo se crea y se envía un mensaje de correo electrónico usando las credenciales del usuario que se especificaron anteriormente. Aquí se ven estas tareas de aplicación comunes de EWS:

  • Crear y enviar un correo electrónico.

  • Analizar la respuesta XML devuelta para determinar si el correo electrónico se ha enviado correctamente.

  • Manejar los mensajes de error y las excepciones comunes.

Agregue el código siguiente al método SendTestEmail que era auxiliar después del método main. Después de ejecutar la aplicación, puede abrir el archivo GetStartedWithEWS.log para ver la solicitud XML que se envió al servidor Exchange y la respuesta que devolvió el servidor.

var createItemSOAPRequest =
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
      "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" +
      "               xmlns:m=\"https://schemas.microsoft.com/exchange/services/2006/messages\" \n" +
      "               xmlns:t=\"https://schemas.microsoft.com/exchange/services/2006/types\" \n" +
      "               xmlns:soap=\"https://schemas.xmlsoap.org/soap/envelope/\">\n" +
      "  <soap:Header>\n" +
      "    <t:RequestServerVersion Version=\"Exchange2007_SP1\" />\n" +
      "  </soap:Header>\n" +
      "  <soap:Body>\n" +
      "    <m:CreateItem MessageDisposition=\"SendAndSaveCopy\">\n" +
      "      <m:SavedItemFolderId>\n" +
      "        <t:DistinguishedFolderId Id=\"sentitems\" />\n" +
      "      </m:SavedItemFolderId>\n" +
      "      <m:Items>\n" +
      "        <t:Message>\n" +
      "          <t:Subject>Company Soccer Team</t:Subject>\n" +
      "          <t:Body BodyType=\"HTML\">Are you interested in joining?</t:Body>\n" +
      "          <t:ToRecipients>\n" +
      "            <t:Mailbox>\n" +
      "              <t:EmailAddress>sadie@contoso.com</t:EmailAddress>\n" +
      "              </t:Mailbox>\n" +
      "          </t:ToRecipients>\n" +
      "        </t:Message>\n" +
      "      </m:Items>\n" +
      "    </m:CreateItem>\n" +
      "  </soap:Body>\n" +
      "</soap:Envelope>\n";
      // Write the create item operation request to the console and log file.
      Tracing.WriteLine("Get folder operation request:");
      Tracing.WriteLine(createItemSOAPRequest);
      var getFolderRequest = WebRequest.CreateHttp(Office365WebServicesURL);
      getFolderRequest.AllowAutoRedirect = false;
      getFolderRequest.Credentials = userCredentials;
      getFolderRequest.Method = "POST";
      getFolderRequest.ContentType = "text/xml";
      var requestWriter = new StreamWriter(getFolderRequest.GetRequestStream());
      requestWriter.Write(createItemSOAPRequest);
      requestWriter.Close();
      try
      {
        var getFolderResponse = (HttpWebResponse)(getFolderRequest.GetResponse());
        if (getFolderResponse.StatusCode == HttpStatusCode.OK)
        {
          var responseStream = getFolderResponse.GetResponseStream();
          XElement responseEnvelope = XElement.Load(responseStream);
          if (responseEnvelope != null)
          {
            // Write the response to the console and log file.
            Tracing.WriteLine("Response:");
            StringBuilder stringBuilder = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(stringBuilder, settings);
            responseEnvelope.Save(writer);
            writer.Close();
            Tracing.WriteLine(stringBuilder.ToString());
            // Check the response for error codes. If there is an error, throw an application exception.
            IEnumerable<XElement> errorCodes = from errorCode in responseEnvelope.Descendants
                                               ("{https://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode")
                                               select errorCode;
            foreach (var errorCode in errorCodes)
            {
              if (errorCode.Value != "NoError")
              {
                switch (errorCode.Parent.Name.LocalName.ToString())
                {
                  case "Response":
                    string responseError = "Response-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(responseError);
                  case "UserResponse":
                    string userError = "User-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(userError);
                }
              }
            }
            Tracing.WriteLine("Message sent successfully.");
          }
        }
      }
      catch (WebException ex)
      {
        Tracing.WriteLine("Caught Web exception:");
        Tracing.WriteLine(ex.Message);
      }
      catch (ApplicationException ex)
      {
        Tracing.WriteLine("Caught application exception:");
        Tracing.WriteLine(ex.Message);
      }

Siguientes pasos

Ahora que ya ha escrito su primera aplicación de EWS, está listo para descubrir otras formas de usar EWS. A continuación, le damos algunas ideas para empezar.

Si surge algún problema con la aplicación envíe una pregunta o un comentario al foro (y no olvide leer la primera publicación).

Vea también