Well you can do that,
One way would be to make "your own" html tag, (more like an extension method on an existing) and implement TagHelper
You can access or alter the content when you override, I have a class that extends a couple of html tags to manage custome globalisation, have a look at the code and see if this helps you.
In your pages you can "take ownership" of a html by adding your attribute like i demonstrate here with my attribute cats-language-key :
<div cats-language-key="Home-S2-h1-p1">
</p>
[HtmlTargetElement("p",Attributes = CatsLanguageKey)]
[HtmlTargetElement("span", Attributes = CatsLanguageKey)]
[HtmlTargetElement("a", Attributes = CatsLanguageKey)]
[HtmlTargetElement("li", Attributes = CatsLanguageKey)]
[HtmlTargetElement("h1", Attributes = CatsLanguageKey)]
[HtmlTargetElement("h2", Attributes = CatsLanguageKey)]
[HtmlTargetElement("h3", Attributes = CatsLanguageKey)]
[HtmlTargetElement("h4", Attributes = CatsLanguageKey)]
[HtmlTargetElement("div", Attributes = CatsLanguageKey)]
public class LanguageTagHelper: TagHelper
{
private const string CatsLanguageKey= "cats-language-key";
private readonly ILanguageRepository _repository;
private readonly ClaimsPrincipal _user;
private readonly IMemoryCache _memoryCache;
public LanguageTagHelper(ILanguageRepository repository, IHttpContextAccessor context, IMemoryCache memoryCache)
{
_repository = repository;
_user = context.HttpContext.User;
_memoryCache = memoryCache;
}
[HtmlAttributeName(CatsLanguageKey)]
public string Key { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var childContent = await output.GetChildContentAsync();
if (!childContent.IsEmptyOrWhiteSpace)
{
var textItem = _repository.GetHtml(Key, childContent.GetContent().Trim());
if (_user.Identity.IsAuthenticated && _user.IsInRole(MagicStrings.ROLE_TEXTER))
{
output.Attributes.Add("data-language-target", textItem.Language);
output.Attributes.Add("data-language-key", textItem.Key);
var html = new HtmlString(textItem.Text);
output.Content.SetHtmlContent(html);
_memoryCache.Remove(Key);
}
else
{
string text = string.Empty;
if (!_memoryCache.TryGetValue(Key, out text))
{
text = Regex.Replace(textItem.Text, @">\s+<", "><", RegexOptions.Compiled | RegexOptions.Multiline);
text = Regex.Replace(text, @"<!--(?!\s*(?:\[if [^\]]+]|<!|>))(?:(?!-->)(.|\n))*-->", "", RegexOptions.Compiled | RegexOptions.Multiline);
text = Regex.Replace(text, @"^\s+", "", RegexOptions.Compiled | RegexOptions.Multiline);
text = Regex.Replace(text, @"\r\n?|\n", "", RegexOptions.Compiled | RegexOptions.Multiline);
text = Regex.Replace(text, @"\s+", " ", RegexOptions.Compiled | RegexOptions.Multiline);
_memoryCache.Set(Key, text, new MemoryCacheEntryOptions() { Priority= CacheItemPriority.Low, SlidingExpiration= new TimeSpan(hours:1,minutes:0,seconds:0) });
}
var html = new HtmlString(text);
output.Content.SetHtmlContent(html);
}
}
}
}
Another item I had to do where I changed the whole page was by adding some capabilitiesto my MidleWare. We noted that the HTML returned by our pages was rather bloated with empty strings and padding where it was not needed, I then minified the page (leaving the spaces and line feeds in the JavaScript)
public static class BuilderExtensions
{
public static IApplicationBuilder UseHTMLMinification(this IApplicationBuilder app)
{
return app.UseMiddleware<HtmlMinificationMiddleware>();
}
public static IApplicationBuilder UseHTMLMinification(this IApplicationBuilder app,
string excludeFilter)
{
var options = new HtmlMinificationOptions() { ExcludeFilter = excludeFilter };
return app.UseMiddleware<HtmlMinificationMiddleware>(options);
}
public static IApplicationBuilder UseHTMLMinification(this IApplicationBuilder app,
HtmlMinificationOptions minificationOptions)
{
return app.UseMiddleware<HtmlMinificationMiddleware>(minificationOptions);
}
///so other options
}
The HtmlMinificationMiddleware looks like this, note you will not have the StatsRepository but you can edit that out of the sample or replace it with your own, the StatsRepository maintains page statistics buch more detailed than google does without all the privacy law exposure that comes with googles AddSence or the AWStats and is real-time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Threading.Tasks;
using System.Text;
using System.Text.RegularExpressions;
namespace CATS.Web.Shared.Infrastructure.Middleware
{
using CATS.Web.Shared.Repositories;
public class HtmlMinificationMiddleware
{
private RequestDelegate _next;
StatsRepository _stats;
private HtmlMinificationOptions _minificationOptions;
public HtmlMinificationMiddleware(RequestDelegate next, StatsRepository stats)
: this(next, null, stats)
{
}
public HtmlMinificationMiddleware(RequestDelegate next, HtmlMinificationOptions minificationOptions, StatsRepository stats)
{
_next = next;
_minificationOptions = minificationOptions;
_stats = stats;
}
public async Task Invoke(HttpContext context)
{
var stream = context.Response.Body;
if (_minificationOptions != null)
{
var filter = _minificationOptions.ExcludeFilter;
if (Regex.IsMatch(context.Request.Path, filter))
{
await _next(context);
return;
}
}
long size = 0;
try
{
using (var buffer = new MemoryStream())
{
context.Response.Body = buffer;
await _next(context);
var isHtml = context.Response.ContentType?.ToLower().Contains("text/html");
buffer.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(buffer))
{
string responseBody = await reader.ReadToEndAsync();
var backup = string.Copy(responseBody);
if (context.Response.StatusCode == 200 && isHtml.GetValueOrDefault())
{
try
{
responseBody = Regex.Replace(responseBody, @">\s+<", "><", RegexOptions.Compiled | RegexOptions.Multiline);
responseBody = Regex.Replace(responseBody, @"<!--(?!\s*(?:\[if [^\]]+]|<!|>))(?:(?!-->)(.|\n))*-->", "", RegexOptions.Compiled | RegexOptions.Multiline);
responseBody = Regex.Replace(responseBody, @"\r\n?|\n", "", RegexOptions.Compiled | RegexOptions.Multiline);
responseBody = Regex.Replace(responseBody, @"\s+", " ", RegexOptions.Compiled | RegexOptions.Multiline);
if (string.IsNullOrWhiteSpace(responseBody))
responseBody = backup;
} catch
{
responseBody = backup;
}
}
var bytes = Encoding.UTF8.GetBytes(responseBody);
using (var memoryStream = new MemoryStream(bytes))
{
memoryStream.Seek(0, SeekOrigin.Begin);
await memoryStream.CopyToAsync(stream);
}
size = bytes.LongLength;
await _stats.UpdateRequestSize(context, size);
}
}
}
finally
{
context.Response.Body = stream;
}
}
}
}
public class HtmlMinificationOptions
{
public string ExcludeFilter { get; set; }
}
The Pipeline configurations I did like this:
namespace CATS.Web.Shared.Infrastructure.Middleware
{
using Microsoft.AspNetCore.Builder;
public class HtmlMinificationPipeline
{
public void Configure(IApplicationBuilder applicationBuilder)
{
applicationBuilder.UseHTMLMinification();
}
}
}
So, I gave you 2 options 1 at tag level say a div, the other basically as big as you like.