Question

I have the following JavaScript code and controller action in my ASP.NET-MVC project:

Javascript:

$("#exportPPT").live('click', function (e) {
    window.location.href = "/Initiative/GenerateFile" + GenerateParams();
});

C# Controller:

    public ActionResult GenerateFile(MyParams myParams)
    {
        var template = Server.MapPath(PPT_ROOT + "/template.pptx");
        IEnumerable<Order> orders = Model.GetOrders(myparams);
        var pptResults = GeneratePowerpointFile(orders);
        return File(pptResults.Content, "application/vnd.ms-powerpoint", pptResults.FileName);
    }

But under certain conditions, let's say when orders.Count() is 0 then instead of generating a file, I would rather have an error message back to the user saying that you have an error.

What is the best way to achieve this given the code above? I thought of changing it to an AJAX call but I wasn't sure how to download my Fie() and package that inside a JSON request (or if that was supported).

Was it helpful?

Solution

I would initiate a $.get request to another controller action that would check for the order count. Return that value, along with an error message if appropriate. Display the error message when it's needed, otherwise process your redirect to download your file. It's an extra call to the controller but it allows you to have full control and handle the possibility of an error without redirecting your user.

$("#exportPPT").live('click', function (e) {
  $.get( "/Initiative/CheckForOrders" + GenerateParams(), function( data ) {
    if (data.IsValid) {
      window.location.href = "/Initiative/GenerateFile" + GenerateParams();
    } else {
      alert(data.ErrorMessage); // or show a div containing error message
    }
  });
});

Controller Action:

public ActionResult CheckForOrders(MyParams myParams)
{
  IEnumerable<Order> orders = Model.GetOrders(myparams);
  if (orders.Any())
    return Json(new { IsValid=true }, JsonRequestBehavior.AllowGet);

  return Json(new { IsValid=false, ErrorMessage="No orders" }, JsonRequestBehavior.AllowGet);
}

OTHER TIPS

I'd return a status that represents that a resource doesn't exist and return null? Then you can handle it accordingly in you javascript without worrying about doing multiple ajax calls to check one is available or the security implications if somebody bypasses such checks.

For example...

Controller

public ActionResult GenerateFile(MyParams myParams)
{
    var template = Server.MapPath(PPT_ROOT + "/template.pptx");
    IEnumerable<Order> orders = Model.GetOrders(myparams);

    if(!orders.Any()){
        Response.StatusCode = (int)HttpStatusCode.NotFound
        Response.StatusDescription = HttpStatusCode.NotFound.ToString();
        Response.TrySkipIisCustomErrors = true;
        return EmptyResult;
    } 

    var pptResults = GeneratePowerpointFile(orders);
    return new File(pptResults.Content, "application/vnd.ms-powerpoint", pptResults.FileName);
}
if (count==0) return View();
else return File(...)

can't this work?

I would suggest to redirect the user if orders.Count() is 0 or some other error occurs. Something like this.

public ActionResult GenerateFile(MyParams myParams)
{
    var template = Server.MapPath(PPT_ROOT + "/template.pptx");
    IEnumerable<Order> orders = Model.GetOrders(myparams);
    if(orders.Count() == 0){
       return RedirectToAction("Orders","ordersError",new { ID = "Error message"});
    }else{
       var pptResults = GeneratePowerpointFile(orders);
       return File(pptResults.Content, "application/vnd.mspowerpoint",pptResults.FileName);
    }
}

So you make an informative ordersError view that displays your error message.

If you will have to handle even internal server errors, you will have to set up a custom filter, apply it to your controller method, which intercepts disk related errors or any, and handles gracefully, and return your view some meaningfull data message.

please refer to this post, on creating a asp.net mvc filter, and related javascript code.

ASP.NET MVC Ajax Error handling

Maybe this post could be useful: Download File Using Javascript/jQuery

I believe the JQuery File Download plugin deserves some attention, it handles errors gracefully :) Have a look on its online demo, too...

The answer of @Anthony Shaw is fine, but there are 2 concerns:

  1. What happens if you use try{} catch{} then you have the error in the export file process?
  2. How can you return the message more friendly along with keeping the current page?

The answer is You should use Ajax to achieve it in two steps:

  1. You should call ajax to generate a file along with store that memory data into Session or TempData.

Ajax

$ajax({
    cache: false,
    url: '/Initiative/GenerateFile',
    data: GenerateParams(), 
    success: function (data){
         var response = JSON.parse(data);
         if(response.IsSuccesful) {
           window.location = '/Initiative/DownloadFile?fileGuid=' + response.FileGuid 
                           + '&filename=' + response.FileName;
         }else{
           alert(response.ErrorMessage);
         }
    }
});

Controller

    public ActionResult GenerateFile(MyParams myParams)
    {
        IEnumerable<Order> orders = Model.GetOrders(myparams);
        if (orders.Count() == 0)
        {
            return new JsonResult
            {
                Data = new { IsSuccesful = false, ErrorMessage = "No orders" }
            };
        }
        try
        {
            var pptResults = GeneratePowerpointFile(orders);
            var guid = Guid.NewGuid().ToString();
            TempData[guid] = pptResults.Content;
            return new JsonResult
            {
                Data = new { IsSuccesful = true, FileGuid = guid, FileName = pptResults.FileName }
            };
        }
        catch (Exception err)
        {
            return new JsonResult
            {
                Data = new { IsSuccesful = false, ErrorMessage = err }
            };
        }

    }
  1. Create a new DownloadFile action to read the TempData[guid] then return that file as well.

    [HttpGet]
    public virtual ActionResult DownloadFile(string fileGuid, string fileName)
    {
        if (TempData[fileGuid] != null)
        {
            var data = TempData[fileGuid] as byte[];
            return File(data, "application/vnd.ms-powerpoint", fileName);
        }
        else
        {
            return new EmptyResult();
        }
    }
    

The great post to read Download Excel file via AJAX MVC

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top