Taritsyn / WebMarkupMin

The Web Markup Minifier (abbreviated WebMarkupMin) - a .NET library that contains a set of markup minifiers. The objective of this project is to improve the performance of web applications by reducing the size of HTML, XHTML and XML code.
Apache License 2.0
449 stars 48 forks source link

Support Razor Pages #135

Closed zuhry closed 2 years ago

zuhry commented 2 years ago

Hi.

May I know if you have a plan to include Razor Pages on supported files lists?

Taritsyn commented 2 years ago

Hello!

Now nothing prevents you from using the middleware from the WebMarkupMin.AspNetCore3, WebMarkupMin.AspNetCore5 or WebMarkupMin.AspNetCore6 module. You just need to call the UseWebMarkupMin method before the MapRazorPages method.

Also, do not forget to configure the WebMarkupMin ASP.NET Core Extensions for the development environment.

zuhry commented 2 years ago

Thanks for your answer.

but unfortunately, I can not see it works for my projects. there is nothing wrong with your extension. also I have reproduced it with a new Razor Pages project and it works. therefore, don't have any idea why the extension can't work on my project.

If you don't mind, please see and correct my startup code below: ` public void ConfigureServices(IServiceCollection services) { // Add framework services. services.Configure(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => false; options.MinimumSameSitePolicy = SameSiteMode.None; });

        services.AddHttpContextAccessor();

        services.AddWebMarkupMin(
                options =>
                {
                    options.AllowMinificationInDevelopmentEnvironment = true;
                    options.AllowCompressionInDevelopmentEnvironment = true;
                    options.DisablePoweredByHttpHeaders = true;
                })
            .AddHtmlMinification(
                options =>
                {
                    options.MinificationSettings.RemoveRedundantAttributes = true;
                    options.MinificationSettings.RemoveHttpProtocolFromAttributes = true;
                    options.MinificationSettings.RemoveHttpsProtocolFromAttributes = true;
                })
            .AddHttpCompression();

        services.AddMvc();

        services.AddMvcCore();

        services.AddControllersWithViews();

        services
            .AddRazorPages()
            .AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);

        services
            .AddMemoryCache()
            .AddSession(s =>
            {
                s.Cookie.Name = $"{Utils.COMPANY_NAME}.{Utils.SUITE_NAME}.{(AppSettings.IsProduction.Equals(true) ? "PRD" : "TRN")}";
                s.IdleTimeout = TimeSpan.FromDays(1);
            });

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
        {
            builder
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowAnyOrigin();
        }));
        services.AddControllers()
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            });

        services.AddSingleton(Configuration);
        services.AddDbContext<AppIdentityContext>();
        services.AddIdentity<IdentityUser, IdentityRole>()
           .AddEntityFrameworkStores<AppIdentityContext>()
           .AddDefaultTokenProviders();

        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie()
            .AddFacebook(FBOptions =>
            {
                var authParamList = AppSettings.ExternalServices.FacebookOAuthParamList;
                if (authParamList != null)
                {
                    FBOptions.AppId = authParamList.FirstOrDefault(w => w.Name.Equals("AppId"))?.Value;
                    FBOptions.AppSecret = authParamList.FirstOrDefault(w => w.Name.Equals("AppSecret"))?.Value;
                    FBOptions.Events = new OAuthEvents()
                    {
                        OnAccessDenied = ctx =>
                        {
                            ctx.Response.Redirect("/Error?errCode=403&provider=facebook");
                            ctx.HandleResponse();
                            return Task.CompletedTask;
                        }
                    };
                }
            }).AddGoogle(GOptions =>
           {
               var authParamList = AppSettings.ExternalServices.GoogleOAuthParamList;
               if (authParamList != null)
               {
                   GOptions.ClientId = authParamList.FirstOrDefault(w => w.Name.Equals("ClientId"))?.Value;
                   GOptions.ClientSecret = authParamList.FirstOrDefault(w => w.Name.Equals("ClientSecret"))?.Value;
                   GOptions.Events = new OAuthEvents()
                   {
                       OnAccessDenied = ctx =>
                       {
                           ctx.Response.Redirect("/Error?errCode=403&provider=google");
                           ctx.HandleResponse();
                           return Task.CompletedTask;
                       }
                   };
               }
           });

        services.AddLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConfiguration(Configuration.GetSection("Logging"));
            logging.AddEventLog(new EventLogSettings
            {
                LogName = $"{Utils.COMPANY_NAME}.{Utils.SUITE_NAME} Log",
                SourceName = $"{Utils.COMPANY_NAME}.{Utils.SUITE_NAME}"
            });
            logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Information);
            logging.AddConsole();

if DEBUG

            logging.AddDebug();

endif

        });

        services.AddDevExpressControls();      

        PrintingSystemXmlSerializer.UnregisterConverter(typeof(Font));
        PrintingSystemXmlSerializer.RegisterConverter(new CustomFontConverter());
        Thread.CurrentThread.CurrentCulture = ApplicationInfo.DefaultCultureInfo();
        CultureInfo.DefaultThreadCurrentCulture = ApplicationInfo.DefaultCultureInfo();
        CultureInfo.DefaultThreadCurrentUICulture = ApplicationInfo.DefaultCultureInfo();
        string contentPath = CurrentEnvironment.ContentRootPath;
        AccessSettings.StaticResources.TrySetRules(DirectoryAccessRule.Allow(contentPath));
        AccessSettings.StaticResources.TrySetRules(DirectoryAccessRule.Allow(Path.Combine(contentPath, "Data"), Path.Combine(contentPath, "wwwroot\\images")));
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory logFactory)
    {
        //var reportingLogger = logFactory.CreateLogger("DXReporting");
        //DevExpress.XtraReports.Web.ClientControls.LoggerService.Initialize((exception, message) =>
        //{
        //    var logMessage = $"[{DateTime.Now}]: Exception occurred. Message: '{message}'. Exception Details:\r\n{exception}";
        //    reportingLogger.LogError(logMessage);
        //});
        //DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions;

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error?errCode={0}");

            app.UseStatusCodePagesWithRedirects("/Error?errCode={0}");

            app.UseStatusCodePagesWithReExecute("/Error?errCode={0}");
        }

        app.UseCors();

        app.UseHttpsRedirection();

        app.UseStaticFiles();

        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "node_modules")),
            RequestPath = "/node_modules"
        });

        app.UseRouting();

        app.UseAuthorization();

        app.UseSession();

        app.UseAuthentication(); 

        app.UseWebMarkupMin();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });

        AppContext.Configure(app.ApplicationServices
            .GetRequiredService<IHttpContextAccessor>());

        app.UseDevExpressControls();

        app.UseMiddleware<GCMiddleware>();

        Logger.LoggerFactory = logFactory;
    }`

Thanks in advance.

Taritsyn commented 2 years ago

Hello!

It is possible that there are syntax errors in HTML output. To check this, use the ThrowExceptionLogger:

…
using IWmmLogger = WebMarkupMin.Core.Loggers.ILogger;
using WmmThrowExceptionLogger = WebMarkupMin.Core.Loggers.ThrowExceptionLogger;

…
    public void ConfigureServices(IServiceCollection services)
    {
        …
        // Override the default logger for WebMarkupMin.
        services.AddSingleton<IWmmLogger, WmmThrowExceptionLogger>();

        services.AddMvc();
        …
    }
…
zuhry commented 2 years ago

You are correct. After I implement the logger. this error appears:

MarkupMinificationException: Category: JS_TEMPLATE_MINIFICATION_ERROR Message: During parsing of HTML-code error has occurred. File: /Flight/Search Line number: 88 Column number: 64 Source fragment: Line 87: … </script><script type="text/html" id="… Line 88: … <% if(typeof IconUrl !== 'undefined' && IconUr… ---------------------------------------------------------^ Line 89: … <span class="<%- IconUrl %>"></span>

also I have tried add this line: options.MinificationSettings.MinifyEmbeddedJsCode = false; options.MinificationSettings.MinifyInlineJsCode = false;

but still no luck. it seems extension still does not support JS Templating. or only on my project because I am using devexpress control. or perhaps I am missing something in my configuration?

here is the control that will cause error: @(Html.DevExtreme().SelectBox() .DataSource(d => d.Mvc().Controller("GenericValues").LoadAction("GetListLangCode").LoadMode(DataSourceLoadMode.Raw)) .DisplayExpr("Text") .ValueExpr("Value") .DropDownOptions(opt=> opt.Width("auto")) .Value(langCode) .WrapItemText(false) .FieldTemplate(@<text> <% if(typeof IconUrl !== 'undefined' && IconUrl) { %> <span class="<%- IconUrl %>"></span> <% } %> <span class="ml-2"><%- Text %> </span> @(Html.DevExtreme().TextBox() .Value(new JS("typeof Text !== 'undefined' && Text")) .ReadOnly(true) .Visible(false) .Width("auto") ) </text>) .ItemTemplate(@<text> <% if(typeof IconUrl !== 'undefined' && IconUrl) { %> <span class="<%- IconUrl %>"></span> <% } %> <%- Text %> </text>).ElementAttr("class", "border-0") .OnContentReady("onListLangCodeContentReady") .OnValueChanged(@"function(e) { changeLangCode(e.value)}") )

Taritsyn commented 2 years ago

Can you give a full example of HTML output of this control?

Taritsyn commented 2 years ago

Try to use the following setting:

            …
            .AddHtmlMinification(
                options =>
                {
                    ...
                    options.MinificationSettings.ProcessableScriptTypeList = "";
                })
            …
zuhry commented 2 years ago

Great. it works now.

But if any anchor with URL has this char "-" I will face this error

`MarkupMinificationException: Category: HTML_PARSING_ERROR Message: In the start tag found invalid characters. File: /Flight/Search Line number: 1212 Column number: 194 Source fragment:

Line 1211: Line 1212: …>Privacy Policy

  • <a href="/Contact")">Contact Us
  • -----------------------------------------------------------^`

    this is caused by this string "<a href="/Misc/Privacy-Policy">Privacy Policy" anyway. thanks a lot for your help.

    Taritsyn commented 2 years ago

    Most likely this error is here and is caused by an extra unescaped double quote:

    <a href="/Contact")">Contact Us
    zuhry commented 2 years ago

    yes. you are correct again. everything works now. thanks for your support 👍

    Taritsyn commented 2 years ago

    Hello!

    To quickly check a HTML code for syntax errors, you can use the WebMarkupMin Online HTML Minifier.

    I also recommend replacing the ThrowExceptionLogger with your own implementation that will using your logging framework.