从Asp.net-mvc控制器操作中返回File或ErrorMessage的最佳方法是什么?

10

我希望你能为我翻译以下的ASP.NET-MVC项目中的JavaScript代码和控制器操作:

Javascript:

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

C#控制器:

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

但在特定情况下,比如当orders.Count()等于0时,我希望向用户返回一个错误消息,而不是生成一个文件。

考虑到上面的代码,最佳方法是什么?我想过将其更改为 AJAX 调用,但我不确定如何下载我的 File() 并将其打包在 JSON 请求中(或者这是否受支持)。

7个回答

11

我会发起一个$.get请求到另一个控制器操作,检查订单数量并返回该值,如果适当的话还会返回一个错误消息。只有在需要时显示错误消息,否则就处理重定向以下载文件。这是对控制器的额外调用,但它允许你拥有完全的控制权,并处理可能出现的错误,而不会将用户重定向。

$("#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
    }
  });
});

控制器动作:

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

2
我不建议这种做法,请参见:http://en.wikipedia.org/wiki/Time_of_check_to_time_of_use。你仍然可能会因为0条记录而导致生成文档时出现错误。 - Dale Reidy
1
@gridzbi 同意,而且从性能方面来看,它需要2个请求而不是1个。 - Tom Riley
如果在导出文件过程中出现错误,使用 try{} catch{} 会发生什么?如何在保留当前页面的同时返回更友好的消息? - Nguyễn Văn Phong

5
我会返回一个表示资源不存在的状态,并返回 null?这样你就可以在javascript中相应地处理它,而不用担心要进行多个ajax调用来检查是否有一个可用或者如果有人绕过此类检查可能产生的安全影响。
例如...
控制器:
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);
}

1
我建议如果 orders.Count() 为0或发生其他错误时,将用户重定向。可以采用以下方式实现。
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);
    }
}

所以您需要创建一个信息性的 ordersError 视图来显示您的错误信息。

1
if (count==0) return View();
else return File(...)

无法工作吗?

1
这似乎部分正确。OP似乎正在寻找更优雅和复杂的解决方案。 - Rowan Freeman

1
如果您需要处理内部服务器错误,您需要设置自定义过滤器,并将其应用于控制器方法中,拦截磁盘相关错误或其他错误,并进行优雅地处理,返回您的视图一些有意义的数据消息。请参考此帖子,创建一个ASP.NET MVC过滤器和相关的JavaScript代码。ASP.NET MVC Ajax Error handling

1

1

@Anthony Shaw的答案很好,但有两个问题:

  1. 如果你使用try{} catch{},那么在导出文件过程中遇到错误会发生什么?
  2. 如何在保留当前页面的同时返回更友好的消息?

答案是你应该使用Ajax来实现它,分为两步:

  1. 你应该调用Ajax生成一个文件,并将内存数据存储到SessionTempData中。

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

控制器

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

这是一个很棒的文章阅读 通过 AJAX MVC 下载 Excel 文件


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接