Monday, July 08, 2013

Simplest Example of Posting File + Object to ASP.NET Web API

I searched through quite a few samples of posting multipart (I need to post a file and a custom object as with a form using Web API Client Libraries for 4.0) data to an ASP.NET Web API 4 service.  I couldn't get MultipartFormDataStreamProvider.FileData to populate on the server.  All the examples I looked at were really complicated (even current ones).

It turns out I was simply calling the wrong overload of MultipartFormDataContent.Add (see comment in client portion below).

Client:

var client = new HttpClient() ;
var formData = new MultipartFormDataContent();
/// FileContent code from stackoverflow.com/questions/17497584/cant-post-a-file-to-asp-net-web-api-from-httpclient
formData.Add( new FileContent( @"C:\temp\killme.xml" ), "name_not_used", "killme.xml" );  // third param causes provider.FileData on server to be populated.
formData.Add( new ObjectContent<MyCustomObject>( new MyCustomObject{ Path=@"C:\temp\killme.xml" }, new JsonMediaTypeFormatter() ), "options" );
client.PostAsync( "http://localhost:52800/api/Import", formData ).ContinueWith( ( task ) =>
{
    Console.WriteLine( task.Result.StatusCode.ToString() );
    client.Dispose();
} );

Server:

public HttpResponseMessage Post()
{
    if ( Request.Content.IsMimeMultipartContent() )
    {
        string root = HttpContext.Current.Server.MapPath( "~/App_Data" );  // This call can't be in async portion.
        var provider = new MultipartFormDataStreamProvider( root );

        try
        {
            Request.Content.ReadAsMultipartAsync( provider ).ContinueWith( task =>
            {
                var options = JsonConvert.DeserializeObject<MyCustomObject>( provider.FormData["options"] );
                if ( !task.IsFaulted )
                {
                    foreach ( MultipartFileData file in provider.FileData )                    {
                        Debug.Print( file.Headers.ContentDisposition.FileName );   // file name parameter to formData.Add back on client.
                        Debug.Print( "Server file path: " + file.LocalFileName );  // posted file automatically streamed to path specified by root var above.
                    }
                }
            } );

        }
        catch ( Exception e )
        {
            ;  // TBD
        }
        return new HttpResponseMessage( HttpStatusCode.Accepted );
    }
    else
    {
        return new HttpResponseMessage( HttpStatusCode.BadRequest );
    }
}

Friday, July 05, 2013

Web API Post with a String

I was trying for the longest time to get my first ASP.NET Web API project up and running with the simplest post.  I'm using Visual Studio 2012 with Web API 4 (along with the Web API Client Libraries nuget package).  My simplest get was working, but my post wasn't.

Here's my controller:

    public class ImportController : ApiController
    {

        public string Get()
        {
            return "hi mom";
        }

        public HttpResponseMessage Post( object s )
        {
            return new HttpResponseMessage( HttpStatusCode.Accepted );
        }

    }

Originally I had that parameter to the Post method as a string.  Then I tried to just post up using a simple HTML form with an input (text box) form element.  That didn't work.  Nor did this:

client.PostAsync( "http://localhost:52800/api/Import", new StringContent( "where my service at?" ) {  } ).ContinueWith( ( task ) =>
{
    Console.WriteLine( task.Result.StatusCode.ToString() );
    client.Dispose();
} );

Nor did anything else I tried.  I suppose the built-in deserializer just wouldn't match anything posted up to a string (nor scalar types I believe).  So after changing the parameter type to object and changing the client call from "PostAsync" to "PostAsJsonAsync" it worked.

Hope it can save someone some time.