I am trying to allow users to download a file, and have been trying for quite a while to no avail to get this to work.
The code I have been using is:
Response.ClearHeaders();
Response.Clear();
Response.BufferOutput = true;
Response.AddHeader("Content-disposition",
"attachment; filename= "+ Path.GetFileName(path) + fileType);
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(buffer);
Response.Flush();
Response.Close();
Response.End();
Unfortunately nothing happens when I try to make it run except for lots of exceptions:
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.Mvc.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.Mvc.dll
A first chance exception of type 'System.NullReferenceException' occurred in WebDev.WebHost40.dll
(etc)
Changing my debugging settings I discovered that the exception I was getting was
"Server cannot set content type after HTTP headers have been sent."
I have searched through Google and attempted many solutions but nothing have worked so far. This is the only time Response has been used as far as I know - unless there is some background processes occurring (if so, how do I change this?). Please note that I am using Asp.NET MVC.
Any ideas?
Edit:
Here is some context, which might help to resolve the problem:
The code is inside a Webmethod that has been called via Ajax, the reason being that I needed to call the server side via the client side (bad practice, but it was needed in the time provided) and I also needed to pass through a parameter.
Here is the ajax call:
$("#Button").each(function() {
this.submitting = false; //To prevent double-clicking problems
}).on("click", function (e) {
if (e.preventDefault) e.preventDefault();
$.ajaxSetup({ cache: false });
e.preventDefault();
if (!this.submitting)
{
this.submitting = true;
var self = this;
$.ajax({
url: 'Controller/retrieveFile',
type: 'POST',
data: { SystemNumber: $("#Button").val() },
dataType: 'json',
success: function (data) {
self.submitting = false;
},
error: function() {
self.submitting = false;
}
});
}
});
Is this, perhaps, a problem with the return value? Currently I am returning
Json(true, JsonRequestBehavior.AllowGet);
Try using HttpContext.Response.AppendHeader("Content-disposition", ....) and getting rid of the Response.ClearHeaders(); and Response.Clear(); lines.
Also you should consider using FileContentResult as the return type of your method.
Related
When I am doing AJAX request to controller, Action Method could not find the View which I am trying to return and throwing the exception as shown at end of the question.
Following is the AJAX method calling the Action Method:
$.ajax({
url: "/Courses/AddTime",
data: { DayName: DayValue, StartTimeValue:
starttimeval,EndTimeValue: EndtimeVal },
dataType: "json",
type: "POST",
error: function () {
alert(" An error occurred.");
},
success: function (data) {
window.location.href = '/Courses/listbatch/?BtchTimingid=' +
data.ID + "&InsertRetId=" + data.secndid;
}
});
Following is Action Method and it is called properly from AJAX request but while returning View it is throwing exception.
public ActionResult listbatch(string Search_name, int? page, int?
BtchTimingid = 0, int InsertRetId=0)
{
/// There Have some code which is working perfect
if (Request.IsAjaxRequest())
return PartialView("_BatchTimingList", model.pageCoursesList);
else
return View(model);
}
Exception Screenshot
The view 'listbatch' or its master was not found or no view engine
supports the searched locations. The following locations were
searched:
~/Views/Courses/listbatch.aspx
~/Views/Courses/listbatch.ascx
~/Views/Shared/listbatch.aspx
~/Views/Shared/listbatch.ascx
~/Views/Courses/listbatch.cshtml
~/Views/Courses/listbatch.vbhtml
~/Views/Shared/listbatch.cshtml
~/Views/Shared/listbatch.vbhtml
I had the same problem and got a solution for that after spending 2 days.
Instead of using
window.location.href = '/Courses/listbatch/?BtchTimingid=' +
data.ID + "&InsertRetId=" + data.secndid;
I suggest you to use
window.location.href = '#Html.Raw(Url.Action("listbatch","Courses",new{BtchTimingid=data.ID, InsertRetId = data.secndid}))
If you use it without #Html.Raw, your URL will have & instead of & for parameters, it shouldn't cause any problem but i don't know why it caused for me and got the same problem that you have i-e The view was not found... So use Html.Raw as well.
Your view is missing.
public ActionResult listbatch(string Search_name, int? page, int? BtchTimingid = 0, int InsertRetId=0)
{
if (Request.IsAjaxRequest())
return PartialView("_BatchTimingList", model.pageCoursesList);
else
return View(model); // <======= HERE (not an AJAX request) =========
}
The following JavaScript does not generate an Ajax request, its a normal GET.
window.location.href = '/Courses/listbatch/?BtchTimingid=' +
data.ID + "&InsertRetId=" + data.secndid;
So View(model) expects to find listbatch.cshtml but cannot (presumably because it is missing) and you get the error message.
You need to create /Views/Courses/listbatch.cshtml
Have a look in the Views folder, see if it is there...
I have a function being called on a page on a local apache instance (/test) which calls a subpage (/test/info) with jQuery.ajax and correctly makes an AJAX call and dynamically loads the content from the response on my desktop in FF, Safari, Chrome, but in the iOS emulator, no call is made and the page is refreshed.
window.getInfo = function ( event ) {
console.log('1) prior to $.ajax');
$.ajax({
url: 'http://localhost/test/info',
dataType: 'html',
beforeSend: function(xhr) {
console.log('2) beforeSend');
},
success: function(data, textStatus) {
console.log('3) success');
if ( textStatus == 'success' ) {
// doing stuff with data
}
}
}).always( function() { console.log('4) always'); });
};
From the desktop browsers all of the logs are printed and my apache server reports a request at /test, but on Safari in the iOS emulator, only the '1) prior to $.ajax' and '2) beforeSend' logs are printed and the next request made to apache is for /test.
Does anyone have any idea what is happening here, and how to make iOS behave itself?
UPDATE: When I add the async: false attribute to the ajax call, all the logs are printed, and the request is made, so that basically fixed the issue; however the page still reloads which I believe is a different issue related to event propagation on iOS.
All that is needed to make this work is a return false; from the handler function. See event.preventDefault() vs. return false for a more complete explanation, but basically "return false from within a jQuery event handler is effectively the same as calling both e.preventDefault and e.stopPropagation on the passed jQuery.Event object."
So a fully functional version of the above code is:
getInfo = function() {
$.ajax({
url: '/test/info',
dataType: 'html',
success: function(data, textStatus) {
if ( textStatus == 'success' ) {
// doing stuff with data
}
}
});
return false;
};
// The order of vv that string may be important
$(document).on('click touchend', '.getInfo', getInfo );
I am developing a rest API on MVC3.
Whenever there is a problem with validation, I want to throw 500 + json that describes the error (the json can be the list of unvalidated fields).
The problem is that the json returns inside html that holds the entire HttpExeption (Server Error in '/' Application.)
If I put filterContext.ExceptionHandled = true; the message goes out clean, but the client can't see the 500 error on his side.
This case: https://stackoverflow.com/a/4707762/936651 actually the html and gives clean json to the client, but also removes the 500 error.
You could set the status code to 500 inside the custom error handler you have seen here:
filterContext.RequestContext.HttpContext.Response.StatusCode = 500;
and on the client:
$.ajax({
url: '/home/foo',
success: function (result) {
alert('success');
},
error: function (jqXHR, textStatus, errorThrown) {
var json = $.parseJSON(jqXHR.responseText);
// TODO: do something with the json that was returned from the server
}
});
As the title says, i want to retrieve my Json data values from a XMLHttpRequest. I already know how to do it by using a common success $.Ajax success event, but i want to get it's values from an error event. A thing that i noticed is not simple to find all kinds of a XMLHttpRequest types.
To explain a little more, here's the scenario: after some inactivity, the user sessions expires. If he tries to do any operations using an Ajax call, he'll be redirected to the login page. I handle this session timeout error on a particular filter that implements an OnException method.
While i can do it using a sort of a hack (by setting manually the HTTP response code), i'd like to do it on "proper way", with no hacks.
OnException method code snippet
filterContext.ExceptionHandled = true;
// If this is an ajax request, return the exception in the response
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
//If the session has expired, throws a SESSION TIMEOUT error
if (Session["UserLogged"] == null)
{
//The hack mentioned before
filterContext.HttpContext.Response.StatusCode = 502;
filterContext.Result = new JsonResult()
{
Data = new { success = false, errorType = "SESSION_TIMEOUT" ,error = filterContext.Exception.ToString() },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
So, by using a simple $.Ajax Error Event, how would i retrive the data from the filterContext.Result? Specifically the errorType parameter.
You should be able to parse the JSON data out of the jqXHR responseText property in your error handler.
error: function(jqXHR, textStatus, errorThrown) {
alert('jqXHR.responseText = ' + jqXHR.responseText);
}
I'm having a problem similar to jQuery $.ajax Not Working in IE8 but it works on FireFox & Chrome, but with a different use case.
I'm using the jQuery Form plug-in to handle a file upload to an ASP.NET MVC controller, which sends the file off for parsing and processing. If an Exception is thrown, it should alert the user to the issue.
//client side code
//make an ajax call, sending the contents of the file
$("#ajaxUploadForm").ajaxSubmit({
dataType: 'json',
url: '/plan/Something/ExcelImport',
iframe: true,
beforeSend: function () {
$(".ui-dialog-buttonpane").find("#my_progress").fadeIn();
},
success: function (data, textStatus) {
output = "<center><span class='flash'>" + data.message + "</span></center>";
$("#flash_message").html(output).fadeIn('slow');
setTimeout(function () { $("#flash_message").fadeOut() }, 5000);
cleanup();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("XMLHttpRequest is " + XMLHttpRequest);
var contents = "";
for (prop in XMLHttpRequest) {
contents += "\na property is " + prop + " it's value is " + XMLHttpRequest[prop];
}
alert("the contents are " + contents);
alert("textStatus is " + textStatus);
alert("errorThrown is " + errorThrown);
//comes back in an HTML envelope. This should be parsed with regex, but I can't get it to work. Dirty hack
response = XMLHttpRequest.responseText.substring(XMLHttpRequest.responseText.indexOf("<body>"));
response = response.replace("<body>", "");
response = response.replace("</body>", "");
alert("There was a problem with the upload.\r\n" + response);
},
complete: function (XMLHttpRequest, textStatus) {
$(".ui-dialog-buttonpane").find("#my_progress").remove();
something_import.dialog('close');
something_import.dialog('destroy');
}
});
//server side code
public FileUploadJsonResult ExcelImport()
{
FileUploadJsonResult result = new FileUploadJsonResult();
HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
if (hpf.ContentLength == 0)
return new FileUploadJsonResult { Data = new { message = "File contained no data" } };
String fileName = Path.GetFileName(hpf.FileName);
String timeStampedFile = fileName.Insert(fileName.IndexOf('.'),"_"+DateTime.Now.ToFileTimeUtc());
string savedFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "tempo", timeStampedFile);
hpf.SaveAs(savedFileName);
try
{
result = ProcessFile(savedFileName, Request["Id"]) as FileUploadJsonResult;
}
catch (ArgumentException e)
{
this.Response.StatusCode = 500;
this.Response.StatusDescription = System.Net.HttpStatusCode.BadRequest.ToString();
Response.Write(e.Message);
result = Json(new { message = e.Message, stackTrace = e.StackTrace }) as FileUploadJsonResult;
}
return result;
}
This works perfectly in Chrome and Firefox. In IE, the XMLHttpRequest object coming back is different:
FF:
IE:
I've been Googling around for differences between the browser implementations of XMLHttpRequest, but haven't found anything that deals specifically with this case. Stymied.
The reason this is happening is because of the iframe fallback strategy that ajaxSubmit employs. I think since the response gets posted into the iframe IE tries to figure out how to dipslay it and decides that it wants to ask you to download the response instead of just putting it in the iframe.
I came across this same situation a while ago and found an article (that I can't find now) that offered a workaround.
If you surround your json response in a textarea nobody is going to complain(IE,FF,Chrome,probably Safari) and you'll get your response parsed correctly.
E.g. if you are returning
{Id: 1, Name: 'Me'}
just return:
<textarea>{Id: 1, Name: 'Me'}</textarea>
You see now IE thinks it's html so it inserts it into the hidden iframe. Your ajaxSubmit function still gets called and parses the json correctly and then everybody's happy. :)
If you're using ASP.NET MVC you could shamelessly copy this extension method :)
public static class ControllerExtensions
{
public static ActionResult JsonSafe(this IController controller, object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return new WriteResult(string.Format("<textarea>{0}</textarea>", serializer.Serialize(obj)));
}
}
The wikipedia article on XMLHttpRequest seems to give a good overview of the history behind the XMLHttpRequest. It seems Microsoft and Mozilla developed/adopted their own versions of the object and hence why you are probably seeing different properties.
Here is a link to Microsoft's implementation of the XMLHttpRequest interface members, which seem to match the properties in your alert.
Here is the a link to Mozilla's implementation of XMLHttpRequest.
So while we wait for the W3C to standardize the XMLHttpRequest you will continue to have different implementations across the browsers like you are seeing in this case.
For some added fun here is Apple's and Opera's specifications on XMLHttpRequest.