ASP.NET MVC - Custom Cache Busting Key - c#

Supposedly, when a file included in a bundle is modified, the url key is supposed to be updated, forcing the client to clear its cache.
However, I have found this to be a very unreliable process - for some reason, the URL key is not always changed even when there are many changes to the underlying files.
So what I wanted to do was to use the assembly version in the query string so that whenever a release occurred, all clients would clear their cache so that they would be updated to the most recent version. Here is what I have so far:
Custom transform to modify query variables:
public class VersionBusterTransform : IBundleTransform
{
public void Process(BundleContext context, BundleResponse Response)
{
string query = string.Format("{0}.{1}.{2}.{3}", Global.Properties.Version.Major, Global.Properties.Version.Minor, Global.Properties.Version.Release, Global.Properties.Version.Build);
Array.ForEach(Response.Files.ToArray(), x => x.IncludedVirtualPath = string.Concat(x.IncludedVirtualPath, "?v=", query));
}
}
Registering the files:
BundleTable.EnableOptimizations = (Global.Properties.Deployment.Environment != DeploymentEnvironment.Development);
var StyleLibrary = new StyleBundle(ConfigBundles.Styles);
StyleLibrary.Include("~/Content/Styles/Libraries/core-{version}.css", new CssRewriteUrlTransform());
StyleLibrary.Include("~/Content/Styles/Libraries/icomoon/style.css", new CssRewriteUrlTransform());
StyleLibrary.Include("~/Content/Styles/Site.css", new CssRewriteUrlTransform());
StyleLibrary.Transforms.Add(new VersionBusterTransform());
BundleTable.Bundles.Add(StyleLibrary);
var ScriptLibrary = new ScriptBundle(ConfigBundles.Scripts);
ScriptLibrary.Include("~/Content/Scripts/Libraries/modernizr-{version}.js");
ScriptLibrary.Include("~/Content/Scripts/Libraries/bootstrap-{version}.js");
ScriptLibrary.Include("~/Content/Scripts/Framework/yeack.js");
ScriptLibrary.Transforms.Add(new JsMinify());
ScriptLibrary.Transforms.Add(new VersionBusterTransform());
BundleTable.Bundles.Add(ScriptLibrary);
Method to get URL:
public static string Query(string Path)
{
if (HttpRuntime.Cache[Path] == null)
{
var absolutePath = HostingEnvironment.MapPath(Path);
HttpRuntime.Cache.Insert(Path, string.Format("{0}?v={1}.{2}.{3}.{4}", Path, Global.Properties.Version.Major, Global.Properties.Version.Minor, Global.Properties.Version.Release, Global.Properties.Version.Build), new CacheDependency(absolutePath));
}
return HttpRuntime.Cache[Path] as string;
}
Header template:
#Styles.Render(ConfigBundles.Styles)
#Scripts.Render(ConfigBundles.Scripts)
Currently, when I open the site in the Development environment, the following references are printed in the header:
<link href="/Content/Styles/Libraries/core-3.1.0.css?v=0.3.5.0" rel="stylesheet">
<link href="/Content/Styles/Libraries/icomoon/style.css?v=0.3.5.0" rel="stylesheet">
<link href="/Content/Styles/Site.css?v=0.3.5.0" rel="stylesheet">
<script src="/Content/Scripts/Libraries/modernizr-2.6.2.js?v=0.3.5.0"></script>
<script src="/Content/Scripts/Libraries/bootstrap-3.0.0.js?v=0.3.5.0"></script>
<script src="/Content/Scripts/Framework/yeack.js?v=0.3.5.0"></script>
However, in Production, it is printing this:
<link href="/Content/Styles/css?v=SmKL_qNLRzByCaBc0zE--HPqJmwlxxsS9p8GL7jtFsc1" rel="stylesheet">
<script src="/Content/Scripts/js?v=YvUa47U8N_htaVmoq5u1VzHyRgEH3quFSUYpjRonpbM1"></script>
Why doesn't my bundling transform have any effect on what is printed in production?

Related

How to pass generic list of values to external js file from controller/view in ASP.NET MVC

The file name is called clik.js here I have to access some values from controller/view
Using view I have tried this code:
<head>
#if (ViewBag.status != null)
{
<script type="text/javascript">
var tagAccess='#ViewBag.status[0]';
</script>
<script src="~/JavaScript/click.js"></script>
}
</head>
Using this code, I could get single value in js file. But I have to get all values
//var tagAccess='#ViewBag.status';
If I write the code like this, I didn't get any output
You can serialize the Item using this:
<script>
var tagAccess = #Html.Raw(Json.Serialize(#ViewBag.status[0]));
</script>

How to show text in PDF generated by Azure function

I am trying to generate a PDF via Azure function using DinkToPdf. This what I have done so far.
[FunctionName("GeneratePdf")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log,
ExecutionContext executionContext)
{
string name = await GetName(req);
return CreatePdf(name, executionContext);
}
private static ActionResult CreatePdf(string name, ExecutionContext executionContext)
{
var globalSettings = new GlobalSettings
{
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
Margins = new MarginSettings { Top = 10 },
};
var objectSettings = new ObjectSettings
{
PagesCount = true,
WebSettings = { DefaultEncoding = "utf-8" },
HtmlContent = $#"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
Hello, ${name}
</body>
</html>",
};
var pdf = new HtmlToPdfDocument()
{
GlobalSettings = globalSettings,
Objects = { objectSettings }
};
byte[] pdfBytes = IocContainer.Resolve<IConverter>().Convert(pdf);
return new FileContentResult(pdfBytes, "application/pdf");
}
This is working pretty good, when I am testing the function on local. However, it is not working as expected when deployed to Azure.
The primary problem is that in the places of the texts in the pdf, boxes are appearing (see below for example).
Moreover the response is also excruciatingly slow. Is there a way to improve/correct this?
Additional Info:
I am also using unity IOC to resolve IConverter. The type registration looks something like below:
var container = new UnityContainer();
container.RegisterType<IConverter>(
new ContainerControlledLifetimeManager(),
new InjectionFactory(c => new SynchronizedConverter(new PdfTools()))
);
I have tried couple of other NuGet packages such as PdfSharp, MigraDoc, Select.HtmlToPdf.NetCore, etc. But alll of those have dependency on System.Drawing.Common, which is not usable in Azure function.
The issue appears to be related to the restrictions of the Azure Function in "Consumption" mode. If you use "App Mode", it should work.
See the discussion below this Gist for some users who had success converting their Azure Function to "App Mode".

angularjs-datetime-picker formatting from Json object

I asked the question to generic so i made a specific case:
Cant implement https://www.npmjs.com/package/angularjs-datetime-picker
I have a class Employee with a List of timeRegistrations.
<link href="~/Content/Angular-Datetimepicker/angularjs-datetime-picker.css" rel="stylesheet" />
<script src="~/Content/Angular-Datetimepicker/angularjs-datetime-picker.min.js"></script>
I added this to my Module
var app;
(function () { app = angular.module( "WorksheetEdit", ['angularjs-datetime-picker']);
I have the following in my HTML:
This is inside a repeater.
<input datetime-picker date-format="yyyy-MM-dd HH:mm:ss" ng-model="timeregistration.StartDate" />
I'm using a $http.get() to return the data from C# controller.
To serialise i'm using :
string isoJson = JsonConvert.SerializeObject(lst);
return Content(isoJson, "application/json");
The problem here seems to be i cant format the date.
It keeps this notation: 2018-02-01T00:00:00

OAuth 2.0 access using javascript

I want to insert, update data from the fusion tables.
While selecting from the fusion table all seems to work fine. But during row addition i need to used OAuth 2.0 but unable to find a suitable solution to get the access token and use it during the insert.
A code sample would help a lot.
var fusiondata;
function initialize() {
// Initialize JSONP request
var script = document.createElement('script');
var url = ['https://www.googleapis.com/fusiontables/v1/query?'];
url.push('sql=');
var query = 'insert into 1bPbx7PVJU9NaxgAGKqN2da4g5EbXDybE_UVvlAE (name,luckynumber) values('abc',89)';
var encodedQuery = encodeURIComponent(query);
url.push(encodedQuery);
url.push('&callback=viewData');
url.push('&key=AIzaSyA0FVy-lEr_MPGk1p_lHSrxGZDcxy6wH4o');
script.src = url.join('');
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);
}
function viewData(data) {
// code not required
}
I know most of you are suffering for google auth and inserting and updating fusion table. I am providing the entire code how to use the gauth lib to insert in a simple manner
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Authorization Request</title>
<script src="https://apis.google.com/js/client.js"></script>
<script type="text/javascript">
function auth() {
var config = {
'client_id': '365219651081-7onk7h52kas6cs5m17t1api72ur5tcrh.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/fusiontables'
};
gapi.auth.authorize(config, function() {
console.log('login complete');
console.log(gapi.auth.getToken());
});
}
function insert_row(){
alert("insert called");
gapi.client.setApiKey('AIzaSyA0FVy-lEr_MPGk1p_lHSrxGZDcxy6wH4o');
var query = "INSERT INTO 1T_qE-o-EtX24VZASFDn6p3mMoPcWQ_GyErJpPIc(Name, Age) VALUES ('Trial', 100)";
gapi.client.load('fusiontables', 'v1', function(){
gapi.client.fusiontables.query.sql({sql:query}).execute(function(response){console.log(response);});
});
}
</script>
</head>
<body>
<button onclick="auth();">Authorize</button>
<p> </p>
<button onclick="insert_row();">Insert Data</button>
</body>
</html>

Add CSS references to page's <head> from a partial view

Is there a way to add CSS references to a page from a partial view, and have them render in the page's <head> (as required by the HTML 4.01 spec)?
If you're using MVC3 & Razor, the best way to add per-page items to your section is to:
1) Call RenderSection() from within your layout page
2) Declare a corresponding section within your child pages:
/Views/Shared/_Layout.cshtml:
<head>
<!-- ... Rest of your head section here ... ->
#RenderSection("HeadArea")
</head>
/Views/Entries/Index.cshtml:
#section HeadArea {
<link rel="Stylesheet" type="text/css" href="/Entries/Entries.css" />
}
The resultant HTML page then includes a section that looks like this:
<head>
<!-- ... Rest of your head section here ... ->
<link rel="Stylesheet" type="text/css" href="/Entries/Entries.css" />
<head>
You could also use the Telerik open source controls for MVC and do something like :
<%= Html.Telerik().StyleSheetRegistrar()
.DefaultGroup(group => group
.Add("stylesheet.css"));
in the head section
and
<%= Html.Telerik().ScriptRegistrar()
.DefaultGroup(group => group
.Add("script.js"));
in the script section at the botttom of your page.
And you can keep adding scripts on any view , or partial view and they should work.
If you don't want to use the component you can always inspire yourself from there and do something more custom.
Oh, with Telerik you also have options of combining and compressing the scripts.
You could have the partial view load in a javascript block that drops in the style to the head, but that would be silly considering that you probably want the javascript block in the head section for the same reason.
I recently discovered something pretty cool though. You can serialize a partial view into a string and send it back to the client as part of a JSON object. This enables you to pass other parameters as well, along with the view.
Returning a view as part of a JSON object
You could grab a JSON object with JQuery and ajax and have it loaded with the partial view, and then another JSON property could be your style block. JQuery could check if you returned a style block, if so then drop it into the head section.
Something like:
$.ajax(
{
url: "your/action/method",
data: { some: data },
success: function(response)
{
$('#partialViewContainer).html(response.partialView);
if (response.styleBlock != null)
$('head').append(response.styleBlock);
}
});
You can use a HttpModule to manipulate the response HTML and move any CSS/script references to the appropriate places. This isn't ideal, and I'm not sure of the performance implications, but it seems like the only way to resolve the issue without either (a) a javascript-based solution, or (b) working against MVC principles.
Another approach, which defeats the principles of MVC is to use a ViewModel and respond to the Init-event of your page to set the desired css/javascript (ie myViewModel.Css.Add(".css") and in your head render the content of the css-collection on your viewmodel.
To do this you create a base viewmodel class that all your other models inherits from, ala
public class BaseViewModel
{
public string Css { get; set; }
}
In your master-page you set it to use this viewmodel
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<BaseViewModel>" %>
and your head-section you can write out the value of the Css property
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
<%= Model.Css %>
</head>
Now, in your partial view you need to have this code, which is kinda ugly in MVC
<script runat="server">
protected override void OnInit(EventArgs e)
{
Model.Css = "hej";
base.OnInit(e);
}
</script>
The following would work only if javascript were enabled. it's a little helper that i use for exactly the scenario you mention:
// standard method - renders as defined in as(cp)x file
public static MvcHtmlString Css(this HtmlHelper html, string path)
{
return html.Css(path, false);
}
// override - to allow javascript to put css in head
public static MvcHtmlString Css(this HtmlHelper html,
string path,
bool renderAsAjax)
{
var filePath = VirtualPathUtility.ToAbsolute(path);
HttpContextBase context = html.ViewContext.HttpContext;
// don't add the file if it's already there
if (context.Items.Contains(filePath))
return null;
// otherwise, add it to the context and put on page
// this of course only works for items going in via the current
// request and by this method
context.Items.Add(filePath, filePath);
// js and css function strings
const string jsHead = "<script type='text/javascript'>";
const string jsFoot = "</script>";
const string jsFunctionStt = "$(function(){";
const string jsFunctionEnd = "});";
string linkText = string.Format("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\"></link>", filePath);
string jsBody = string.Format("$('head').prepend('{0}');", linkText);
var sb = new StringBuilder();
if (renderAsAjax)
{
// join it all up now
sb.Append(jsHead);
sb.AppendFormat("\r\n\t");
sb.Append(jsFunctionStt);
sb.AppendFormat("\r\n\t\t");
sb.Append(jsBody);
sb.AppendFormat("\r\n\t");
sb.Append(jsFunctionEnd);
sb.AppendFormat("\r\n");
sb.Append(jsFoot);
}
else
{
sb.Append(linkText);
}
return MvcHtmlString.Create( sb.ToString());
}
usage:
<%=Html.Css("~/content/site.css", true) %>
works for me, tho as stated, only if javascript is enabled, thus limiting its usefulness a little.

Categories