Why is my custom HTML Helper result getting html encoded? - asp.net-mvc-3

I've got the following custom html helper in asp.net mvc 3
public static string RegisterJS(this HtmlHelper helper, ScriptLibrary scriptLib)
{
return "<script type=\"text/javascript\"></script>\r\n";
}
The problem is that the result is getting html encoded like so (I had to add spaces to get so to show the result properly:
<script type="text/javascript"></script>
This obviously isn't much help to me.. Nothing I've read says anything about this.. any thoughts on how I can get my real result back?

You're calling the helper in a Razor # block or an ASPX <%: %> block.
These constructs automatically escape their output.
You need to change the helper to return an HtmlString, which will not be escaped:
return new HtmlString("<script ...");

Related

Adding a model attribute to the html template without encoding

I am using spring boot(ver. 1.1.1.RELEASE) and trying to add a string model attribute in a html template.
the Controller:
#RequestMapping({"/", ""})
public String template(Model model) {
model.addAttribute("coolStuff", coolStuff);
return "panel/index";
}
the HTML template:
<script type="text/javascript" th:inline="text">
/*<![CDATA[*/
[[${coolStuff}]]
/*]]>*/
</script>
thymeleaf's th:inline in mode "text" worked very well for that before, but now it is adding HTML encoding (escaping characters) to the provided string.
th:inline in mode "javascript" escapes double quotes so that is not working eihter.
Is there any way to put a string from a model attribute without encoding in the html template?
You can use th:utext to disable Thymeleaf's escaping but I'm not aware of a way to use that in combination with th:inline. I think you can still achieve what you want, but you'd have to change the value of coolStuff to contain the entirety of the <script> block.

Using MVC3, how to get browsers to explicitly interpret a transferred script as HTML?

In my MVC app, I am returning some Javascript. Howveer, I am using the anti-forgery token on the view, so the rendered result would be
<input name="__RequestVerificationToken" type="hidden" value="E8as+4Ff1u/c/+kuFcNXXCREB5pz5GAfH2krN5RvzURJaHZSApuRc4czZqmoITaKdy0XhN5sFfRzl4ne+wB3PkWOscBWzoIxUk3hGaFwDxRXSbMs8K9IwojEAtV5u57MR7hiSujr6MOTpjjbf5FPaYgO4gmH6lSR9mbSyO2IedI=" />
<script type="text/javascript">
// Here, we ensure that jQuery is loaded then load up the rest of our JS in in order.
ord = Math.random() * 10000000000000000;
...
So there is some HTML to be added to the page then the JS.
The issue is that I get the following notification in Chrome:
Resource interpreted as Script but transferred with MIME type
I need the browser to interpret this as HTML in order to make use of the anti-forgery token.
I have tried putting this on the view:
<%#Page Title="" Language="C#" ContentType="text/xml" %>
Which renders:
<%System.Web.WebPages.DynamicPageDataDictionary`1[System.Object] Title="" Language="C#" ContentType="text/xml" %>
<input name="__RequestVerificationToken" type="hidden"
...
...but the same message persists.
In my controller I have also tried:
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(page.clientScript);
return new ContentResult
{
ContentType = "text/xml", // also tried text/html
Content = Encoding.UTF8.GetString(bytes),
ContentEncoding = System.Text.Encoding.UTF8
};
Same issue.
-- UPDATE --
This is how I'm invoking the MVC app to return the text:
// used to load scripts on to the client script using a non-blocking asynch request
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://myserver/MyAppPath/someids';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
If I've understood correctly, you need an MVC action which returns both html and a script tag that can be injected in a page via a <script... include. You also want to render this via an MVC view.
The biggest issue you've missed is that in order to get this content into the calling page, you need to execute document.write from the script - you can't just send back HTML and script in response to the script include - the browser won't understand it, it's expecting javascript only.
There are a few ways to do this - I have written a full suite of ViewContent MVC controller methods, with the same overloads as View which returns the result of a view to a controller action as a string. I can then pass that back as a string literal (useful for html email generation) but also to a javascript encoder.
In this case, you don't need to be so generalist. We can leverage Darin Dimitrov's answer to this SO: Embed MVC Partial View into a document.write JS call and split your view into a View and a partial. The view writes the document.write() skeleton, and the partial view renders the dynamic html you want to be injected into the page. It's unclear if you're using the Anti Forgery Token in the main view which will call the script (in which case it should be rendered as part of the view that it returns) or if you're actually hard-coding it in the script. The second should definitely not be used but I'm writing this answer as if it is, because that appears to be what you want.
First, your partial view (let's call it Fragment.cshtml, put it in ~/Views/Shared)
<input name="__RequestVerificationToken"
type="hidden"value="[ommitted]" />
<script type="text/javascript">
// Here, we ensure that jQuery is loaded then load up the rest of our JS in in order.
ord = Math.random() * 10000000000000000;
...
Second, the host view, called SomeIds.cshtml
#{ Response.ContentType = "text/javascript"; }
document.write('#Html.Raw(HttpUtility.JavaScriptStringEncode(Html.Partial("~/Views/Shared/Fragment").ToHtmlString()))')
Now this view returns a document.write call that injects the HTML returned by the Fragment.cshtml into the page that includes the script.
Are you returning a PartialView that has all of the markup rendered?
Create a PartialView with your (form and script includes) and in your Controller:
public ActionResult Index(Models.MyModel model)
{
// validate the model if needed
return PartialView("[My PartialView Name]", model);
}
You could put your scripts in separate files, and add the [script src] tags in the PartialView.

HtmlTextWriter & MVC 3 Razor Output

This is probably a quick fix, but I am writing to a HtmlTextWriter in my HtmlHelper and no matter what I try it is encoding the HTML so the output shows the markup.
Helper:
protected override void WriteHtml(System.Web.UI.HtmlTextWriter writer)
{
writer.WriteFullBeginTag("div");
writer.WriteEndTag("div");
base.WriteHtml(writer);
}
and it will out put this to the page: <div></div>
EDIT
I am calling it in the view like this:
#Html.SAIF().Toolbar().Items(items => {
items.Add().Action("Action");
... ...
})
Edit #2
OK, let's see if I can explain this a little better.
If I use ViewContext.Writer to write my helper to the stream, when using a syntax like this:
#Html.MyHelper
It outputs encoded HTML (e.g.) <div>Test Div</div> = Not correct
If I use a syntax like this:
#{Html.MyHelper.Render();}
It renders correctly, meaning that it actually inserts the div into the DOM instead of displaying it as a string.
Make sense?

jQuery Mobile/MVC: Getting the browser URL to change with RedirectToAction

My first post...
When I use RedirectToAction the url in the browser doesn't change. How can I achieve this?
I'm switching over to ASP.NET MVC 3.0 (also using jQuery Mobile) after 10+ years using web forms. I'm about 8 weeks into it, and after several books and scouring Google for an answer, I'm coming up dry.
I have a single route defined in Global.asax:
routes.MapRoute(
"Routes",
"{controller}/{action}/{id}",
new { controller = "Shopping", action = "Index", id = UrlParameter.Optional }
I have a ShoppingController with these actions:
public ActionResult Cart() {...}
public ActionResult Products(string externalId) {...}
[HttpPost]
public ActionResult Products(List<ProductModel> productModels)
{
// do stuff
return RedirectToAction("Cart");
}
The url when I do a get and post (with the post having the RedirectToAction) is always:
/Shopping/Products?ExternalId=GenAdmin
After the post and RedirectToAction I want the url in the browser to change to:
/Shopping/Cart
I've tried Redirect, and RedirectToRoute but get the same results.
Any help would be greatly appreciated.
[Update]
I found that jQuery Mobile AJAX posts are the culprit here. If I turn off jQuery Mobile's AJAX it works.
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript">
// do not handle links via ajax by default
$(document).bind("mobileinit", function () { $.mobile.ajaxEnabled = false; });
</script>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.css" />
The ordering of the above scripts is important. I had to include the script to jQuery first, then include the script to disable jQuery Mobile's use of AJAX and then include the script to jQuery Mobile.
I'd still like to find a way to use AJAX and have the url update properly. Or at the least be able to call jQuery Mobile's "loading" message (or bake my own).
I think I've found an answer. Buried deep in the jQuery Mobile documentation, there is information about setting the data-url on the div with data-role="page". When I do this, I get the nice jQuery Mobile AJAX stuff (page loading message, page transitions) AND I get the url in the browser updated correctly.
Essentially, this is how I'm doing it...
[HttpPost]
public ActionResult Products(...)
{
// ... add products to cart
TempData["DataUrl"] = "data-url=\"/Cart\"";
return RedirectToAction("Index", "Cart");
}
Then on my layout page I have this....
<div data-role="page" data-theme="c" #TempData["DataUrl"]>
On my HttpPost actions I now set the TempData["DataUrl"] so for those pages it gets set and is populated on the layout page. "Get" actions don't set the TempData["DataUrl"] so it doesn't get populated on the layout page in those situtations.
The only thing that doesn't quite work with this, is when you right-click... view source... in the browser, the html isn't always for the page you are on, which isn't unusual for AJAX.
Not sure if it is still actual, but in my case I wrote following helper method
public static IHtmlString GetPageUrl<TModel>(this HtmlHelper<TModel> htmlHelper, ViewContext viewContext)
{
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.Append("data-url='");
urlBuilder.Append(viewContext.HttpContext.Request.Url.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped));
urlBuilder.Append("'");
return htmlHelper.Raw(urlBuilder.ToString());
}
And then use it as follows:
<div data-role="page" data-theme="d" #Html.GetPageUrl(ViewContext) >
This way I don't need for every redirect action store a TempData. Worked for me fine both for Redirect and RedirectToAction. This would not work properly in case if you use method "View()" inside controller and return different view name, which will change UI, but will retain url.
Hope it helps
Artem
David, this was a big help to me. I just wanted to add that in my case I had to use the following format to get the Url to display in the correct form as my other url's:
TempData["DataUrl"] = "data-url=/appName/controller/action";
return RedirectToAction("action", "controller");
As a side note, I also found that when assigning the value to TempData["DataUrl"] I was able to leave out the escaped quotes and enter it exactly as above and it seems to be working fine for me. Thanks again for your help.
There is an issue on github
https://github.com/jquery/jquery-mobile/issues/1571
It has a nice solution without the need of TempData

How do you write a literal string in a Webmatrix Helper?

I've tried getting the following helper to work...but I'm scratching my head.
#helper SimpleHelper()
{
string message;
message="<b>Hello</b>";
#message
}
The text comes out improperly encoded. With the lt; and gt; instead of the html tags.
Neither WriteLiteral(message) or #:message work.
Use the Html.Raw() helper in Razor to display that as an un-encoded string.
Here's the Razor quick-reference that Phil Haack put together recently.

Resources