diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Controllers/WeatherForecastController.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Controllers/WeatherForecastController.cs new file mode 100644 index 000000000..cad502615 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Controllers/WeatherForecastController.cs @@ -0,0 +1,57 @@ +using EmailService; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace EmailApp.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = + [ + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + ]; + + private readonly IEmailSender _emailSender; + + public WeatherForecastController(IEmailSender emailSender) + { + _emailSender = emailSender; + } + + [HttpGet(Name = "GetWeatherForecast")] + public async Task> Get() + { + var message = new Message(["codemazetest@mailinator.com"], + "Test email async", + "This is the content from our async email.", + null); + await _emailSender.SendEmailAsync(message); + + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } + + [HttpPost] + public async Task> Post() + { + var files = Request.Form.Files.Any() ? Request.Form.Files : new FormFileCollection(); + + var message = new Message(["codemazetest@mailinator.com"], "Test mail with Attachments", "This is the content from our mail with attachments.", files); + await _emailSender.SendEmailAsync(message); + + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/EmailApp.csproj b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/EmailApp.csproj new file mode 100644 index 000000000..0d96752f2 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/EmailApp.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + codemaze-send-email-mailkit + + + + + + + + + + + diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Program.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Program.cs new file mode 100644 index 000000000..8bb9d75f5 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Program.cs @@ -0,0 +1,36 @@ +using EmailService; +using Microsoft.AspNetCore.Http.Features; + +var builder = WebApplication.CreateBuilder(args); + +var emailConfig = builder.Configuration + .GetSection("EmailConfiguration") + .Get() + ?? throw new InvalidOperationException("The EmailConfiguration section is missing."); +builder.Services.AddSingleton(emailConfig); +builder.Services.AddScoped(); + +builder.Services.Configure(o => +{ + o.ValueLengthLimit = int.MaxValue; + o.MultipartBodyLengthLimit = int.MaxValue; + o.MemoryBufferThreshold = int.MaxValue; +}); + +builder.Services.AddControllers(); +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Properties/launchSettings.json b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Properties/launchSettings.json new file mode 100644 index 000000000..b0f6425ad --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5115", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7076;http://localhost:5115", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/WeatherForecast.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/WeatherForecast.cs new file mode 100644 index 000000000..395776e36 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace EmailApp; + +public class WeatherForecast +{ + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/appsettings.Development.json b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/appsettings.json b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/appsettings.json new file mode 100644 index 000000000..0493aa6b6 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailApp/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "EmailConfiguration": { + "From": "codemazetest@gmail.com", + "SmtpServer": "smtp.gmail.com", + "Port": 465, + "Username": "codemazetest@gmail.com" + }, + "AllowedHosts": "*" +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailConfiguration.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailConfiguration.cs new file mode 100644 index 000000000..25793a66e --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailConfiguration.cs @@ -0,0 +1,10 @@ +namespace EmailService; + +public class EmailConfiguration +{ + public string From { get; set; } = string.Empty; + public string SmtpServer { get; set; } = string.Empty; + public int Port { get; set; } + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailSender.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailSender.cs new file mode 100644 index 000000000..0c4f83136 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailSender.cs @@ -0,0 +1,78 @@ +using MailKit.Net.Smtp; +using MimeKit; + +namespace EmailService; + +public class EmailSender : IEmailSender +{ + private readonly EmailConfiguration _emailConfig; + + public EmailSender(EmailConfiguration emailConfig) + { + _emailConfig = emailConfig; + } + + public void SendEmail(Message message) + { + var emailMessage = CreateEmailMessage(message); + + Send(emailMessage); + } + + public async Task SendEmailAsync(Message message) + { + var mailMessage = CreateEmailMessage(message); + + await SendAsync(mailMessage); + } + + private MimeMessage CreateEmailMessage(Message message) + { + var emailMessage = new MimeMessage(); + emailMessage.From.Add(MailboxAddress.Parse(_emailConfig.From)); + emailMessage.To.AddRange(message.To); + emailMessage.Subject = message.Subject; + + var bodyBuilder = new BodyBuilder { HtmlBody = $"

{message.Content}

" }; + + if (message.Attachments != null && message.Attachments.Any()) + { + byte[] fileBytes; + foreach (var attachment in message.Attachments) + { + using (var ms = new MemoryStream()) + { + attachment.CopyTo(ms); + fileBytes = ms.ToArray(); + } + + bodyBuilder.Attachments.Add(attachment.FileName, fileBytes, ContentType.Parse(attachment.ContentType)); + } + } + + emailMessage.Body = bodyBuilder.ToMessageBody(); + return emailMessage; + } + + private void Send(MimeMessage mailMessage) + { + using var client = new SmtpClient(); + + client.Connect(_emailConfig.SmtpServer, _emailConfig.Port, true); + client.Authenticate(_emailConfig.UserName, _emailConfig.Password); + + client.Send(mailMessage); + client.Disconnect(true); + } + + private async Task SendAsync(MimeMessage mailMessage) + { + using var client = new SmtpClient(); + + await client.ConnectAsync(_emailConfig.SmtpServer, _emailConfig.Port, true); + await client.AuthenticateAsync(_emailConfig.UserName, _emailConfig.Password); + + await client.SendAsync(mailMessage); + await client.DisconnectAsync(true); + } +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailService.csproj b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailService.csproj new file mode 100644 index 000000000..becac4d24 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/EmailService.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailService/IEmailSender.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/IEmailSender.cs new file mode 100644 index 000000000..c9c4f1cb7 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/IEmailSender.cs @@ -0,0 +1,7 @@ +namespace EmailService; + +public interface IEmailSender +{ + void SendEmail(Message message); + Task SendEmailAsync(Message message); +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/EmailService/Message.cs b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/Message.cs new file mode 100644 index 000000000..1da0730ba --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/EmailService/Message.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using MimeKit; + +namespace EmailService; + +public class Message +{ + public List To { get; set; } + public string Subject { get; set; } + public string Content { get; set; } + + public IFormFileCollection? Attachments { get; set; } + + public Message(IEnumerable to, string subject, string content, IFormFileCollection? attachments) + { + To = new List(); + + To.AddRange(to.Select(x => MailboxAddress.Parse(x))); + Subject = subject; + Content = content; + Attachments = attachments; + } +} diff --git a/dotnet-client-libraries/SendEmailWithMailKit/README.md b/dotnet-client-libraries/SendEmailWithMailKit/README.md new file mode 100644 index 000000000..252146888 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/README.md @@ -0,0 +1 @@ +Source code for [How to Send Email in ASP.NET Core Web API With Attachments](https://code-maze.com/aspnetcore-send-email/). diff --git a/dotnet-client-libraries/SendEmailWithMailKit/SendEmailWithMailKit.sln b/dotnet-client-libraries/SendEmailWithMailKit/SendEmailWithMailKit.sln new file mode 100644 index 000000000..38018cdd7 --- /dev/null +++ b/dotnet-client-libraries/SendEmailWithMailKit/SendEmailWithMailKit.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailApp", "EmailApp\EmailApp.csproj", "{18F79C88-A3AF-4A1D-BA13-592A82E333A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmailService", "EmailService\EmailService.csproj", "{1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Debug|x64.Build.0 = Debug|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Debug|x86.Build.0 = Debug|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Release|Any CPU.Build.0 = Release|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Release|x64.ActiveCfg = Release|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Release|x64.Build.0 = Release|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Release|x86.ActiveCfg = Release|Any CPU + {18F79C88-A3AF-4A1D-BA13-592A82E333A4}.Release|x86.Build.0 = Release|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Debug|x64.ActiveCfg = Debug|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Debug|x64.Build.0 = Debug|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Debug|x86.ActiveCfg = Debug|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Debug|x86.Build.0 = Debug|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Release|Any CPU.Build.0 = Release|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Release|x64.ActiveCfg = Release|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Release|x64.Build.0 = Release|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Release|x86.ActiveCfg = Release|Any CPU + {1987546B-DF9F-46DD-83ED-AE7BE1FF06AC}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal