Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web API 2 download file using async Task<IHttpActionResult>

I need to write a method like below to return a text document (.txt, pdf, .doc, .docx etc) While there are good examples of posting file in Web API 2.0 on the web , I couldn't find a relevant one for just downloading one. (I know how to do it in HttpResponseMessage.)

  public async Task<IHttpActionResult> GetFileAsync(int FileId)
  {    
       //just returning file part (no other logic needed)
  }

Does the above needs to be async at all? I am only looking to return stream. (Is that okay?)

More importantly before I end up doing the job one way or the otther, I wanted to know what's the "right" way of doing this sort of job... (so approaches and techniques mentioning this would be greatly appreciated).. thanks.

like image 805
Kcats Wolfrevo Avatar asked Feb 03 '14 16:02

Kcats Wolfrevo


2 Answers

Right, for your above scenario the action does not need to return an async action result. Here I am creating a custom IHttpActionResult. You can check my comments in the below code here.

public IHttpActionResult GetFileAsync(int fileId)
{
    // NOTE: If there was any other 'async' stuff here, then you would need to return
    // a Task<IHttpActionResult>, but for this simple case you need not.

    return new FileActionResult(fileId);
}

public class FileActionResult : IHttpActionResult
{
    public FileActionResult(int fileId)
    {
        this.FileId = fileId;
    }

    public int FileId { get; private set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage();
        response.Content = new StreamContent(File.OpenRead(@"<base path>" + FileId));
        response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");

        // NOTE: Here I am just setting the result on the Task and not really doing any async stuff. 
        // But let's say you do stuff like contacting a File hosting service to get the file, then you would do 'async' stuff here.

        return Task.FromResult(response);
    }
}
like image 162
Kiran Avatar answered Nov 08 '22 13:11

Kiran


Methods are asynchronous if return a Task object, not because are decorated with async keyword. async is only a syntactical sugar to replace this syntax what can get rather complex when there are more tasks combined or more continuations:

public Task<int> ExampleMethodAsync()
{
    var httpClient = new HttpClient();

    var task = httpClient.GetStringAsync("http://msdn.microsoft.com")
        .ContinueWith(previousTask =>
        {
            ResultsTextBox.Text += "Preparing to finish ExampleMethodAsync.\n";

            int exampleInt = previousTask.Result.Length;

            return exampleInt;
        });

    return task;
}

Original sample with async: http://msdn.microsoft.com/en-us/library/hh156513.aspx

async always requires await, this is enforced by compiler.

Both implementations are asynchroous, the only difference is that async+await replaces expands the ContinueWith into "synchronous" code.

Returning Task from controller methods what do IO (99% of cases I estimate) is important because the runtime can suspend and reuse the request thread to serve other requests while the IO operation is in progress. This lowers the chances of running out of thread pool threads. Here's an article on the topic: http://www.asp.net/mvc/overview/performance/using-asynchronous-methods-in-aspnet-mvc-4

So the answer to your question "Does the above needs to be async at all? I am only looking to return stream. (Is that okay?)" is that it makes no difference to the caller, it only changes how your code looks (but not how it works).

like image 29
user3285954 Avatar answered Nov 08 '22 13:11

user3285954