En entornos locales de Exchange, el complemento debe realizar los pasos siguientes para obtener datos adjuntos directamente de Exchange.
Obtenga el token de devolución de llamada del Exchange Server.
Envíe el token de devolución de llamada y la información de datos adjuntos al servicio remoto.
Obtenga los datos adjuntos del servidor exchange mediante el ExchangeService.GetAttachments
método o la GetAttachment
operación .
Cada paso se describe con detalle en las secciones siguientes mediante código del ejemplo Outlook-Add-in-JavaScript-GetAttachments .
Nota:
El código de estos ejemplos se ha abreviado para destacar la información de los datos adjuntos. El ejemplo contiene más código que sirve para autenticar el complemento en el servidor remoto y administrar el estado de la solicitud.
Obtener un token de devolución de llamada
El objeto Office.context.mailbox proporciona el getCallbackTokenAsync
método para obtener un token que el servidor remoto puede usar para autenticarse con el servidor de Exchange. El siguiente código muestra una función en un complemento que inicia la solicitud asincrónica para obtener el token de devolución de llamada y la función de devolución de llamada que obtiene la respuesta. El token de devolución de llamada se almacena en el objeto de solicitud de servicio definido en la sección siguiente.
function getAttachmentToken() {
if (serviceRequest.attachmentToken == "") {
Office.context.mailbox.getCallbackTokenAsync(attachmentTokenCallback);
}
}
function attachmentTokenCallback(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
// Cache the result from the server.
serviceRequest.attachmentToken = asyncResult.value;
serviceRequest.state = 3;
testAttachments();
} else {
showToast("Error", "Couldn't get callback token: " + asyncResult.error.message);
}
}
El servicio remoto al que el complemento llama define los detalles sobre cómo se debe enviar la información de datos adjuntos al servicio. En este ejemplo, el servicio remoto es una aplicación de API web creada mediante Visual Studio. El servicio remoto espera que la información de datos adjuntos esté en un objeto JSON.
La propiedad Office.context.mailbox.ewsUrl se usa para proporcionar la dirección URL de Exchange Web Services (EWS) en el servidor exchange que hospeda el buzón. A continuación, el servicio remoto usa esta dirección URL para llamar al método ExchangeService.GetAttachments o a la operación EWS GetAttachment en un paso posterior.
Con el siguiente código se inicializa un objeto que contiene la información de datos adjuntos.
// Initialize a context object for the add-in.
// Set the fields that are used on the request
// object to default values.
const serviceRequest = {
attachmentToken: '',
ewsUrl: Office.context.mailbox.ewsUrl,
attachments: []
};
La propiedad Office.context.mailbox.item.attachments
contiene una colección de objetos AttachmentDetails
, uno para cada dato adjunto al elemento. En la mayoría de los casos, el complemento puede pasar simplemente la propiedad del identificador de los datos adjuntos de un objeto AttachmentDetails
al servicio remoto. Si el servicio remoto necesita más información sobre los datos adjuntos, puede pasar todo el objeto AttachmentDetails
o parte de él. El siguiente código define un método que pone toda la matriz AttachmentDetails
en el objeto serviceRequest
y envía una solicitud al servicio remoto.
function makeServiceRequest() {
// Format the attachment details for sending.
for (let i = 0; i < mailbox.item.attachments.length; i++) {
serviceRequest.attachments[i] = JSON.parse(JSON.stringify(mailbox.item.attachments[i]));
}
$.ajax({
url: '../../api/Default',
type: 'POST',
data: JSON.stringify(serviceRequest),
contentType: 'application/json;charset=utf-8'
}).done(function (response) {
if (!response.isError) {
const names = "<h2>Attachments processed using " +
serviceRequest.service +
": " +
response.attachmentsProcessed +
"</h2>";
for (let i = 0; i < response.attachmentNames.length; i++) {
names += response.attachmentNames[i] + "<br />";
}
document.getElementById("names").innerHTML = names;
} else {
app.showNotification("Runtime error", response.message);
}
}).fail(function (status) {
}).always(function () {
$('.disable-while-sending').prop('disabled', false);
})
}
Obtener los datos adjuntos del Exchange Server.
El servicio remoto usa el método GetAttachments de la API administrada de EWS o la operación GetAttachment de EWS para recuperar los datos adjuntos del servidor. La aplicación de servicio necesita dos objetos para deserializar la cadena JSON y convertirla en objetos .NET Framework que se puedan usar en el servidor. En el código siguiente se muestran las definiciones de los objetos de deserialización.
namespace AttachmentsSample
{
public class AttachmentSampleServiceRequest
{
public string attachmentToken { get; set; }
public string ewsUrl { get; set; }
public string service { get; set; }
public AttachmentDetails [] attachments { get; set; }
}
public class AttachmentDetails
{
public string attachmentType { get; set; }
public string contentType { get; set; }
public string id { get; set; }
public bool isInline { get; set; }
public string name { get; set; }
public int size { get; set; }
}
}
Uso de la API administrada de EWS para obtener datos adjuntos
Si utiliza la API administrada de EWS en su servicio remoto, puede usar el método GetAttachments, el cual creará, enviará y recibirá una solicitud EWS SOAP para obtener los datos adjuntos. Se recomienda usar la API administrada de EWS porque requiere menos líneas de código y tiene una interfaz para realizar llamadas a EWS más intuitiva. El código siguiente realiza una solicitud para recuperar todos los datos adjuntos y devuelve el número y los nombres de los datos adjuntos procesados.
private AttachmentSampleServiceResponse GetAtttachmentsFromExchangeServerUsingEWSManagedApi(AttachmentSampleServiceRequest request)
{
var attachmentsProcessedCount = 0;
var attachmentNames = new List<string>();
// Create an ExchangeService object, set the credentials and the EWS URL.
ExchangeService service = new ExchangeService();
service.Credentials = new OAuthCredentials(request.attachmentToken);
service.Url = new Uri(request.ewsUrl);
var attachmentIds = new List<string>();
foreach (AttachmentDetails attachment in request.attachments)
{
attachmentIds.Add(attachment.id);
}
// Call the GetAttachments method to retrieve the attachments on the message.
// This method results in a GetAttachments EWS SOAP request and response
// from the Exchange server.
var getAttachmentsResponse =
service.GetAttachments(attachmentIds.ToArray(),
null,
new PropertySet(BasePropertySet.FirstClassProperties,
ItemSchema.MimeContent));
if (getAttachmentsResponse.OverallResult == ServiceResult.Success)
{
foreach (var attachmentResponse in getAttachmentsResponse)
{
attachmentNames.Add(attachmentResponse.Attachment.Name);
// Write the content of each attachment to a stream.
if (attachmentResponse.Attachment is FileAttachment)
{
FileAttachment fileAttachment = attachmentResponse.Attachment as FileAttachment;
Stream s = new MemoryStream(fileAttachment.Content);
// Process the contents of the attachment here.
}
if (attachmentResponse.Attachment is ItemAttachment)
{
ItemAttachment itemAttachment = attachmentResponse.Attachment as ItemAttachment;
Stream s = new MemoryStream(itemAttachment.Item.MimeContent.Content);
// Process the contents of the attachment here.
}
attachmentsProcessedCount++;
}
}
// Return the names and number of attachments processed for display
// in the add-in UI.
var response = new AttachmentSampleServiceResponse();
response.attachmentNames = attachmentNames.ToArray();
response.attachmentsProcessed = attachmentsProcessedCount;
return response;
}
Usar EWS para obtener datos adjuntos
Si usa EWS en el servicio remoto, debe crear una solicitud SOAP GetAttachment para obtener los datos adjuntos del Exchange Server. El siguiente código devuelve una cadena que proporciona la solicitud SOAP. El servicio remoto usa el método String.Format
para insertar el identificador de los datos adjuntos en la cadena.
private const string GetAttachmentSoapRequest =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:xsi=""https://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""https://www.w3.org/2001/XMLSchema""
xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">
<soap:Header>
<t:RequestServerVersion Version=""Exchange2016"" />
</soap:Header>
<soap:Body>
<GetAttachment xmlns=""http://schemas.microsoft.com/exchange/services/2006/messages""
xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">
<AttachmentShape/>
<AttachmentIds>
<t:AttachmentId Id=""{0}""/>
</AttachmentIds>
</GetAttachment>
</soap:Body>
</soap:Envelope>";
Por último, el siguiente método usa una solicitud GetAttachment
de EWS para obtener los datos adjuntos del Exchange Server. Esta implementación hace una solicitud individual de cada dato adjunto y devuelve el número de datos adjuntos procesados. Cada respuesta se procesa en un método ProcessXmlResponse
independiente, definido a continuación.
private AttachmentSampleServiceResponse GetAttachmentsFromExchangeServerUsingEWS(AttachmentSampleServiceRequest request)
{
var attachmentsProcessedCount = 0;
var attachmentNames = new List<string>();
foreach (var attachment in request.attachments)
{
// Prepare a web request object.
HttpWebRequest webRequest = WebRequest.CreateHttp(request.ewsUrl);
webRequest.Headers.Add("Authorization",
string.Format("Bearer {0}", request.attachmentToken));
webRequest.PreAuthenticate = true;
webRequest.AllowAutoRedirect = false;
webRequest.Method = "POST";
webRequest.ContentType = "text/xml; charset=utf-8";
// Construct the SOAP message for the GetAttachment operation.
byte[] bodyBytes = Encoding.UTF8.GetBytes(
string.Format(GetAttachmentSoapRequest, attachment.id));
webRequest.ContentLength = bodyBytes.Length;
Stream requestStream = webRequest.GetRequestStream();
requestStream.Write(bodyBytes, 0, bodyBytes.Length);
requestStream.Close();
// Make the request to the Exchange server and get the response.
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
// If the response is okay, create an XML document from the response
// and process the request.
if (webResponse.StatusCode == HttpStatusCode.OK)
{
var responseStream = webResponse.GetResponseStream();
var responseEnvelope = XElement.Load(responseStream);
// After creating a memory stream containing the contents of the
// attachment, this method writes the XML document to the trace output.
// Your service would perform it's processing here.
if (responseEnvelope != null)
{
var processResult = ProcessXmlResponse(responseEnvelope);
attachmentNames.Add(string.Format("{0} {1}", attachment.name, processResult));
}
// Close the response stream.
responseStream.Close();
webResponse.Close();
}
// If the response is not OK, return an error message for the
// attachment.
else
{
var errorString = string.Format("Attachment \"{0}\" could not be processed. " +
"Error message: {1}.", attachment.name, webResponse.StatusDescription);
attachmentNames.Add(errorString);
}
attachmentsProcessedCount++;
}
// Return the names and number of attachments processed for display
// in the add-in UI.
var response = new AttachmentSampleServiceResponse();
response.attachmentNames = attachmentNames.ToArray();
response.attachmentsProcessed = attachmentsProcessedCount;
return response;
}
Cada respuesta de la operación GetAttachment
se envía al método ProcessXmlResponse
. Este método comprueba la respuesta en busca de errores. Si no encuentra ningún error, procesa los datos adjuntos de los archivos y elementos. El método ProcessXmlResponse
realiza la mayor parte del trabajo para procesar los datos adjuntos.
// This method processes the response from the Exchange server.
// In your application the bulk of the processing occurs here.
private string ProcessXmlResponse(XElement responseEnvelope)
{
// First, check the response for web service errors.
var errorCodes = from errorCode in responseEnvelope.Descendants
("{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode")
select errorCode;
// Return the first error code found.
foreach (var errorCode in errorCodes)
{
if (errorCode.Value != "NoError")
{
return string.Format("Could not process result. Error: {0}", errorCode.Value);
}
}
// No errors found, proceed with processing the content.
// First, get and process file attachments.
var fileAttachments = from fileAttachment in responseEnvelope.Descendants
("{http://schemas.microsoft.com/exchange/services/2006/types}FileAttachment")
select fileAttachment;
foreach(var fileAttachment in fileAttachments)
{
var fileContent = fileAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}Content");
var fileData = System.Convert.FromBase64String(fileContent.Value);
var s = new MemoryStream(fileData);
// Process the file attachment here.
}
// Second, get and process item attachments.
var itemAttachments = from itemAttachment in responseEnvelope.Descendants
("{http://schemas.microsoft.com/exchange/services/2006/types}ItemAttachment")
select itemAttachment;
foreach(var itemAttachment in itemAttachments)
{
var message = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}Message");
if (message != null)
{
// Process a message here.
break;
}
var calendarItem = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}CalendarItem");
if (calendarItem != null)
{
// Process calendar item here.
break;
}
var contact = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}Contact");
if (contact != null)
{
// Process contact here.
break;
}
var task = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}Tontact");
if (task != null)
{
// Process task here.
break;
}
var meetingMessage = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}MeetingMessage");
if (meetingMessage != null)
{
// Process meeting message here.
break;
}
var meetingRequest = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}MeetingRequest");
if (meetingRequest != null)
{
// Process meeting request here.
break;
}
var meetingResponse = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}MeetingResponse");
if (meetingResponse != null)
{
// Process meeting response here.
break;
}
var meetingCancellation = itemAttachment.Element("{http://schemas.microsoft.com/exchange/services/2006/types}MeetingCancellation");
if (meetingCancellation != null)
{
// Process meeting cancellation here.
break;
}
}
return string.Empty;
}