Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm trying to use the JQuery Form plugin (http://jquery.malsup.com/form/) to upload a file and a couple extra fields from my view, and I want the action method to return a Json result to the javascript callback.

Currently, the ActionMethod is called correctly (I can process the files and fields from the form) but when I return the Json result the browser tries to download it as a file (If I download the file and see its contents, it's the JSON content that I am returning.).

This is my form:

<form id="FormNewFile" action="@Url.Content("~/Home/AddFile")" method="post" enctype="multipart/form-data">
    <input type="hidden" name="eventId" value="25" />
    <input type="text" name="description" />
    <input type="file" name="fileName" />
    <input type="submit" value="Send!" />
</form>

This is my javascript:

  <script type="text/javascript">
     $(function () {
        $("#FormNewFile").ajaxForm({
           dataType:'json',
           success:processJson
     });
     });

     function processJson(a,b) {
        alert('success');
     }
  </script>

And this is my ActionMethod:

   [HttpPost]
   public ActionResult AddFile(long? eventId, string description)
   {
      int id = 5;
      return Json(new {id});
   }

The name of the file the browser tries to download is something like AddFilee87ce48e, with the last 8 characters being random hexadecimal characters.

And finally, the content of the file downloaded is:

{"id":5}

And the processJson function in javascript never gets called.

I googled a lot and the only thing that seems to work is returning the JSON result as a "Content" result from the action method, I think that's the approach I'm gonna take, but I still want to know why this isn't working?

Any ideas?

share|improve this question

5 Answers

My solution using json and no specific javascript:

/// <summary>
/// A result to use in combination with jquery.ajax.form when we want to upload a file using ajax
/// </summary>
public class FileJsonResult : JsonResult
{
    public JsonResult Result { get; set; }

    public FileJsonResult(JsonResult result):base()
    {
        this.Result = result;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.Write("<textarea>");
        this.Result.ExecuteResult(context);
        context.HttpContext.Response.Write("</textarea>");
        context.HttpContext.Response.ContentType = "text/html";
    }
}
share|improve this answer
up vote 6 down vote accepted

What I ended up doing was manually serializing the object I wanted to return as JSON, if I do this then the response won't have a header, therefore it will be handled by javascript instead of the browser. I used this helper method:

  public static ActionResult JsonPlain(object x)
  {
     var result = new ContentResult();
     result.Content = new JavaScriptSerializer().Serialize(x);
     return result;
  }

Hope this helps someone else

share|improve this answer
Thanx willvv, that did the trick. I was only getting this issue in IE9 – theahuramazda Jun 10 '11 at 12:24
+1 @willw, thanks for that. Has also solved the and issue I was having where IE9 wants to download the JSON response. – Iain M Norman Jan 5 '12 at 14:00

The following blog post, jQuery File Upload in ASP.NET MVC without using Flash, addresses the issue of wrapping the response in a textarea as described by Darin Dimitrov's answer.

share|improve this answer

That's normal behavior. Excerpt from the documentation:

Since it is not possible to upload files using the browser's XMLHttpRequest object, the Form Plugin uses a hidden iframe element to help with the task. This is a common technique, but it has inherent limitations. The iframe element is used as the target of the form's submit operation which means that the server response is written to the iframe. This is fine if the response type is HTML or XML, but doesn't work as well if the response type is script or JSON, both of which often contain characters that need to be repesented using entity references when found in HTML markup.

To account for the challenges of script and JSON responses, the Form Plugin allows these responses to be embedded in a textarea element and it is recommended that you do so for these response types when used in conjuction with file uploads. Please note, however, that if there is no file input in the form then the request uses normal XHR to submit the form (not an iframe). This puts the burden on your server code to know when to use a textarea and when not to. If you like, you can use the iframe option of the plugin to force it to always use an iframe mode and then your server can always embed the response in a textarea. The following response shows how a script should be returned from the server:

This means that if you want to return JSON you need to wrap it in a <textarea> tags on your server. To achieve this you could write a custom action result deriving from JsonResult and wrapping the generated JSON inside those tags.

share|improve this answer
Yep, I saw that, but I just didn't understand the <textarea> section. What's the response type that I should use? Also, as I posted on my question, if I just serialize the object and send a Content response with the JSON (without the <textarea>) it works, therefore it seems to be something related with the Json result response type. – willvv Feb 22 '11 at 17:20

Have a look at the action on your form tag:

action="@Url.Content("~/Home/AddFile")"

The only usage I've seen of this is in script tags such as :

<script language="javascript" src="<%=Url.Content("~/Scripts/jquery-1.3.2.js")%>

The correct reference for the action would be:

action="/Home/Addfile"

While I am not too sure about the explanation as to why but the UrlHelper:

Url.Content("~/somepath")

converts a virtual path to a absolute path and expects a physical file such as a javascript.

The json result might be being returned as a file download because the mvc framework is expecting a file download of some description. Just as:

<a href="<%= Url.Content("~/_docs/folder/folder/document.pdf") %>">
document.pdf</a>

would download or display a physical pdf file.

share|improve this answer
You're right in that Url.Content isn't meant to do that, but in the end it generates action="Home/Addfile", so it doesn't make any difference. It is actually trying to download the file because when you return a JsonResult it adds a JSON header, and the browser tries to process all responses with a header. – willvv Feb 25 '11 at 19:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.