How to configure HTML Minification in ASP.NET MVC - asp.net-mvc

I would like to configure HTML minification to my ASP>NET MVC5 web application.
I installed Nuget
Install-Package WebMarkupMin.Mvc
Then I add Filter Attributte:
[MinifyHtmlAttribute]
public ActionResult Index()
{
return View();
}
But the HTML minification does not work.
Nuget Installation add few lines to the web.config:
<sectionGroup name="webMarkupMin">
<section name="core" type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
<section name="webExtensions" type="WebMarkupMin.Web.Configuration.WebExtensionsConfiguration, WebMarkupMin.Web" />
</sectionGroup>
<webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
<core>
<css>
<minifiers>
<add name="NullCssMinifier" displayName="Null CSS Minifier" type="WebMarkupMin.Core.Minifiers.NullCssMinifier, WebMarkupMin.Core" />
<add name="KristensenCssMinifier" displayName="Mads Kristensen's CSS minifier" type="WebMarkupMin.Core.Minifiers.KristensenCssMinifier, WebMarkupMin.Core" />
</minifiers>
</css>
<js>
<minifiers>
<add name="NullJsMinifier" displayName="Null JS Minifier" type="WebMarkupMin.Core.Minifiers.NullJsMinifier, WebMarkupMin.Core" />
<add name="CrockfordJsMinifier" displayName="Douglas Crockford's JS Minifier" type="WebMarkupMin.Core.Minifiers.CrockfordJsMinifier, WebMarkupMin.Core" />
</minifiers>
</js>
<html whitespaceMinificationMode="Medium" removeHtmlComments="true"
removeHtmlCommentsFromScriptsAndStyles="true"
removeCdataSectionsFromScriptsAndStyles="true"
useShortDoctype="true" useMetaCharsetTag="true"
emptyTagRenderMode="NoSlash" removeOptionalEndTags="true"
removeTagsWithoutContent="false" collapseBooleanAttributes="true"
removeEmptyAttributes="true" attributeQuotesRemovalMode="Html5"
removeRedundantAttributes="true"
removeJsTypeAttributes="true" removeCssTypeAttributes="true"
removeHttpProtocolFromAttributes="false"
removeHttpsProtocolFromAttributes="false"
removeJsProtocolFromAttributes="true"
minifyEmbeddedCssCode="true" minifyInlineCssCode="true"
minifyEmbeddedJsCode="true" minifyInlineJsCode="true"
processableScriptTypeList="" minifyKnockoutBindingExpressions="false"
minifyAngularBindingExpressions="false" customAngularDirectiveList="" />
<logging>
<loggers>
<add name="NullLogger" displayName="Null Logger" type="WebMarkupMin.Core.Loggers.NullLogger, WebMarkupMin.Core" />
<add name="ThrowExceptionLogger" displayName="Throw exception logger" type="WebMarkupMin.Core.Loggers.ThrowExceptionLogger, WebMarkupMin.Core" />
</loggers>
</logging>
</core>
</webMarkupMin>
The html element was added by me manually according to documentation.
Am I missing something?

Web application may be in debug mode. In order to switch it to release mode you need to edit the Web.config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<system.web>
<compilation debug="false" ... />
...
</system.web>
...
</configuration>
In addition, you can disable dependence on web application mode. Using the following settings:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
<webExtensions disableMinificationInDebugMode="false"
disableCompressionInDebugMode="false" />
...
</webMarkupMin>
...
</configuration>

So large library with so difficult usage and configuration... Are you sure need all this for just the HTML minification?
Create a new filter under the Filters subfolder of your project and call it CompactHtmlFilterAttribute Use the following code:
public class CompactHtmlFilterAttribute : ActionFilterAttribute
{
public class WhitespaceFilter : MemoryStream
{
private string Source = string.Empty;
private Stream Filter = null;
public WhitespaceFilter(HttpResponseBase HttpResponseBase)
{
Filter = HttpResponseBase.Filter;
}
public override void Write(byte[] buffer, int offset, int count)
{
Source = UTF8Encoding.UTF8.GetString(buffer).Replace("\r", "").Replace("\n", "").Replace("\t", "");
Filter.Write(UTF8Encoding.UTF8.GetBytes(Source), offset, UTF8Encoding.UTF8.GetByteCount(Source));
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
#if DEBUG
base.OnActionExecuting(filterContext);
#else
try
{
filterContext.HttpContext.Response.Filter = new WhitespaceFilter(filterContext.HttpContext.Response);
}
catch (Exception) { }
#endif
}
}
Pay atention on the #if DEBUG dirrective. HTML will be minified only in release configuration, while on debug the original code will be kept for the better readability.
Add this attribute to the controller methods
[CompactHtmlFilter]
public ActionResult Index()
{
return View();
}
and we're done.

You need to add the following to enable the webextensions (from the doc):
<webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
…
<webExtensions enableMinification="true" disableMinificationInDebugMode="true"
enableCompression="true" disableCompressionInDebugMode="true"
maxResponseSize="100000" disableCopyrightHttpHeaders="false" />
…
</webMarkupMin>
Note that it's outside the <core> element.
also in your view markup you should have the attribute as:
[MinifyHtml]
Itshouldn't have the ..Attribute at the end of it.

Related

Episerver Output Cache - implementing

I am trying to follow some instructions I found online in order to implement output caching for episerver.
In my web.config I have set up the following:
<caching>
<outputCache enableOutputCache="true">
</outputCache>
<outputCacheSettings>
<outputCacheProfiles>
<add name="ClientResourceCache" enabled="true" duration="3600" varyByParam="*" varyByContentEncoding="gzip;deflate" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
As a test, I selected StartPageController.cs and added the [ContentOutputCache] tag like so:
[ContentOutputCache]
public class StartPageController : PageControllerBase<StartPage>
{
public ActionResult Index(StartPage currentPage)
{
var model = PageViewModel.Create(currentPage);
if (SiteDefinition.Current.StartPage.CompareToIgnoreWorkID(currentPage.ContentLink))
{
var editHints = ViewData.GetEditHints<PageViewModel<StartPage>, StartPage>();
editHints.AddConnection(m => m.Layout.Logotype, p => p.SiteLogotype);
editHints.AddConnection(m => m.Layout.Footer, p => p.FooterBlock);
}
return View(model);
}
}
}
Then the instructions say:
At some point on a page load you need to add the following code:
public void SetResposneHeaders()
{
HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.AddMinutes(2.0));
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
}
Due to my limited knowledge of .NET, MVC etc.. this part confuses me as I am not sure in which file to place it and where? Does this go in StartPageController.cs? Or somewhere else?
Any pointers would be appreciated.
The instructions I am trying to follow are here.

EnableApiKeySupport generates error

while trying to Add bearer token using Swashbuckle swagger-document
i got this error in SwaggerConfig.cs
swaggeruiconfig does not contain definition for 'EnableApiKeySupport'
do any body know what may be the reason
Update
this is the entire swagger.config
using System.Web.Http;
using WebActivatorEx;
using Swashbuckle.Application;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace AppNameSpace
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.ApiKey("Token")
.Description("Filling bearer token here")
.Name("Authorization")
.In("header");
c.SingleApiVersion("v2", "AppNameSpace");
c.IncludeXmlComments(GetXmlCommentsPath());
})
.EnableSwaggerUi(c =>
{
c.EnableApiKeySupport("Authorization", "header");
});
}
protected static string GetXmlCommentsPath()
{
return System.String.Format(#"{0}\bin\AppNameSpace.XML", System.AppDomain.CurrentDomain.BaseDirectory);
}
}
}
This I believe is something on your project outside Swashbuckle, I created a test project with your GlobalConfiguration and it compiles without any error:
https://github.com/heldersepu/csharp-proj/tree/master/WebApi560
On that test project I'm using the following packages:
<package id="Swashbuckle" version="5.6.0" targetFramework="net452" />
<package id="Swashbuckle.Core" version="5.6.0" targetFramework="net452" />
<package id="WebActivatorEx" version="2.0" targetFramework="net452" />
You can try seeing what options are available for the SwaggerUiConfig...
in your VisualStudio project remove the EnableApiKeySupport and let the IntelliSense show you what's available, you should see something like:
If yours does not look like the one on my picture. You might be overwriting the class SwaggerUiConfig.

Getting logged out in short time in mvc application

I have an ASP.NET MVC application and I have received complaints from users that they are getting logged out after a relatively short time.
I have the following settings in web.config but apparently it does not work.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
Any idea why this is not working?
I believe you have same issue as here
So you should place machine key in your Web.Config. Ex:
<configuration>
<system.web>
<machineKey decryptionKey="F6722806843145965513817CEBDECBB1F94808E4A6C0B2F2,IsolateApps" validationKey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45,IsolateApps" />
</system.web>
</configuration>
You can learn how to generate machine key here or here
If you want user stays login until his/her browser is open, I do a trick. I create an Action or any Handler like below, it just return some dummy text (ex: context.User.Identity.IsAuthenticated), and then use jQuery.get to request this. I keeps user login because browser keep sends requests:
Handler:
public class KeepAlive : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write(context.User.Identity.IsAuthenticated);
}
public bool IsReusable
{
get
{
return false;
}
}
}
MVC Action:
public bool KeppChecking()
{
return User.Identity.IsAuthenticated;
}
jQuery:
setInterval(function () {
$.get('/Ajax/KeepAlive.ashx');
}, 10000);

Catch request to a file in content folder to handle with controller action

I got a third party widget library I have to use. This library has a hardcoded string to a file. Is it possible to intercept this request with routes? My try looked like this:
routes.MapRoute(name: "ribbonbar",
url: "Content/Ribbon/Scripts/Ribbon.Tabs.foo",
defaults: new { controller = "Ribbon", action = "Index" });
But I only got a 404. Is it impossible or do I have mixed something up?
Yes, this is possible. You will need to add the following handler to your web.config in order to ensure that this request goes through the managed pipeline and your routes:
<system.webServer>
<handlers>
...
<add
name="MyCustomhandler"
path="Content/Ribbon/Scripts/Ribbon.Tabs.foo"
verb="GET"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
and then you could have the following controller action to serve this request:
public class RibbonController
{
// GET Content/Ribbon/Scripts/Ribbon.Tabs.foo
public ActionResult Index()
{
var file = Server.MapPath("~/App_Data/foo.bar");
return File(file, "application/foo-bar");
}
}
You could also serve all requests to Content/Ribbon/Scripts/* from the same controller action:
<system.webServer>
<handlers>
...
<add
name="MyCustomhandler"
path="Content/Ribbon/Scripts/*"
verb="GET"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
and a route like that:
routes.MapRoute(
name: "ribbonbar",
url: "Content/Ribbon/Scripts/{name}",
defaults: new { controller = "Ribbon", action = "Index" }
);
with an action like that:
public class RibbonController
{
// GET Content/Ribbon/Scripts/*
public ActionResult Index(string name)
{
...
}
}
Alternatively to using a specific handler you could have enabled managed modules for all requests like that:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
...
</system.webServer>
But I wouldn't recommend you enabling this option because now all requests will go through the managed pipeline, even those from for static resources which might have a negative impact on the performance of your application. It's much better to selectively enable this only for selected urls.

HttpHandler never getting called

I have an MVC application that I'm trying to add the ManagedFusion.Web.Captcha.CaptchaImageHandler into so that I can write the following code :
<label for="captcha">Enter #Html.Raw(Business.Captcha.CaptchaImage(Html, 50, 180)) Below</label>
and have the image appear. The code for that class is just cut and paste from the examples online:
public static string CaptchaImage(this HtmlHelper helper, int height, int width) {
ManagedFusion.Web.Controls.CaptchaImage image = new ManagedFusion.Web.Controls.CaptchaImage {
Height = height,
Width = width,
};
HttpRuntime.Cache.Add(image.UniqueId, image,
null,
DateTime.Now.AddSeconds(ManagedFusion.Web.Controls.CaptchaImage.CacheTimeOut),
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
null);
StringBuilder stringBuilder = new StringBuilder(256);
stringBuilder.Append("<input type=\"hidden\" name=\"captcha-guid\" value=\"");
stringBuilder.Append(image.UniqueId);
stringBuilder.Append("\" />");
stringBuilder.AppendLine();
stringBuilder.Append("<img src=\"");
stringBuilder.Append("/captcha.ashx?guid=" + image.UniqueId);
stringBuilder.Append("\" alt=\"CAPTCHA\" width=\"");
stringBuilder.Append(width);
stringBuilder.Append("\" height=\"");
stringBuilder.Append(height);
stringBuilder.Append("\" />");
return stringBuilder.ToString();
}
I've added the following to my web.config
<system.web>
<httpHandlers>
<add verb="GET" path="test.sample" type="ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha" validate="false" />
</httpHandlers>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" >
</modules>
<handlers>
<add name="CaptchaImageHandler" verb="GET" path="captcha.ashx" type="ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha" />
</handlers>
All previous SO questions point to the system.web->httpHandlers getting picked up by Cassini and the system.webServer->handlers getting picked up by IIS 7. But whenever I navigate to the view that has the aforementioned code, I always get a 404 for the /captcha.ashx. There is no route ignore rule in the global.asax. What is going on here? It's like nothing I do will get the handler to fire either on my local machine or on a deployed IIS 7 instance.
In the Global.asax file, I needed to add the ignore route before the default route mapping, so the whole method looks like this:
public static void RegisterRoutes(RouteCollection routes) {
routes.Ignore("captcha.ashx");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}

Resources