FineUploader - 500 Server Error in MVC app - fine-uploader

I just started learning FineUploader. I believe I have everything setup correctly. But when I attempt to upload a file, I get a 500 server error in my JavaScript code. My controller method, which is below, is never getting called. So it's happening before controll is passed to the method.
[HttpPost]
public FineUploaderResult UploadFile(FineUpload upload, string extraParam1, int extraParam2)
{
// Asp.net MVC will set extraParam1 and extraParam2 from the params object passed by Fine-Uploader
string dir = #"c:\upload\path";
var filePath = Path.Combine(dir, upload.Filename);
try
{
upload.SaveAs(filePath);
}
catch (Exception ex)
{
return new FineUploaderResult(false, error: ex.Message);
}
// the anonymous object in the result below will be convert to json and set back to the browser
return new FineUploaderResult(true, new { extraInformation = 12345 });
}
My JavaScript code is as follows:
$(document).ready(function () {
$("#fine-uploader").fineUploader({
request: {
endpoint: 'UploadFile'
}
});
});
My ModelBinder class, which is getting executed, is as follows:
public class ModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.RequestContext.HttpContext.Request;
var formUpload = request.Files.Count > 0;
// find filename
var xFileName = request.Headers["X-File-Name"];
var qqFile = request["qqfile"];
var formFilename = formUpload ? request.Files[0].FileName : null;
var upload = new FineUpload
{
Filename = xFileName ?? qqFile ?? formFilename,
InputStream = formUpload ? request.Files[0].InputStream : request.InputStream
};
return upload;
}
}

If your expected endpoint is not hit and your server is returning a 500 response to the request, then the issue is with your server or client-side configuration of Fine Uploader. Your endpoint option of "UploadFile" seems suspicious. I would expect a path to be entered instead.

For anybody else who comes across this post, my problem lay in the following line of code within UploadController.cs:
public FineUploaderResult UploadFile(FineUpload upload, string extraParam1, int extraParam2)
BindModel was being called in FineUpload.cs but then I was getting a 500 error after that with no methods being fired in debug.
The code is my cshtml page was as follows:
<div id="fine-uploader-gallery"></div>
<script>
$("#fine-uploader-gallery").fineUploader({
template: 'qq-template-gallery',
request: {
endpoint: '#Url.Action("UploadFile", "Upload")'
},
thumbnails: {
placeholders: {
waitingPath: '/source/placeholders/waiting-generic.png',
notAvailablePath: '/source/placeholders/not_available-generic.png'
}
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'png'],
itemLimit: 3,
sizeLimit: 102400 // 50 kB = 50 * 1024 bytes
},
autoUpload: false,
debug: true
});
$("#trigger-upload").click(function () {
$("#fine-uploader-gallery").fineUploader("uploadStoredFiles");
});
</script>
This may be my lack of understanding of this excellent product but the only way I could get it to work was to change the problem line above to
public FineUploaderResult UploadFile(FineUpload upload)
That way the signature of the call matched the method in the controller.

Related

React/Redux download file

I need to download a file from the server when a button is clicked.
I created a MaterialUI button and on its onclick callback i call an action of the container component connected.
The action is asynchronous and does an ajax POST:
export const onXlsxClick = () => dispatch => {
const urlParams = {
filters: {
aggregation: 'macro_area',
chart_resolution: '1_hour',
chart_from: '1478080363',
chart_to: '1477993963'
},
labels: ['PROVA1', 'PROVA2'],
series: [
{
label: null,
timestamp: 1478080363,
values: [123, 345]
},
{
label: null,
timestamp: 1477993963,
values: [153, 3435]
}
]
};
return $.ajax({
url:'/rest/export/chart/xlsx',
type: 'POST',
dataType: 'application/json',
contentType: 'application/json',
data: JSON.stringify(urlParams)
})
.done(data => {
console.log('success');
})
.fail(error => {
console.log(error);
});
};
The server receive the request and handle it correctly through this REST service:
#POST
#Path("xlsx")
#Produces("application/vnd.ms-excel")
public Response getXlsx(ChartExportRequest request) {
ResponseBuilder responseBuilder;
ChartExportRequestDTO reqDto = null;
try {
reqDto = parseDTO(request);
checkRequestDTO(reqDto);
ExportDTO dto = getXlsxProvider().create(reqDto);
responseBuilder = Response.ok(dto.getFile())
.header("Content-disposition", "attachment;filename=" + dto.getFileName());
}
catch(Exception e) {
logger.error("Error providing export xlsx for tab RIGEDI with request [" + (reqDto != null ? reqDto.toString() : null) + "]", e);
responseBuilder = Response.serverError().entity(e.getMessage());
}
return responseBuilder.build();
}
The problem is that the response arrives correctly to the client but then nothing happens: I am expecting that the browser shows the download dialog (example: in chrome I expect the bottom bar of downloads to appear with my file).
What am I doing wrong?
AS per Nate's answer here, the response of Ajax request is not recognised by a browser as a file. It will behave in the same way for all Ajax responses.
You need to trigger the download popup manually.
In my implementation, I used filesaverjs to trigger the download popup, once I have received the API response in reducer.
Since FileSaver uses blob for saving the file, I am sending the response from server as a blob, converting it into string array buffer and then using it to save my file. This approach was described in
Please find the sample code below for the reducer :
(using reducer for state modification, as per Redux)
reducer.js
let fileSaver = require("file-saver");
export default function projectReducer(state = {}, action)
{
let project;
switch (action.type) {
case GET_PROJECT_SUCCESS :
project = Object.assign(action.response.data);
return project;
case EXPORT_AND_DOWNLOAD_DATA_SUCCESS :
let data = s2ab(action.response.data);
fileSaver.saveAs(new Blob([data], {type: "application/octet-stream"}), "test.xlsx");
return state;
}
return state;
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}

Hide image using Javascript after controller action is complete MVC3

My application has been implemeted using MVC 3, .net.
I am trying to generate an excel file at the click of a button.
The call to the controller action is made using Ajax.
My main problem is: During the file generation i am trying to display an image on the screen to let the user know of the ingoing operation. I can very well display the image but i cannot hide it after the operation is completed. The codei am using is :
Javascript code:
$("input.DownloadExcelReport").click(function (e) {
e.preventDefault();
var parameter = -- code to fetch parameter value;
var outputViewUrl = (the url is created here);
showLoading(); -- This function displays the image
window.location.href = outputViewUrl;
});
Controller Action code:
public ActionResult DownExcelReportForAssortment(Guid parameter)
{
try
{
//the contents for the file generation are fetched here..
// Write contents to excel file
if (memoryStream != null)
{
var documentName = "Report.xls";
byte[] byteArrary = memoryStream.ToArray();
return File(byteArrary, "application/vnd.ms-excel", documentName);
}
}
catch (Exception ex)
{
LogManager.LogException(ex);
}
}
I do not return a Json result to the calling javascript method where i can write the code to hide the image.
I am returning a file which can be saved by the user and the action is completed.
Can somone please suggect/help me of how can i hide the image once the file generation operation is complete?
Appreciate the help...
You may checkout the following article and put this into action. So we start by defining a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DownExcelReportForAssortment(Guid parameter, string tokenId)
{
// Simulate some heavy work to fetch the report
Thread.Sleep(5000);
// we fake it
byte[] byteArray = System.IO.File.ReadAllBytes(#"c:\test.xls");
var cookie = new HttpCookie("fileDownloadToken", tokenId);
Response.AppendCookie(cookie);
return File(byteArray, "application/vnd.ms-excel", "report.xls");
}
}
and in the view:
#Html.ActionLink(
"download report",
"DownExcelReportForAssortment",
"Home",
new { parameter = Guid.NewGuid(), tokenId = "__token__" },
new { #class = "download" }
)
Now the last step is to include the jquery.cookie plugin:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.cookie.js")"></script>
and write a script to subscribe to the click event of the anchor and track the download progress:
$(function () {
var fileDownloadCheckTimer;
$('.download').click(function () {
var token = new Date().getTime();
$(this).attr('href', function () {
return this.href.replace('__token__', token);
});
// Show the download spinner
$('body').append('<span id="progress">Downloading ...</span>');
// Start polling for the cookie
fileDownloadCheckTimer = window.setInterval(function () {
var cookieValue = $.cookie('fileDownloadToken');
if (cookieValue == token) {
window.clearInterval(fileDownloadCheckTimer);
$.cookie('fileDownloadToken', null);
// Hide the download spinner
$('#progress').remove();
}
}, 1000);
});
});

Reporting errors from Ajax invoked PartialView methods in MVC

If a normal page load errors I can report the exception details to the user via the Error view and HandleErrorInfo model.
If an ajax call expecting a Json result errors, I can explicitly handle the error and pass details to the client:
public JsonResult Whatever()
{
try
{
DoSomething();
return Json(new { status = "OK" });
}
catch (Exception e)
{
return Json(new { status = "Error", message = e.Message });
}
}
So, my problem, I can't see any way to report error details from an Ajax call to an action returning a partial view.
$.ajax({
url: 'whatever/trevor',
error: function (jqXHR, status, error) {
alert('An error occured: ' + error);
},
success: function (html) {
$container.html(html);
}
});
This will only report an Http error code (e.g. Internal Server Error) which is not helpful to the client. Is there some clever trick to pass either a successful PartialView (html) result or an error message?
Explicitly evaluating the html from the ViewResult and returning it as part of a Json object along with a status seems too smelly. Is there an established pattern for handling this scenario?
Controller action:
public ActionResult Foo()
{
// Obviously DoSomething could throw but if we start
// trying and catching on every single thing that could throw
// our controller actions will resemble some horrible plumbing code more
// than what they normally should resemble: a.k.a being slim and focus on
// what really matters which is fetch a model and pass to the view
// Here you could return any type of view you like: JSON, HTML, XML, CSV, PDF, ...
var model = DoSomething();
return PartialView(model);
}
Then we define a Global error handler for our application:
protected void Application_Error(object sender, EventArgs e)
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.Clear();
Server.ClearError();
if (new HttpRequestWrapper(Request).IsAjaxRequest())
{
// Some error occurred during the execution of the request and
// the client made an AJAX request so let's return the error
// message as a JSON object but we could really return any JSON structure
// we would like here
Response.StatusCode = 500;
Response.ContentType = "application/json";
Response.Write(new JavaScriptSerializer().Serialize(new
{
errorMessage = exception.Message
}));
return;
}
// Here we do standard error handling as shown in this answer:
// http://stackoverflow.com/q/5229581/29407
var routeData = new RouteData();
routeData.Values["controller"] = "Errors";
routeData.Values["action"] = "General";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (Response.StatusCode)
{
case 404:
routeData.Values["action"] = "Http404";
break;
case 500:
routeData.Values["action"] = "Http500";
break;
}
}
IController errorsController = new ErrorsController();
var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
errorsController.Execute(rc);
}
Here's how the ErrorsController used in the global error handler could look like. Probably we could define some custom views for the 404 and 500 actions:
public class ErrorsController : Controller
{
public ActionResult Http404()
{
return Content("Oops 404");
}
public ActionResult Http500()
{
return Content("500, something very bad happened");
}
}
Then we could subscribe for a global error handler for all AJAX errors so that we don't have to repeat this error handling code for all AJAX requests but if we wanted we could repeat it:
$('body').ajaxError(function (evt, jqXHR) {
var error = $.parseJSON(jqXHR.responseText);
alert('An error occured: ' + error.errorMessage);
});
And finally we fire an AJAX request to the controller action that we hope will return an HTML partial in this case:
$.ajax({
url: 'whatever/trevor',
success: function (html) {
$container.html(html);
}
});
Create an overriden version of HandleErrorAttribute (JsonHandleErrorAttribute ?) and add [JsonHandleError] on your json action.
Have a look at AjaxAuthorizeAttribute in asp.net mvc [handleerror] [authorize] with JsonResult?

JSON returned using Json() by jquery $.post()

I can't work out what I'm doing wrong - I'm sure this used to work...:
<script type="text/javascript">
$("##containerId form").submit(function (event) {
event.preventDefault();
var form = $(this);
if (form.valid()) {
$.post(form.attr('action'), form.serialize(), function(data) {
$("##containerId").replaceWith(data.result);
}, "json");
}
});
</script>
I have a function that returns a view result as a string so I can return it as an object within the JSON response:
protected string RenderViewResultToString(ViewResultBase viewResult) {
using (var sw = new StringWriter()) {
if (string.IsNullOrEmpty(viewResult.ViewName))
viewResult.ViewName = ControllerContext.RouteData.GetRequiredString("action");
ViewEngineResult result = null;
if (viewResult.View == null) {
result = viewResult.ViewEngineCollection.FindPartialView(ControllerContext, viewResult.ViewName);
if (result.View == null)
throw new InvalidOperationException("Unable to find view. Searched in: " + string.Join(",", result.SearchedLocations));
viewResult.View = result.View;
}
var view = viewResult.View;
var viewContext = new ViewContext(ControllerContext, view, viewResult.ViewData, viewResult.TempData, sw);
view.Render(viewContext, sw);
if (result != null)
result.ViewEngine.ReleaseView(ControllerContext, view);
return sw.ToString();
}
}
So, in my controller I have:
[HttpPost, ValidateInput(false)]
public JsonResult Edit(/* stuff */) {
bool success = true;
try {
/* stuff */
} catch {
/* stuff */
success = false;
}
return Json(new { success, result = RenderViewResultToString(/* stuff - call to something that gives a ViewResult */) });
}
In Chrome, I get: "Resource interpreted as Document but transferred with MIME type application/json." and it renders the JSON in the browser as text.
In Firefox/IE, it prompts me to download a file.
What gives?
The form submission isn't getting suppressed. The messages you are getting are from an actual form submission to a page that returns JSON. If you check the browser address bar, you should see the URL is different.
If you run $("##containerId form") in the console, you should see that you're getting no results. "#" is an invalid character in a selector and needs to be escaped. $("#\\#containerId form") should work.

Using an MVC Action Filter to catch redirects in Ajax requests and return a JsonResult

In my ASP.NET MVC 3 application, I have some action methods that can be invoked with Ajax and non-Ajax requests. The action methods may return a RedirectResult and I want the target URL to be loaded in the browser - even for Ajax requests.
My current solution is for the action method to call IsAjaxRequest itself. If false, it returns a RedirectResult. If true, it returns a JsonResult containing the target URL, and I have script in the browser to read this and set window.location accordingly.
I was hoping to declutter the action methods and handle this in a filter. My problem is that the target URL (filterContext.HttpContext.Response.RedirectLocation) is null in the filter event handlers other than OnResultExecuted, and setting filterContext.Result in that handler (and changing response.StatusCode) doesn't succeed in issuing JSON in the response.
If I use one of the other handlers, such as OnActionExecuted, I can change the response to issue JSON, but cannot get hold of the target URL.
A 2-step process does not work either - if I change the result to a JsonResult in OnActionExecuted, the RedirectLocation is null in OnResultExecuted.
Can anyone recreate this problem or recommend a better solution? Thanks.
PS here is the code from OnResultExecuted:
if ((filterContext.Result is RedirectToRouteResult ||
filterContext.Result is RedirectResult) &&
filterContext.HttpContext.Request.IsAjaxRequest())
{
string url = filterContext.HttpContext.Response.RedirectLocation;
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
filterContext.HttpContext.Response.RedirectLocation = "";
filterContext.Result = new JsonResult
{
Data = new { Redirect = url },
ContentEncoding = System.Text.Encoding.UTF8,
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
Here's an example of how you could proceed:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
string url = "/";
var redirectResult = filterContext.Result as RedirectResult;
if (filterContext.Result is RedirectResult)
{
// It was a RedirectResult => we need to calculate the url
var result = filterContext.Result as RedirectResult;
url = UrlHelper.GenerateContentUrl(result.Url, filterContext.HttpContext);
}
else if (filterContext.Result is RedirectToRouteResult)
{
// It was a RedirectToRouteResult => we need to calculate
// the target url
var result = filterContext.Result as RedirectToRouteResult;
url = UrlHelper.GenerateUrl(result.RouteName, null, null, result.RouteValues, RouteTable.Routes, filterContext.RequestContext, false);
}
filterContext.Result = new JsonResult
{
Data = new { Redirect = url },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
// TODO: It is not an AJAX request => do whatever you were doing
}
}

Resources