11

I have a controller action that downloads a file from an azure blob based on the container reference name (i.e. full path name of the file in the blob). The code looks something like this:

public FileContentResult GetDocument(String pathName)
{
    try
    {
        Byte[] buffer = BlobStorage.DownloadFile(pathName);
        FileContentResult result = new FileContentResult(buffer, "PDF");
        String[] folders = pathName.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
        // get the last one as actual "file name" based on some convention
        result.FileDownloadName = folders[folders.Length - 1];

        return result;
    }
    catch (Exception ex)
    {
        // log error
    }
    // how to handle if file is not found?
    return new FileContentResult(new byte[] { }, "PDF");
}

The BlobStorage class there is my helper class to download the stream from the blob.

My question is stated in the code comment: How should I handle the scenario when the file/stream is not found? Currently, I am passing an empty PDF file, which I feel is not the best way to do it.

Alex R.
  • 4,664
  • 4
  • 30
  • 40
  • @Stecya: This is called from the view via javascript. – Alex R. Jun 23 '11 at 07:11
  • What should be the datatype on the ajax call? @Alex R could you also show how the ajax call is made in the view it would be helpful. – michael Jul 22 '11 at 06:57
  • The question is already answered. Are you also having the same issue? The call from the javascript is simply `window.location.href = "GetDocument?pathName=" + docPath;`. That will call the controller action above and respond accordingly. – Alex R. Jul 22 '11 at 07:06
  • yes i have , i am not able open the file my view looks like this var options = { iframe: true, dataType: "html", url: "Upload/Previewfile" }; preview file is similar to your GetDocument after executing this ishould get popup to open file but i am not getting.Do you have any idea or shall i post as a question? thanks. – michael Jul 22 '11 at 07:11
  • Yeah, better post it as a separate question. That's how it works here. I'll be glad to help if I can. – Alex R. Jul 22 '11 at 07:13

2 Answers2

22

The correct way to handle a not found in a web application is by returning a 404 HTTP status code to the client which in ASP.NET MVC terms translates into returning a HttpNotFoundResult from your controller action:

return new HttpNotFoundResult();

Ahh, oops, didn't notice you were still on ASP.NET MVC 2. You could implement it yourself because HttpNotFoundResult was introduced only in ASP.NET MVC 3:

public class HttpNotFoundResult : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        context.HttpContext.Response.StatusCode = 404;
    }
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    In case of MVC3, is `HttpNotFoundResult` inherited from `ActionResult`? In this case the definition of the action method should be `public ActionResult GetDocument(String pathName)`, Right? – Ken D Jun 23 '11 at 07:17
  • 1
    @LordCover, right. I would recommend you to always use `ActionResult` as return type in your action signatures. I have also updated my answer to provide a sample implementation of `HttpNotFoundResult`. – Darin Dimitrov Jun 23 '11 at 07:18
  • Well thanks, usually I use the higher level in such cases as this question, but if I have one and only one definite value like `JsonResult` I use it alone. Yes I noticed it and +1. – Ken D Jun 23 '11 at 07:21
  • Thanks, unfortunately I am on MVC2 still. (Will need to upgrade soon!) I'll implement what you said and see. – Alex R. Jun 23 '11 at 07:29
6

In ASP.NET Core, use NotFound()

Your controller must inherit of Controller and the method must return ActionResult

Example:

public ActionResult GetFile(string path)
{
    if (!File.Exists(path))
    {
        return NotFound();
    }
    
    try
    {
        return new FileContentResult(File.ReadAllBytes(path), "application/octet-stream");
    }
    catch (FileNotFoundException)
    {
        return NotFound();
    }
}

Note: the above code doesn't handle all cases of file operations errors as invalid chars in path or file unavailability (because it is beyond the scope of the current answer) and proper responses (like restricted file access due to permissions or else)

Fab
  • 14,327
  • 5
  • 49
  • 68
  • actually, for me that didn't work, what worked for me was : return new NotFoundResult(); – Allie Aug 15 '19 at 06:38
  • Also checking `File.Exists()` is bad practice (https://github.com/dotnet/dotnet-api-docs/issues/3277) you should rather catch the `FileNotFoundException` of `ReadAllBytes()` – Daniel Habenicht Jun 30 '21 at 21:08
  • @DanielHabenicht Using File.Exists() is not a bad practice. It's assuming that file will exist in all cases after the check that is a bad practice. In most of the use cases, calling File.Exists will avoid throwing an exception when the file doesn't exist. – Fab Jul 14 '21 at 15:01
  • Yes, but if your sole use case is to serve that file. Throwing and catching the error instead of checking before is more clear than writing duplicate code. – Daniel Habenicht Jul 14 '21 at 17:21