Πώς να δημιουργήσετε ένα απλό web API μετατροπής ηλεκτρονικού ταχυδρομείου στο ASP.NET Core
Δημιουργία ενός microservice ή Web API για τη μετατροπή ηλεκτρονικού ταχυδρομείου επιτρέπει σε άλλες εφαρμογές να αξιοποιήσουν τη λειτουργικότητα μετατροπής χωρίς να ενσωματώσουν ολόκληρη την βιβλιοθήκη Aspose.Email.Αυτή η προσέγγιση παρέχει μια κεντρική, κλιμακώσιμη λύση που μπορεί να εξυπηρετήσει πολλαπλούς πελάτες, εφαρμογών ή συστημάτων.Με τη χρήση του ASP.NET Κορυφής και του Asposa.email LowCode Converter, μπορείτε να δημιουργήσετε μια ισχυρή υπηρεσία RESTful που αποδέχεται τα αρχεία email μέσω HTTP upload και επιστρέφει τα μετασχηματισμένα αρχείο για λήψη.
Γιατί να δημιουργήσετε μια API μετατροπής ηλεκτρονικού ταχυδρομείου?
Μια αφιερωμένη API μετατροπής ηλεκτρονικού ταχυδρομείου προσφέρει διάφορα πλεονεκτήματα:
- Κεντρική επεξεργασία : Η ενιαία υπηρεσία χειρίζεται όλη τη λογική μετατροπής
- Cross-Platform Access : Κάθε εφαρμογή μπορεί να καταναλώνει το API μέσω HTTP
- Scalability : Ανάπτυξη ανεξάρτητα και κλίμακα με βάση την ζήτηση
- Ανεξαρτησία τεχνολογίας : Οι πελάτες δεν χρειάζονται εξαρτήσεις .NET ή Aspose.e-mail
- Microservice Architecture : Προσαρμόζει τα σύγχρονα διανεμημένα πρότυπα εφαρμογής
- Διαχείριση πόρων : Κεντρικός έλεγχος των πηγών επεξεργασίας
Προϋποθέσεις
Πριν από την κατασκευή του API μετατροπής ηλεκτρονικού ταχυδρομείου, βεβαιωθείτε ότι έχετε:
- ASP.NET Core 6.0 ή υψηλότερο περιβάλλον ανάπτυξης
- Visual Studio 2022 ή Visual Studio Code με επέκταση C
- Ασποσία.e-mail πακέτο NuGet
- Βασικές γνώσεις του σχεδιασμού RESTful API
- Η κατανόηση της φόρτωσης / λήψης αρχείων σε web APIs
Βήμα 1: Εγκατάσταση του έργου API
Δημιουργία νέου προγράμματος ASP.NET Core Web API:
dotnet new webapi -n EmailConverterApi
cd EmailConverterApi
dotnet add package Aspose.Email
dotnet add package Swashbuckle.AspNetCore
Ενεργοποιήστε το αρχείο (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>
Βήμα 2: Δημιουργήστε τον διαχειριστή μετατροπής ηλεκτρονικού ταχυδρομείου
Δημιουργήστε έναν ολοκληρωμένο διαχειριστή που χειρίζεται τις εργασίες μετατροπής ηλεκτρονικού ταχυδρομείου:
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"
};
}
}
Βήμα 3: Δημιουργήστε το In-Memory Output Handler
Δημιουργήστε έναν προσαρμοσμένο διαχειριστή εκκίνησης που καταγράφει το μετασχηματισμένο περιεχόμενο στη μνήμη:
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;
}
}
Βήμα 4: Δημιουργία μοντέλων αντίδρασης
Προσδιορίστε δομημένα μοντέλα αντίδρασης για το 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;
}
}
Βήμα 5: Προσθήκη υποστήριξης μετατροπής Batch
Επεκτείνετε τον ελεγκτή για να υποστηρίξετε τις μετατροπές συσκευών:
/// <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();
}
Βήμα 6: Ρυθμίστε την εφαρμογή
Εισάγετε την εφαρμογή με τη σωστή διαμόρφωση 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();
Βήμα 7: Ρυθμίσεις
Προσθήκη διαμόρφωσης σε appsettings.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"EmailConverterApi": "Debug"
}
},
"AllowedHosts": "*",
"MaxFileSize": 52428800,
"ConversionSettings": {
"DefaultOutputFormat": "html",
"EnableBatchConversion": true,
"MaxBatchSize": 10,
"TempDirectory": "temp"
}
}
Βήμα 8: Προσθήκη μέσων επεξεργασίας σφάλματος
Δημιουργία παγκόσμιας διαχείρισης σφαλμάτων 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>();
Βήμα 9: Παραδείγματα χρήσης πελατών
CURL Παραδείγματα
Μετατρέψτε ένα μόνο αρχείο:
curl -X POST "https://localhost:5001/api/emailconverter/convert?format=html" \
-H "Content-Type: multipart/form-data" \
-F "emailFile=@sample.eml" \
-o converted.html
Πληροφορίες για το API:
curl -X GET "https://localhost:5001/api/emailconverter/info"
C# Πελάτης Παράδειγμα
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
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();
}
Συμπεράσματα
Η δημιουργία μιας διαδικτυακής API μετατροπής ηλεκτρονικού ταχυδρομείου με ASP.NET Core και Aspose.Email LowCode Converter δημιουργεί μια ισχυρή, επαναχρησιμοποιήσιμη υπηρεσία που παρέχει:
- Κεντρική επεξεργασία : Η ενιαία υπηρεσία χειρίζεται όλες τις μετατροπές ηλεκτρονικού ταχυδρομείου
- Cross-Platform Access : Η διεπαφή με βάση το HTTP λειτουργεί με οποιαδήποτε τεχνολογία πελατών
- Scalability : Ανάπτυξη και κλίμακα ανεξάρτητα από τις εφαρμογές του πελάτη
- Πολιτικά χαρακτηριστικά : Υποστήριξη για πολλαπλές μορφές, επεξεργασία συσκευών και χειρισμό σφαλμάτων
- Production Ready : Περιλαμβάνει εγγραφή, διαμόρφωση, επικύρωση και τεκμηρίωση
Αυτή η αρχιτεκτονική API επιτρέπει στους οργανισμούς να παρέχουν δυνατότητες μετατροπής ηλεκτρονικού ταχυδρομείου ως υπηρεσία, υποστηρίζοντας πολλαπλές εφαρμογές και περιπτώσεις χρήσης, διατηρώντας παράλληλα κεντρικό έλεγχο της λογικής επεξεργασίας και των πόρων. Η προσέγγιση μικροεξυπηρέτησης διευκολύνει τις σύγχρονες δομές εφαρμογών και καθιστά δυνατή την εύκολη ολοκλήρωση με τα υπάρχοντα συστήματα και ροές εργασίας.