Thursday, July 27, 2017

How do I pass both a POCO and a file to a Microsoft.AspNetCore.Mvc.Controller at once?

It turns out this is modestly challenging and unintuitive and that you cannot really pass an object at the method signature of the action catching stuff if you are catching a file too in this manner. Instead try something like so:

[HttpPut]
[Route("api/widget/{keyId}")]
public bool UpdateWidgetWithInstructionManual(long keyId)
{
   HttpRequest request = this.Request;
   IFormFile file = request.Form.Files[0];
   string serializedWidget = request.Form.Keys.ToList()[0];
   serializedWidget = serializedWidget.Replace("%22", "\"");
   Widget widget = JsonConvert.DeserializeObject<Widget>(serializedWidget);
   var fileName = file.FileName;
   var fileContentType = file.ContentType;
   var fileData = default(byte[]);
   using (var reader = new StreamReader(file.OpenReadStream()))
   {
      using (var memoryStream = new MemoryStream())
      {
         var buffer = new byte[file.Length];
         var bytesRead = default(int);
         while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
         {
            memoryStream.Write(buffer, 0, bytesRead);
         }
         fileData = memoryStream.ToArray();
      }
   }
   _myStaticStuff.Act(fileName, file.Length, fileContentType, fileData, widget, keyId,
         _widgetRepository);
   return true;
}

 
 

I am deferring saving the stuff I collect to a database in the third to last line above by calling another method. The signature for as much would look like so:

public static void Act(string fileName, long fileLength, string fileContentType, byte[]
      fileData, Widget widget, long keyId, WidgetRepository widgetRepository)
{

 
 

What does this do? Well, you can use your imagination. The point of this exercise is not really to show the savings of a bytes array to a database, but instead to exhibit taking in both an object and a file at a controller action. This does beg the question of how does one hand off to the action to begin with. In TypeScript in an Angular 2 application you would have to have some magic like this wherein this.http is an Http looped in from @angular/http as an import. Note: no options! Headers seem to sabotage things.

public updateWidgetWithInstructionManual(widget: any, keyId: number, file: any): void {
   let url = "http://www.example.com/api/widget/";
   let formData:FormData = new FormData();
   formData.append('uploadFile', file, file.name);
   formData.append(JSON.stringify(widget), 'widget');
   this.http.put(url + keyId, formData, {headers:{}})
         .toPromise()
         .then(function(data){
            console.log('success');
         },function(error){
            console.log(error);
         });
}

No comments:

Post a Comment