How to retrieve the exact mail content (i.e., Body) as it is.

Roshan Ramesh 25 Reputation points
2025-05-21T06:21:26.65+00:00

Hello,

I am using Microsoft Graph to retrieve emails. However, while retrieving the email, some carriage return and newline characters are removed from the body. I need to retrieve the email body exactly as it is, but for some reason, these characters are being eliminated in the process. I have attached a sample of my code. Please review it and suggest a way to retrieve the email body without modifying the content.

NuGet Package Version:-

Microsoft Graph version : 4.54.0

Microsoft.Identity.Client : 4.69.1

private void Mails()
{           
 try
 {
    Mails = Mail.GetEmails();
    Folders = Mail.GetFolders();
 }                
 catch (Exception ex)
 {
    throw new Exception (ex.Message);
 }

public static IMailFolderMessagesCollectionPage GetEmails()
{
   try
   {
      IMailFolderMessagesCollectionPage messages = MsGraph.MsGraphInfos(TenantId,     ClientIdService, new[] { EmailScope }, CertificateKey)
                    .GraphConnection().Users[MailBox].MailFolders.Inbox
                    .Messages
                    .Request()
                    .Select("id,subject,hasAttachments")
                    .Expand("attachments")
                    .GetAsync().Result;
       if (messages != null)
       {
          foreach (var message in messages)
          {
              var mimeMessage = MsGraph.MsGraphInfos(MscTenantId, ClientIdService,
                                new[] { EmailScope }, CertificateKey)
                                .GraphConnection()
                                .Users[MailBox]
                                .Messages[message.Id]
                                .Request()
                                .GetAsync().Result;
          }
       }
       return messages ?? new MailFolderMessagesCollectionPage();
    }
    catch (Exception ex)
    {
        throw new Exception($"Error in GetEmails:\r\n\r\n{ex}");
    }
            
}

public static MsGraph MsGraphInfos(string tenantId, string clientId, string[] scope, string certificatKey)
{
  return _msGraphInfos ?? (_msGraphInfos = new MsGraph(tenantId, clientId, scope,                      certificatKey));
}

public GraphServiceClient GraphConnection()
{
   if (Connection != null)
   {
      return Connection;
   }
   Connection = new GraphServiceClient("https://graph.microsoft.com/v1.0",
                new DelegateAuthenticationProvider(
                requestMessage =>
                 {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer",       Authenticate().AccessToken);
                    requestMessage.Headers.Add("Prefer", "outlook.body-content-type='text'");
                    return Task.CompletedTask;
                  }));
    return Connection;
}

private AuthenticationResult Authenticate()
{
    Scope = new[] { "https://graph.microsoft.com/.default" };
    App = ConfidentialClientApplicationBuilder.Create(ClientId)
          .WithCertificate(ReadCertificate(CertificatKey))
          .WithTenantId(TenantId)
          .Build();
    if (App != null)
    {
        Authentication = App.AcquireTokenForClient(Scope).ExecuteAsync().Result;
    }
   return Authentication;
}

private X509Certificate2 ReadCertificate(string thumbprint)
{
  X509Certificate2 cert;
  using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
  {
    store.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection certCollection = store.Certificates;
    X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now.ToUniversalTime(), false);
    X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindByThumbprint, thumbprint, false);
    cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore)
           .FirstOrDefault();
  }
    return cert;
}
Microsoft Security | Microsoft Entra | Microsoft Entra ID
{count} votes

Accepted answer
  1. SrideviM 5,715 Reputation points Microsoft External Staff Moderator
    2025-05-23T11:44:59.7266667+00:00

    Hello Roshan Ramesh,

    I understand you're trying to retrieve email content exactly as it appears, including spacing, line breaks, and formatting, but you're seeing differences when using Microsoft Graph.

    To solve this, you can fetch the email in MIME format and extract the original body using the MimeKit library. This preserves the full HTML or plain text without modifying the formatting.

    Below is a working example that retrieves recent emails and saves the content to .html or .txt files:

    using Microsoft.Graph;
    using Microsoft.Identity.Client;
    using System.Net.Http.Headers;
    using System.Security.Cryptography.X509Certificates;
    using MimeKit;
    using System;
    using System.Linq;
    
    class Program
    {
        private static readonly string tenantId = "tenantId";
        private static readonly string clientId = "appId";
        private static readonly string mailboxUser = "******@xxxxxxxxxxxxx.onmicrosoft.com";
        private static readonly string certThumbprint = "thumbprint";
    
        static async Task Main(string[] args)
        {
            var graphClient = GetGraphClient();
            var messages = await graphClient.Users[mailboxUser]
                .MailFolders["Inbox"].Messages
                .Request().Select("id,subject").Top(5).GetAsync();
    
            string outputDir = @"C:\MyMailBodies";
            System.IO.Directory.CreateDirectory(outputDir);
    
            foreach (var message in messages.CurrentPage)
            {
                var mimeStream = await graphClient.Users[mailboxUser]
                    .Messages[message.Id].Content.Request().GetAsync();
                var mime = MimeMessage.Load(mimeStream);
    
                string name = string.Join("_", (message.Subject ?? "No_Subject")
                    .Split(System.IO.Path.GetInvalidFileNameChars()));
    
                if (!string.IsNullOrWhiteSpace(mime.HtmlBody))
                    System.IO.File.WriteAllText(System.IO.Path.Combine(outputDir, $"{name}.html"), mime.HtmlBody.Trim());
                else if (!string.IsNullOrWhiteSpace(mime.TextBody))
                    System.IO.File.WriteAllText(System.IO.Path.Combine(outputDir, $"{name}.txt"), mime.TextBody.Trim());
            }
        }
    
        static GraphServiceClient GetGraphClient()
        {
            var cert = GetCertificateByThumbprint(certThumbprint);
            var app = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithCertificate(cert).WithTenantId(tenantId).Build();
    
            var provider = new DelegateAuthenticationProvider(async request =>
            {
                var token = await app.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" }).ExecuteAsync();
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);
            });
    
            return new GraphServiceClient(provider);
        }
    
        static X509Certificate2 GetCertificateByThumbprint(string thumbprint)
        {
            using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            return store.Certificates
                .Find(X509FindType.FindByThumbprint, thumbprint, false)
                .OfType<X509Certificate2>().FirstOrDefault()
                ?? throw new Exception("Certificate not found.");
        }
    }
    

    Response:

    User's image

    File Explorer:

    User's image

    Browser response:

    User's image

    This ensures the body content is preserved exactly as received, without loss of spacing or structure.

    Hope this helps!


    If this answers your query, do click Accept Answer and Yes for was this answer helpful, which may help members with similar questions.

    User's image

    If you have any other questions or are still experiencing issues, feel free to ask in the "comments" section, and I'd be happy to help.


1 additional answer

Sort by: Most helpful
  1. Roshan Ramesh 25 Reputation points
    2025-05-27T05:47:08.19+00:00

    Hello @SrideviM

    As per my requirement, i have used the regex in a customized way, which solved the issue.
    Hope this helps others too.

    I have attached the code snippet below.

                string pattern = @"(\r\n){2,}";
                string output = Regex.Replace(content, pattern, match =>
                {
                    int count = match.Length / 2; 
                    int startIndex = match.Index;
                    int endIndex = match.Index + match.Length;
                    bool precededBySlashParen = startIndex >= 2 && content.Substring(startIndex - 2, 2) == "/)";
                    bool followedByKeyword = false;
                    foreach (Match isMatch in Regex.Matches(content, $@"(?<=\r\n)({keywordsPattern})"))
                    {
                        if (isMatch.Index == endIndex)
                        {
                            followedByKeyword = true;
                            break;
                        }
                        else if (isMatch.Index > endIndex)
                        {
                            var intermediate = content.Substring(endIndex, isMatch.Index - endIndex);
                            if (string.IsNullOrWhiteSpace(intermediate))
                            {
                                followedByKeyword = true;
                                break;
                            }
                        }
                    }
                    string result = "";
                    for (int i = 1; i <= count; i++)
                    {
                        if (precededBySlashParen && i == 1)
                        {
                            result += "\r\n";
                        }
                        else if (followedByKeyword && i == count)
                        {
                            result += "\r\n";
                        }
                        else
                        {
                            result += "\r\n ";
                        }
                    }
                    return result;
                });
    
    1 person found this answer helpful.
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.