MVC Mini Profiler includes not respecting application's path - asp.net

I've got the MVC Mini Profiler set up as described on its project page, and the includes are indeed being written on the page.
Problem is, my application sits at http://localhost:8080/web, and the markup written by the profiler includes looks like this:
<link rel="stylesheet/less" type="text/css" href="/mini-profiler-includes.less?v=2.0.4177.17902">
<script type="text/javascript" src="/mini-profiler-includes.js?v=2.0.4177.17902"></script>
<script type="text/javascript"> jQuery(function() { MiniProfiler.init({ id:'fb4dc30e-c1aa-4be6-902c-ef2812dd1fe2', renderDirection:'left' }); } ); </script>
These all of course give 404 errors, but if I navigate to /web/mini-profiler-includes.less?, it loads fine.
The source that creates that string can be found here:
// MiniProfilerHandler.cs
/// <summary>
/// Understands how to route and respond to MiniProfiler UI urls.
/// </summary>
public class MiniProfilerHandler : IRouteHandler, IHttpHandler
{
internal static HtmlString RenderIncludes(MiniProfiler profiler, RenderPosition? position = null, bool showTrivial = false, bool showTimeWithChildren = false)
{
const string format =
#"<link rel=""stylesheet/less"" type=""text/css"" href=""{0}mini-profiler-includes.less?v={1}"">
<script type=""text/javascript"" src=""{0}mini-profiler-includes.js?v={1}""></script>
<script type=""text/javascript""> jQuery(function() {{ MiniProfiler.init({{ id:'{2}', path:'{0}', renderDirection:'{3}', showTrivial: {4}, showChildrenTime: {5} }}); }} ); </script>";
var pos = position ?? (MiniProfiler.Settings.RenderPopupButtonOnRight ? RenderPosition.Right : RenderPosition.Left);
var result = profiler == null ? "" : string.Format(format,
EnsureEndingSlash(HttpContext.Current.Request.ApplicationPath),
MiniProfiler.Settings.Version,
profiler.Id,
pos.ToString().ToLower(),
showTrivial ? "true" : "false",
showTimeWithChildren ? "true" : "false");
return new HtmlString(result);
}
// rest of the code
}
Why isn't Request.ApplicationPath returning my application's path? Am I doing something wrong, or should I file an issue on the mvc-mini-profiler page?
EDIT: To make things even weirder, I put a breakpoint on the MiniProfiler.RenderIncludes() call, and checked what the value of HttpContext.Current.Request.ApplicationPath was at that moment, and it was "/web"! Very mysterious.
EDIT 2: Looks like they may have added support for virtual paths in the latest version (2 hours ago :)), and the NuGet package (which is how I installed it) is not completely up to date. Investigating...

Pulling the latest source (this commit being the most recent at time of this post), building the project, grabbing the DLL and referencing that instead of using the project's NuGet package fixed the problem.
EDIT: As of right now, the NuGet package is now up to date with the latest commit, so NuGet away!

Related

Solution for SAPUI5 error message because of templateShareable:true?

Since an upgrade of SAPUI5 1.28.20 I receive the following error message:
A shared template must be marked with templateShareable:true in the
binding info
Code is in MangedObject.js and looks like this:
} else if ( oBindingInfo.templateShareable === MAYBE_SHAREABLE_OR_NOT ) {
// a 'clone' operation implies sharing the template (if templateShareable is not set to false)
oBindingInfo.templateShareable = oCloneBindingInfo.templateShareable = true;
jQuery.sap.log.error("A shared template must be marked with templateShareable:true in the binding info");
}
Value of oBindingInfo.templateShareable is true, value of MAYBE_SHAREABLE_OR_NOT is 1.
According to documentation the default of oBindingInfo.templateShareable is true.
So what is wrong here? A bug in the library? Or something with my code?
See also: https://sapui5.netweaver.ondemand.com/sdk/#docs/api/symbols/sap.ui.base.ManagedObject.html
Update for SAPUI5 version 1.32.x
With version 1.32.x the message has changed it is now:
A template was reused in a binding, but was already marked as
candidate for destroy. You better should declare such a usage with
templateShareable:true in the binding configuration. -
but according to the documentation the default should still be true:
{boolean} oBindingInfo.templateShareable?, Default: true option to
enable that the template will be shared which means that it won't be
destroyed or cloned automatically
Now it looks like, that this produces some endless loading, I got this message again and again till the browser crashes.
Anyone an idea what could be wrong?
Looks like the message occurs if the template was instantiated outside the binding. Example:
This code will work:
new sap.m.Select({
items : {
path : "/Items",
template : new sap.ui.core.Item({
text : "{Name}"
})
}
})
This code seems to produce the message:
var oTemplate = new sap.ui.core.Item({
text : "{Name}"
})
new sap.m.Select({
items : {
path : "/Items",
template :oTemplate
}
})
This seems to fix the problem:
var oTemplate = new sap.ui.core.Item({
text : "{Name}"
})
new sap.m.Select({
items : {
path : "/Items",
template :oTemplate,
templateShareable : true
}
})
The answer above marked as being correct is actually not correct at all because this here is wrong:
Looks like the message occurs if the template was instantiated outside
the binding. [...] This code seems to produce the message: [...]
To prove the answer above os wrong on your own just run this example (SAPUI5 1.28.20 has the same result):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SAPUI5 single file template | nabisoft</title>
<script
src="https://openui5.hana.ondemand.com/1.36.12/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-libs="sap.m"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"></script>
<!-- use "sync" or change the code below if you have issues -->
<script>
sap.ui.getCore().attachInit(function () {
"use strict";
var oModel = new sap.ui.model.json.JSONModel({
Items: [
{Name: "Michael"},
{Name: "John"},
{Name: "Frank"},
{Name: "Jane"}
]
});
sap.ui.getCore().setModel(oModel);
var oTemplate = new sap.ui.core.Item({
text : "{Name}"
});
new sap.m.Select({
items : {
path : "/Items",
template : oTemplate
}
}).placeAt("content");
});
</script>
</head>
<body class="sapUiBody">
<div id="content"></div>
</body>
</html>
Basically, a clear definition of the lifecycle for templates is (or was) missing in UI5. When this issue was detected there were already many older apps around... So now this new "feature" was introduced somewhen last year (which is kind of backwards compatible). UI5 tries to detect automatically the developer's intention about the lifecycle of a given template used in the binding (using some heuristic). If UI5 cannot clearly tell what the developer actually wanted then you will see this error log - which actually does not affect the functionality at all. It just tells the developer that there is a template somewhere which will not be destroyed by the UI5 runtime. In other words, if you set templateShareable=true then you should make sure to destroy the template in order to avoid memory leaks. So just setting templateShareable=true is not the whole story...
I have published a detailed blog about this: Understanding templateShareable in SAPUI5

jsdom does not fetch scripts on local file system

This is how i construct it:
var fs = require("fs");
var jsdom = require("jsdom");
var htmlSource = fs.readFileSync("./test.html", "utf8");
var doc = jsdom.jsdom(htmlSource, {
features: {
FetchExternalResources : ['script'],
ProcessExternalResources : ['script'],
MutationEvents : '2.0'
},
parsingMode: "auto",
created: function (error, window) {
console.log(window.b); // always undefined
}
});
jsdom.jQueryify(doc.defaultView, 'https://code.jquery.com/jquery-2.1.3.min.js', function() {
console.log( doc.defaultView.b ); // undefined with local jquery in html
});
the html:
<!DOCTYPE HTML>
<html>
<head></head>
<body>
<script src="./js/lib/vendor/jquery.js"></script>
<!-- <script src="http://code.jquery.com/jquery.js"></script> -->
<script type="text/javascript">
var a = $("body"); // script crashes here
var b = "b";
</script>
</body>
</html>
As soon as i replace the jquery path in the html with a http source it works. The local path is perfectly relative to the working dir of the shell / actual node script. To be honest i don't even know why i need jQueryify, but without it the window never has jQuery and even with it, it still needs the http source inside the html document.
You're not telling jsdom where the base of your website lies. It has no idea how to resolve the (relative) path you give it (and tries to resolve from the default about:blank, which just doesn't work). This also the reason why it works with an absolute (http) URL, it doesn't need to know where to resolve from since it's absolute.
You'll need to provide the url option in your initialization to give it the base url (which should look like file:///path/to/your/file).
jQuerify just inserts a script tag with the path you give it - when you get the reference in the html working, you don't need it.
I found out. I'll mark Sebmasters answer as accepted because it solved one of two problems. The other cause was that I didn't properly wait for the load event, thus the code beyond the external scripts wasn't parsed yet.
What i needed to do was after the jsdom() call add a load listener to doc.defaultView.
The reason it worked when using jQuerify was simply because it created enough of a timeout for the embedded script to load.
I had the same issue when full relative path of the jquery library to the jQueryify function. and I solved this problem by providing the full path instead.
const jsdom = require('node-jsdom')
const jqueryPath = __dirname + '/node_modules/jquery/dist/jquery.js'
window = jsdom.jsdom().parentWindow
jsdom.jQueryify(window, jqueryPath, function() {
window.$('body').append('<div class="testing">Hello World, It works')
console.log(window.$('.testing').text())
})

Using java script code in MVC5 - where to put it

Im having MVC5 application and in the view index.cshtml I need to use
some java script code ,currently I put the script code inside the view and its working fine.
My question is where should I put this code (from best practice) and how should
I refer to it from the view?please provide an example.
The approach I've written down below is my way of extracting JavaScript completely from your views.
better to maintain (js issues -> look in js files and not in views)
modular approach
clear separation
better to understand by design
In HTML5, use the data attribute to pass along variables from the Model.
This helps tremendously in porting variables from MVC (your viewmodel) to javascript.
This also allows you to keep javaScript stored in separate files as you probably would like in an MVC environment.
1.1 Binding c# to HTML
<div class="news" data-js-params="websiteName=#LocationWebsiteHelper.CurrentLocationWebsiteName()&languageName=#languageName&page=0&itemsPerPage=#Model.MaxNumberOfItems">
1.2 JS Helper functions to convert data into object literals
Although built on jQuery, I've written 2 small functions which can help porting querystring variables into object literals and back. I use these throughout my js files:
// #param (qs): a query string of key value pairs (without ?)
// #param (keyDelimiter): string : character between values and keys
// #param (valDelimiter): string : character between keys and values
// #return (obj): an object literal
// #example: key1=val1&key2=val2&key3=val3
convertQsToLiteral: function (qs, keyDelimiter, valDelimiter) {
var arrParams, obj = {};
if (qs && qs.length) {
keyDelimiter = keyDelimiter || '&';
valDelimiter = valDelimiter || '=';
arrParams = qs.split(keyDelimiter);
$.each(arrParams, function (i, pair) {
var arrPair = pair.split(valDelimiter),
key = arrPair[0],
val = arrPair[1];
obj[key] = val;
});
}
return obj;
},
// #param (literal): an object literal key value paired of one level deep
// #param (keyDelimiter): string character between values and keys
// #param (valDelimiter): string : character between keys and values
// #return (string): array string representation
// #example: { key1: val1, key2: val2, key3: val3 }
convertLiteralToQs: function (literal, keyDelimiter, valDelimiter) {
var arrQs = [],
arrPairs, key;
keyDelimiter = keyDelimiter || '&';
valDelimiter = valDelimiter || '=';
for (key in literal) {
if (literal.hasOwnProperty(key)) {
arrPairs = [];
arrPairs.push(key, literal[key]);
arrQs.push(arrPairs.join(valDelimiter));
}
}
return arrQs.join(keyDelimiter);
},
1.3 Convert HTML data into js object literals
With these functions in mind you can pass any query string like variables into an object literal.
var dataParams = convertQsToLiteral($('.news').data('js-params')); // get data attr
var urlParams = convertQsToLiteral(window.location.search.substr(1)); // get url query string
1.4 Example: JS modular setup to extend and override object literals
Combined with jQuery's $.extend() function you can now override javascript objects in a modular approach (considering all closures a js file/module looks like this):
window.ProjectName = (function($, projectname){
// default object literal
var cfg = {
// your default options
idea: 'great'
};
// #param (options): something like the cfg object
projectname.Module = function (options) {
this.settings = $.extend(true, {}, cfg, options); // deep copy
this.init();
};
projectname.Module.prototype = {
init: function(){
this.idea = this.settings.idea;
console.log(this.idea);
}
};
return projectname;
}(window.jQuery, window.ProjectName));
1.5 Initializing a js module
var module = new ProjectName.Module({ idea: 'even better' });
2.1 Adding scripts/css to your views
You have a couple options for attaching scripts to your views/pages/blocks:
section defined in the baselayout (only for partial views, directly included into the baselayout)
c# ClientResources (not the best approach in MVC but still doable, allows you to include external files into a partial view -> view in view)
bundles (good or minification and modular approach)
2.2.1 baselayout setup for sections
#RenderSection("AdditionalJS", false)
2.2.2 usage partial view
#section AdditionalJS
{
<script>
var module = new ProjectName.Module({ idea: #Model.idea });
</script>
}
2.3.1 baselayout setup for view in view
#Html.Raw(Html.RequiredClientResources(RenderingTags.Header))
2.3.2 usage view in view
ClientResources.RequireScript("/Design/js/projectname.module.js").AtHeader();
2.4.1 BundleConfig setup for scripts
/// <summary>
/// Register the Javascript bundles
/// Separated in libJs, projectJs and polyfillJs
/// </summary>
/// <param name="bundles"></param>
private static void RegisterScripts(BundleCollection bundles)
{
// usage for libraries
bundles.Add(new ScriptBundle(
"~/bundles/libJs").Include(
"~/Design/js/lib/*.js"
));
// project object
bundles.Add(new ScriptBundle(
"~/bundles/projectJs").Include(
"~/Design/js/project.dev.js",
"~/Design/js/classes/*.js",
"~/Design/js/components/*.js"
));
// usage for browser support
bundles.Add(new ScriptBundle(
"~/bundles/polyfillJs").Include(
"~/Design/js/polyfills/*.js"
));
}
/// <summary>
/// Render scripts inside conditional comments
/// http://stackoverflow.com/questions/12865939/mvc4-bundling-minification-with-ie-conditional-comments
/// </summary>
/// <param name="ie"></param>
/// <param name="paths"></param>
/// <returns></returns>
public static IHtmlString RenderConditionalScripts(string ie, params string[] paths)
{
var tag = string.Format("<!--[if {0}]>{1}<![endif]-->", ie, Scripts.Render(paths));
return new MvcHtmlString(tag);
}
2.4.2 baselayout setup
...
<head>
...
#BundleConfig.RenderConditionalScripts("lte IE 9", "~/bundles/polyfillJs")
#Scripts.Render("~/bundles/libJs")
<head>
<body>
...
#Scripts.Render("~/bundles/projectJs")
</body>
You better use Razor #section for this.
In your Layout:
<html>
<head>
<title>My Title</title>
.....
#RenderSection("Scripts", false)
</head>
<body>
#RenderBody
</body>
</html>
In your View:
<div id="container">
<h3>Welcome!</h3>
...
</div>
#section Scripts
{
<script type="text/javascript">
// your script goes here
</script>
}
Some would prefer to place #RenderSection("Scripts") just before the </body> tag instead.
If you have and HTML 5 template it really doesn't matter where you place the JavaScript code, If you haver a XHTML template you should put your code inside the <head></head> tags.
Now with the best practices, right now the best practice is to put all your JS code just before your </body> closing tag. This way you make sure that your html elements tags have been parse by the browser.
When going to a production environment the best is to concatenate all your JS into a single .js file and then have it minify, that way you would have an only small js file that the client browser needs to fetch.
Minify your code
The term minify in frontend code (css/js) stands for a process where you trim all your spaces and line breaks, and also the function scope variables get replace with a shorter name, usually just a vowel.
function foo(someVar){
return someVar;
}
when minified gets replaced to:
function foo(e){return e}
In MVC4 there was the Bundling and Minification feature that could help you with this. In MVC5 I'm not that sure.
Further Reading: http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

Excluding bootstrap from specific routes in Meteor

I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)

Ajax.ActionLink not working, Response.IsAjaxRequest() is always false

I have been googling/SO:ing this issue for a while and many seem to be sharing this, but I haven't found any successful solution to my problem.
Using MVC3 and Razor.
Master page contains:
<script src="#Url.Content("~/Scripts/jquery-1.5.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
AjaxTest.cshtml contains:
<div id="AjaxTestDiv">content</div>
#Ajax.ActionLink("Update", "AjaxTester", new AjaxOptions { UpdateTargetId = "AjaxTestDiv" })
AjaxTester action method:
public string AjaxTester()
{
if (Request.IsAjaxRequest())
{
return DateTime.Now.ToString();
}
else
{
return "FAIL";
}
}
I always get the "FAIL" returned, to a blank page, not in the targeted div.
Edit: Also note that if I remove the if (Request.IsAjaxRequest()), I still don't get back anything to the targeted div, but instead a blank page.
Edit2: Looking at the HTML generated, this is my link:
<a data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace"
data-ajax-update="#AjaxTestDiv" href="/Area/AjaxTester">Update</a>
Have tried switching the method to GET, to no avail.
By default ASP.NET MVC 3 uses unobtrusive jquery with all the Ajax.* helpers. So start by getting rid off all MicrosoftAjax scripts (this useless c**p) and put the following instead:
<script src="#Url.Content("~/Scripts/jquery-1.5.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
and then simply activate unobtrusive AJAX in your web.config (if not already done):
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
Now jquery is going to unobtrusively AJAXify all the links containing those HTML 5 data-* attributes.
Or even better IMHO:
In your view simply:
#Html.ActionLink("Update", "AjaxTester", new { id = "mylink" })
and in a separate javascript file AJAXify this anchor:
$(function() {
$('#mylink').click(function() {
$('#AjaxTestDiv').load(this.href);
return false;
});
});
Another IE-specific issue that can keep ActionLink from functioning correctly is covered here: ASP.NET MVC - Prevent Cache on Ajax.ActionLinks
Basically, IE sometimes caches Ajax GET requests, so setting the AjaxOption's HttpMethod to POST is a less fragile way to construct an ActionLink:
#Ajax.ActionLink(
item.Name + " (Ajax link test)",
"MyActionName",
routeValues: new { id = item.Id },
ajaxOptions: new AjaxOptions()
{
UpdateTargetId = "divToUpdate",
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST"
})
For those still facing this issue:
You should have below files in your project somewhere:
If not, below is the nuget package, either download this from nuget manager in your visual studio project or package manager console:
https://www.nuget.org/packages/Microsoft.jQuery.Unobtrusive.Validation/3.2.11/
Give reference of above added file after the jquery.min.js latest version in either your respective view or layout file:
Add the jquery.unobtrusive-ajax.js package in your project and it should work in the expected way.
Include the current script form
CDN: https://www.cdnpkg.com/jquery-ajax-unobtrusive
<script src="#Url.Content("https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
or install package
NuGet:
https://www.nuget.org/packages/Microsoft.jQuery.Unobtrusive.Ajax/3.2.6
and apply installed bundle.
Note that jquery.unobtrusive-ajax.js and jquery.validate.unobtrusive.js are not equal.
In my case this package was not installed and ajax.action link worked like html.actionLink and rendered entire view instead of inserting partial view in the target div.

Resources