Moin,
in einigen Asp.NET Projekten (Sowohl MVC, als auch API) versende ich Mails, am liebsten HTML formatiert.
Gibt es einen schönen Weg HTML formatierte EMails zu erstellen?
Mein aktuelles Vorgehen:
Was mich daran stört, ist der teils ellenlange HTML-string. Man kann ihn einfach schlecht lesen.
Wenn ich eine Kleinigkeit an der Mail ändern will suche ich mich teils blöd, oder muss dann erst wieder umständlich in VS Code editieren, kopieren, usw.
Schön wäre es ja, wenn ich mit Razor arbeiten könnte.
Welche Ansätze verfolgt ihr denn so?
An die Admins, bei Mails vom Forum steht in der Signatur "Made with ASP.Net", bezieht sich das aufs Forum, oder hab ich da bei Asp.Net eine Möglichkeit übersehen Mails zu erstellen?
Danke schonmal!
Schau Dir mal folgenden Thread an Dokument mit C# und HTML
Ich erstelle die Mailvorlagen mit einem HTML Editor.
Da gibt es viele Fertige.
Grüße Bernd
Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3
Welche Ansätze verfolgt ihr denn so?
An die Admins, bei Mails vom Forum steht in der Signatur "Made with ASP.Net", bezieht sich das aufs Forum, oder hab ich da bei Asp.Net eine Möglichkeit übersehen Mails zu erstellen?
Beides.
Sowohl das Forum wie auch die Mails werden mit Hilfe von ASP.NET Core generiert. ASP.NET Core ist nur eine Framework aus vielen Bestandteilen.
Du bekommst nirgends die Vorgabe "Hier, das hier ist für E-Mails" - sondern Du kannst Dir einfach ein Baustein nehmen - zB Razor Render - und damit machen, was Du willst.
Man kann auch ASP.NET Core als Framework verwenden um Windows Services zu machen, die nachher Files überwachen. Razor (bzw. MVC) ist nur eine Middleware, die austauschbar ist.
Beim Forum eben die Views als Razor, bei den E-Mails die MailView selbst als Razor.
Dazu haben wir zwei Projekte:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
Durch die Reference auf Microsoft.AspNetCore.App kannst Du Dir einen Render Context erstellen, mit dem Du dann einfach eine Page rendern kannst.
public class RazorViewRenderProvider : IRazorViewRenderProvider
{
private readonly IRazorViewEngine _viewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
public RazorViewRenderProvider(
IRazorViewEngine viewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
public async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model)
{
ActionContext actionContext = GetActionContext();
IView view = FindView(actionContext, viewName);
using StringWriter output = new();
ViewContext viewContext = new(
actionContext,
view,
new ViewDataDictionary<TModel>(
metadataProvider: new EmptyModelMetadataProvider(),
modelState: new ModelStateDictionary())
{
Model = model
},
new TempDataDictionary(
actionContext.HttpContext,
_tempDataProvider),
output,
new HtmlHelperOptions());
await view.RenderAsync(viewContext).ConfigureAwaitFalse();
return output.ToString();
}
private IView FindView(ActionContext actionContext, string viewName)
{
ViewEngineResult vrByPath = _viewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
if (vrByPath.Success)
{
return vrByPath.View;
}
ViewEngineResult vrByContext = _viewEngine.FindView(actionContext, viewName, isMainPage: true);
if (vrByContext.Success)
{
return vrByContext.View;
}
IEnumerable<string> searchedLocations = vrByPath.SearchedLocations.Concat(vrByContext.SearchedLocations);
string errorMessage = string.Join(
Environment.NewLine,
new[] { $"Unable to find view '{viewName}'. The following locations were searched:" }.Concat(searchedLocations)); ;
throw new InvalidOperationException(errorMessage);
}
private ActionContext GetActionContext()
{
DefaultHttpContext httpContext = new() { RequestServices = _serviceProvider };
return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
}
}
Rendern geht dann einfach via
public Task<string> Render<T>(T model) where T : MailModel
=> _renderProvider.RenderViewToStringAsync($"~/Views/{model.GetType().Name}.cshtml", model);
Unser model
heisst dann einfach wie die View.
Ich mach das prinzipiell so in vielen Projekten, wo der Kunde kein MailJet, SendGrid... verwenden will.
Ansonsten würde ich immer solche Anbieter bevorzugen.
https://github.com/sebastienros/fluid ist auch ein sehr gutes Projekt und ist von den Ressourcen auch effizienter als die Razor Render Variante.
Ich spiel schon mit dem Gedanken auf Fluid umzubauen 😁
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code