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

941 Views Asked by At

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);
  })();
2

There are 2 best solutions below

0
On BEST ANSWER

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.

0
On

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.