Wie man eine einfache E-Mail Conversion Web API in ASP.NET Core baut
Das Erstellen einer Microservice oder Web API für die E-Mail-Konvertierung ermöglicht es anderen Anwendungen, die Conversion-Funktionalität zu nutzen, ohne die gesamte Aspose.Email-Bibliothek zu integrieren.Dieser Ansatz bietet eine zentralisierte, skalierbare Lösung, welche mehrere Clients, Applikationen oder Systeme dienen kann.Mit ASP.NET Core und dem Asposa.email LowCode Converter können Sie einen robusten RESTful-Service bauen, der e-Mails über HTTP-Upload-Dateien akzeptiert und konvertierte Dateien zum Download zurückgibt.
Warum eine E-Mail Conversion API bauen?
Eine dedizierte E-Mail-Konvertierung API bietet mehrere Vorteile:
- Zentralisierte Verarbeitung : Einheitliche Dienstleistungen verwalten alle Konvertierungslogik
- Cross-Platform Access : Jede Anwendung kann die API über HTTP verbrauchen
- Scalability : Unabhängig und nach Nachfrage basierend auf Skalierung
- Technology Independence : Kunden brauchen keine Abhängigkeiten von .NET oder Aspose.Email
- Microservice Architecture : Passend für moderne verteilte Anwendungsmuster
- Resource Management : Zentralisierte Kontrolle über Verarbeitungsressourcen
Voraussetzung
Bevor Sie die E-Mail Conversion API erstellen, stellen Sie sicher, dass Sie:
- ASP.NET Core 6.0 oder höher Entwicklungsumfeld
- Visual Studio 2022 oder Visual Studio Code mit C#-Erweiterungen
- Aspose.Email NuGet Paket
- Grundkenntnisse von RESTful API Design
- Verständnis der Datei-Upload/Download in Web APIs
Schritt 1: Setup des API-Projekts
Erstellen eines neuen ASP.NET Core Web API-Projekts:
dotnet new webapi -n EmailConverterApi
cd EmailConverterApi
dotnet add package Aspose.Email
dotnet add package Swashbuckle.AspNetCore
Erstellen der Projektdatei (EmailConverterApi.csproj
):
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspose.Email" Version="24.3.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
</Project>
Schritt 2: Erstellen des E-Mail Converter Controller
Erstellen Sie einen umfassenden Controller, der E-Mail-Konvertierungsoperationen verwalten:
using Aspose.Email.LowCode;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
namespace EmailConverterApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class EmailConverterController : ControllerBase
{
private readonly ILogger<EmailConverterController> _logger;
private readonly IConfiguration _configuration;
private readonly long _maxFileSize;
private readonly string[] _allowedExtensions = { ".eml", ".msg" };
private readonly string[] _supportedOutputFormats = { "html", "eml", "msg", "mhtml", "mht" };
public EmailConverterController(ILogger<EmailConverterController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
_maxFileSize = _configuration.GetValue<long>("MaxFileSize", 10 * 1024 * 1024); // 10MB default
}
/// <summary>
/// Converts an email file to the specified format
/// </summary>
/// <param name="emailFile">The email file to convert (EML or MSG)</param>
/// <param name="format">Output format (html, eml, msg, mhtml, mht)</param>
/// <param name="filename">Optional custom filename for the output</param>
/// <returns>The converted file for download</returns>
[HttpPost("convert")]
[ProducesResponseType(typeof(FileResult), 200)]
[ProducesResponseType(typeof(ApiErrorResponse), 400)]
[ProducesResponseType(typeof(ApiErrorResponse), 500)]
public async Task<IActionResult> ConvertEmail(
[Required] IFormFile emailFile,
[FromQuery, Required] string format = "html",
[FromQuery] string? filename = null)
{
var requestId = Guid.NewGuid().ToString("N")[..8];
_logger.LogInformation("Starting conversion request {RequestId} for file: {FileName}, format: {Format}",
requestId, emailFile?.FileName, format);
try
{
// Validate request
var validationResult = ValidateRequest(emailFile, format);
if (validationResult != null)
{
_logger.LogWarning("Validation failed for request {RequestId}: {Error}", requestId, validationResult.Message);
return validationResult;
}
// Create in-memory output handler
var outputHandler = new MemoryOutputHandler();
// Perform conversion
using var inputStream = emailFile!.OpenReadStream();
await ConvertEmailInternal(inputStream, emailFile.FileName, outputHandler, format);
// Get converted content
var convertedContent = outputHandler.GetContent();
var outputFileName = filename ?? GenerateOutputFileName(emailFile.FileName, format);
var contentType = GetContentType(format);
_logger.LogInformation("Successfully converted {RequestId}: {InputFile} -> {OutputFile} ({Size} bytes)",
requestId, emailFile.FileName, outputFileName, convertedContent.Length);
// Return file for download
return File(convertedContent, contentType, outputFileName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Conversion failed for request {RequestId}: {Error}", requestId, ex.Message);
return StatusCode(500, new ApiErrorResponse
{
Message = "Internal server error during conversion",
RequestId = requestId
});
}
}
/// <summary>
/// Gets information about supported formats and API capabilities
/// </summary>
[HttpGet("info")]
[ProducesResponseType(typeof(ApiInfoResponse), 200)]
public IActionResult GetApiInfo()
{
return Ok(new ApiInfoResponse
{
SupportedInputFormats = _allowedExtensions,
SupportedOutputFormats = _supportedOutputFormats,
MaxFileSizeMB = _maxFileSize / (1024 * 1024),
Version = "1.0.0",
Description = "Email format conversion API powered by Aspose.Email"
});
}
/// <summary>
/// Health check endpoint
/// </summary>
[HttpGet("health")]
[ProducesResponseType(typeof(HealthResponse), 200)]
public IActionResult HealthCheck()
{
return Ok(new HealthResponse
{
Status = "Healthy",
Timestamp = DateTime.UtcNow,
Version = "1.0.0"
});
}
private IActionResult? ValidateRequest(IFormFile? emailFile, string format)
{
if (emailFile == null || emailFile.Length == 0)
{
return BadRequest(new ApiErrorResponse { Message = "No file provided or file is empty" });
}
if (emailFile.Length > _maxFileSize)
{
return BadRequest(new ApiErrorResponse
{
Message = $"File size exceeds maximum allowed size of {_maxFileSize / (1024 * 1024)}MB"
});
}
var extension = Path.GetExtension(emailFile.FileName)?.ToLowerInvariant();
if (string.IsNullOrEmpty(extension) || !_allowedExtensions.Contains(extension))
{
return BadRequest(new ApiErrorResponse
{
Message = $"Unsupported file format. Allowed formats: {string.Join(", ", _allowedExtensions)}"
});
}
if (string.IsNullOrEmpty(format) || !_supportedOutputFormats.Contains(format.ToLowerInvariant()))
{
return BadRequest(new ApiErrorResponse
{
Message = $"Unsupported output format. Supported formats: {string.Join(", ", _supportedOutputFormats)}"
});
}
return null;
}
private static async Task ConvertEmailInternal(Stream inputStream, string fileName, IOutputHandler outputHandler, string format)
{
switch (format.ToLowerInvariant())
{
case "html":
await Converter.ConvertToHtml(inputStream, fileName, outputHandler);
break;
case "eml":
await Converter.ConvertToEml(inputStream, fileName, outputHandler);
break;
case "msg":
await Converter.ConvertToMsg(inputStream, fileName, outputHandler);
break;
case "mhtml":
await Converter.ConvertToMhtml(inputStream, fileName, outputHandler);
break;
case "mht":
await Converter.ConvertToMht(inputStream, fileName, outputHandler);
break;
default:
throw new ArgumentException($"Unsupported conversion format: {format}");
}
}
private static string GenerateOutputFileName(string inputFileName, string format)
{
var nameWithoutExtension = Path.GetFileNameWithoutExtension(inputFileName);
return $"{nameWithoutExtension}.{format.ToLowerInvariant()}";
}
private static string GetContentType(string format) => format.ToLowerInvariant() switch
{
"html" => "text/html",
"eml" => "message/rfc822",
"msg" => "application/vnd.ms-outlook",
"mhtml" => "message/rfc822",
"mht" => "message/rfc822",
_ => "application/octet-stream"
};
}
}
Schritt 3: Erstellen des In-Memory Output Handlers
Erstellen Sie einen benutzerdefinierten Output-Handler, der konvertierte Inhalte in Speicher erfährt:
using Aspose.Email.LowCode;
using System.Text;
namespace EmailConverterApi.Services
{
/// <summary>
/// Output handler that captures converted content in memory for API responses
/// </summary>
public class MemoryOutputHandler : IOutputHandler
{
private byte[]? _content;
private string? _fileName;
public async Task AddOutputStream(string name, Func<Stream, Task> writeAction)
{
_fileName = name;
using var memoryStream = new MemoryStream();
await writeAction(memoryStream);
_content = memoryStream.ToArray();
}
public void AddOutputStream(string name, Action<Stream> writeAction)
{
_fileName = name;
using var memoryStream = new MemoryStream();
writeAction(memoryStream);
_content = memoryStream.ToArray();
}
public byte[] GetContent()
{
return _content ?? throw new InvalidOperationException("No content has been written to this handler");
}
public string GetContentAsString()
{
var content = GetContent();
return Encoding.UTF8.GetString(content);
}
public string GetFileName()
{
return _fileName ?? throw new InvalidOperationException("No file name available");
}
public bool HasContent => _content != null && _content.Length > 0;
}
}
Schritt 4: Erstellen von Reaktionsmodellen
Definieren Sie strukturierte Reaktionsmodelle für die API:
namespace EmailConverterApi.Models
{
/// <summary>
/// Standard error response model
/// </summary>
public class ApiErrorResponse
{
public string Message { get; set; } = string.Empty;
public string? RequestId { get; set; }
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
public string? Details { get; set; }
}
/// <summary>
/// API information response model
/// </summary>
public class ApiInfoResponse
{
public string[] SupportedInputFormats { get; set; } = Array.Empty<string>();
public string[] SupportedOutputFormats { get; set; } = Array.Empty<string>();
public long MaxFileSizeMB { get; set; }
public string Version { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
/// <summary>
/// Health check response model
/// </summary>
public class HealthResponse
{
public string Status { get; set; } = string.Empty;
public DateTime Timestamp { get; set; }
public string Version { get; set; } = string.Empty;
public Dictionary<string, object>? AdditionalInfo { get; set; }
}
/// <summary>
/// Conversion request model for batch operations
/// </summary>
public class BatchConversionRequest
{
public string OutputFormat { get; set; } = "html";
public bool ZipOutput { get; set; } = true;
public string? CustomPrefix { get; set; }
}
/// <summary>
/// Batch conversion result
/// </summary>
public class BatchConversionResult
{
public int TotalFiles { get; set; }
public int SuccessfulConversions { get; set; }
public int FailedConversions { get; set; }
public List<ConversionError> Errors { get; set; } = new();
public string? OutputFileName { get; set; }
}
/// <summary>
/// Individual conversion error details
/// </summary>
public class ConversionError
{
public string FileName { get; set; } = string.Empty;
public string ErrorMessage { get; set; } = string.Empty;
}
}
Schritt 5: Add Batch Conversion Support
Erweitern Sie den Controller, um Batch-Konvertierungen zu unterstützen:
/// <summary>
/// Converts multiple email files and returns them as a ZIP archive
/// </summary>
/// <param name="emailFiles">Multiple email files to convert</param>
/// <param name="request">Batch conversion settings</param>
/// <returns>ZIP file containing all converted emails</returns>
[HttpPost("convert-batch")]
[ProducesResponseType(typeof(FileResult), 200)]
[ProducesResponseType(typeof(ApiErrorResponse), 400)]
[ProducesResponseType(typeof(ApiErrorResponse), 500)]
public async Task<IActionResult> ConvertEmailBatch(
[Required] IFormFileCollection emailFiles,
[FromForm] BatchConversionRequest request)
{
var requestId = Guid.NewGuid().ToString("N")[..8];
_logger.LogInformation("Starting batch conversion request {RequestId} for {FileCount} files, format: {Format}",
requestId, emailFiles.Count, request.OutputFormat);
try
{
if (!emailFiles.Any())
{
return BadRequest(new ApiErrorResponse { Message = "No files provided for batch conversion" });
}
if (!_supportedOutputFormats.Contains(request.OutputFormat.ToLowerInvariant()))
{
return BadRequest(new ApiErrorResponse
{
Message = $"Unsupported output format: {request.OutputFormat}"
});
}
var results = new List<(string FileName, byte[] Content, bool Success, string? Error)>();
// Process each file
foreach (var file in emailFiles)
{
try
{
var validationResult = ValidateRequest(file, request.OutputFormat);
if (validationResult != null)
{
results.Add((file.FileName, Array.Empty<byte>(), false, "Validation failed"));
continue;
}
var outputHandler = new MemoryOutputHandler();
using var inputStream = file.OpenReadStream();
await ConvertEmailInternal(inputStream, file.FileName, outputHandler, request.OutputFormat);
results.Add((file.FileName, outputHandler.GetContent(), true, null));
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to convert file {FileName} in batch {RequestId}", file.FileName, requestId);
results.Add((file.FileName, Array.Empty<byte>(), false, ex.Message));
}
}
// Create ZIP archive
var zipContent = CreateZipArchive(results, request);
var zipFileName = $"converted_emails_{DateTime.Now:yyyyMMdd_HHmmss}.zip";
_logger.LogInformation("Batch conversion {RequestId} completed: {Success}/{Total} successful",
requestId, results.Count(r => r.Success), results.Count);
return File(zipContent, "application/zip", zipFileName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Batch conversion failed for request {RequestId}: {Error}", requestId, ex.Message);
return StatusCode(500, new ApiErrorResponse
{
Message = "Internal server error during batch conversion",
RequestId = requestId
});
}
}
private static byte[] CreateZipArchive(List<(string FileName, byte[] Content, bool Success, string? Error)> results, BatchConversionRequest request)
{
using var zipStream = new MemoryStream();
using (var archive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Create, true))
{
// Add successful conversions
foreach (var (fileName, content, success, _) in results.Where(r => r.Success))
{
var outputFileName = GenerateOutputFileName(fileName, request.OutputFormat);
if (!string.IsNullOrEmpty(request.CustomPrefix))
{
outputFileName = $"{request.CustomPrefix}_{outputFileName}";
}
var entry = archive.CreateEntry(outputFileName);
using var entryStream = entry.Open();
entryStream.Write(content, 0, content.Length);
}
// Add error log if there were failures
var failures = results.Where(r => !r.Success).ToList();
if (failures.Any())
{
var errorLog = string.Join("\n", failures.Select(f => $"{f.FileName}: {f.Error}"));
var errorEntry = archive.CreateEntry("conversion_errors.txt");
using var errorStream = errorEntry.Open();
using var writer = new StreamWriter(errorStream);
writer.Write(errorLog);
}
}
return zipStream.ToArray();
}
Schritt 6: Konfigurieren der Anwendung
Setzen Sie die Anwendung mit der richtigen Konfiguration in Program.cs
:
using EmailConverterApi.Services;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Email Converter API",
Version = "v1",
Description = "RESTful API for converting email files between different formats using Aspose.Email",
Contact = new OpenApiContact
{
Name = "API Support",
Email = "support@example.com"
}
});
// Include XML comments if available
var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
});
// Configure file upload limits
builder.Services.Configure<IISServerOptions>(options =>
{
options.MaxRequestBodySize = 50 * 1024 * 1024; // 50MB
});
// Add CORS if needed
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
// Add logging
builder.Logging.AddConsole();
builder.Logging.AddDebug();
var app = builder.Build();
// Configure pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Email Converter API V1");
c.RoutePrefix = string.Empty; // Swagger at root
});
}
app.UseHttpsRedirection();
app.UseCors("AllowAll");
app.UseAuthorization();
app.MapControllers();
app.Run();
Schritt 7: Konfiguration Settings
Konfiguration hinzufügen appsettings.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"EmailConverterApi": "Debug"
}
},
"AllowedHosts": "*",
"MaxFileSize": 52428800,
"ConversionSettings": {
"DefaultOutputFormat": "html",
"EnableBatchConversion": true,
"MaxBatchSize": 10,
"TempDirectory": "temp"
}
}
Schritt 8: Add Error Handling Middleware
Erstellen globaler Fehlerbehandlung middleware:
using System.Net;
using System.Text.Json;
namespace EmailConverterApi.Middleware
{
public class GlobalErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalErrorHandlingMiddleware> _logger;
public GlobalErrorHandlingMiddleware(RequestDelegate next, ILogger<GlobalErrorHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred");
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
var (statusCode, message) = exception switch
{
ArgumentException => (HttpStatusCode.BadRequest, exception.Message),
FileNotFoundException => (HttpStatusCode.NotFound, "Requested file not found"),
UnauthorizedAccessException => (HttpStatusCode.Unauthorized, "Access denied"),
NotSupportedException => (HttpStatusCode.BadRequest, "Operation not supported"),
_ => (HttpStatusCode.InternalServerError, "An error occurred while processing your request")
};
context.Response.StatusCode = (int)statusCode;
var response = new
{
message = message,
statusCode = (int)statusCode,
timestamp = DateTime.UtcNow
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
}
// Register middleware in Program.cs
app.UseMiddleware<GlobalErrorHandlingMiddleware>();
Schritt 9: Kundenanwendungsbeispiele
CURL Beispiele
Konvertieren einer einzelnen Datei:
curl -X POST "https://localhost:5001/api/emailconverter/convert?format=html" \
-H "Content-Type: multipart/form-data" \
-F "emailFile=@sample.eml" \
-o converted.html
Erhalten Sie API Informationen:
curl -X GET "https://localhost:5001/api/emailconverter/info"
C# Kundenbeispiele
public class EmailConverterClient
{
private readonly HttpClient _httpClient;
public EmailConverterClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<byte[]> ConvertEmailAsync(string filePath, string outputFormat = "html")
{
using var form = new MultipartFormDataContent();
using var fileStream = File.OpenRead(filePath);
using var fileContent = new StreamContent(fileStream);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
form.Add(fileContent, "emailFile", Path.GetFileName(filePath));
var response = await _httpClient.PostAsync($"/api/emailconverter/convert?format={outputFormat}", form);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
}
JavaScript Client Beispiel
async function convertEmail(file, format = 'html') {
const formData = new FormData();
formData.append('emailFile', file);
const response = await fetch(`/api/emailconverter/convert?format=${format}`, {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`Conversion failed: ${response.statusText}`);
}
return await response.blob();
}
Schlussfolgerungen
Bauen Sie eine E-Mail-Konvertierung Web API mit ASP.NET Core und Aspose.Email LowCode Converter schafft einen leistungsfähigen, wiederverwendbaren Service, der bietet:
- Zentralisierte Verarbeitung : Einheitliche Dienste verwalten alle E-Mail-Konvertierungen
- Cross-Platform Access : HTTP-basierte Schnittstelle funktioniert mit jeder Client-Technologie
- Scalability : Entwickeln und Skalieren unabhängig von Kundenanwendungen
- Comprehensive Features : Unterstützung für mehrere Formate, Batchverarbeitung und Fehlerbehandlung
- Produktion Ready : Inbegriffen Logging, Konfiguration, Validierung und Dokumentation
Diese API-Architektur ermöglicht es Organisationen, E-Mail-Konvertierungsfähigkeiten als Dienst zu bieten, mehrere Anwendungen und Benutzungsfälle zu unterstützen, während zentralisierte Kontrolle über Verarbeitungslogik und Ressourcen beibehalten wird. Der Microservice-Ansatz erleichtert moderne Anwendungskonstruktionen und erlaubt eine einfache Integration mit bestehenden Systemen und Workflows.