2

I am trying to upload an image along with some json data inside a MultipartFormDataContent object. But my webapi is not receiving the requests properly for some reason. Initially the api was rejecting the request outright with a 415 response. To fix this I added an xml formatter for multipart/form-data to WebApiConfig.cs

 public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
        config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data"));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

This seemed to work for the most part, I tested it using ARC and it received all of the parts of the multipart, the only issue was the string content didn't appear in the form,it was in the files but I put that down to the request not being formatted as a StringContent object.

The current issue I am having is that my Xamarin app, when sending the multipart request doesn't seem to be posting anything. When the request hits the API Controller, the content headers are there and everything, but the files and form fields are both empty.

After doing some research it seems that I have to write a custom MediaTypeFormatter but none of the ones I have found seem to be the one I am looking for.

Here is the rest of my code:

Api Controller:

[HttpPost]
    public SimpleResponse UploadImage(Image action)
    {
        SimpleResponse ReturnValue = new SimpleResponse();

        try
        {
            if (HttpContext.Current.Request.Files.AllKeys.Any())
            {
                var httpPostedFile = HttpContext.Current.Request.Files["uploadedImage"];
                var httpPostedFileData = HttpContext.Current.Request.Form["imageDetails"];

                if (httpPostedFile != null) 
                {
                    MiscFunctions misctools = new MiscFunctions();

                    string fileName = User.Identity.Name + "_" + misctools.ConvertDateTimeToUnix(DateTime.Now) + ".jpg";
                    string path = User.Identity.Name + "/" + DateTime.Now.ToString("yyyy-mm-dd");
                    string fullPath = path + fileName;

                    UploadedFiles uploadedImageDetails = JsonConvert.DeserializeObject<UploadedFiles>(httpPostedFileData);

                    Uploaded_Files imageDetails = new Uploaded_Files();

                    imageDetails.FileName = fileName;
                    imageDetails.ContentType = "image/jpeg";
                    imageDetails.DateCreated = DateTime.Now;
                    imageDetails.UserID = User.Identity.GetUserId();
                    imageDetails.FullPath = fullPath;

                    Stream imageStream = httpPostedFile.InputStream;
                    int imageLength = httpPostedFile.ContentLength;

                    byte[] image = new byte[imageLength];

                    imageStream.Read(image, 0, imageLength);

                    Image_Data imageObject = new Image_Data();
                    imageObject.Image_Data1 = image;

                    using (var context = new trackerEntities())
                    {
                        context.Image_Data.Add(imageObject);
                        context.SaveChanges();

                        imageDetails.MediaID = imageObject.ImageID;
                        context.Uploaded_Files.Add(imageDetails);
                        context.SaveChanges();
                    }

                    ReturnValue.Success = true;
                    ReturnValue.Message = "success";
                    ReturnValue.ID = imageDetails.ID;
                }
            }
            else
            {
                ReturnValue.Success = false;
                ReturnValue.Message = "Empty Request";
                ReturnValue.ID = 0;
            }
        }
        catch(Exception ex)
        {
            ReturnValue.Success = false;
            ReturnValue.Message = ex.Message;
            ReturnValue.ID = 0;
        }

        return ReturnValue;
    }

Xamarin App Web Request:

public async Task<SimpleResponse> UploadImage(ImageUpload action)
    {
        SimpleResponse ReturnValue = new SimpleResponse();

        NSUserDefaults GlobalVar = NSUserDefaults.StandardUserDefaults;
        string token = GlobalVar.StringForKey("token");

        TaskCompletionSource<SimpleResponse> tcs = new TaskCompletionSource<SimpleResponse>();

        try
        {
            using (HttpClient httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                httpClient.DefaultRequestHeaders.Add("Accept", "application/xml");

                using (var httpContent = new MultipartFormDataContent())
                {
                    ByteArrayContent baContent = new ByteArrayContent(action.Image.Data);
                    baContent.Headers.ContentType = new MediaTypeHeaderValue("Image/Jpeg");

                    string jsonString = JsonConvert.SerializeObject(action.UploadFiles);

                    StringContent stringContent = new StringContent(jsonString);
                    stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

                    using (HttpResponseMessage httpResponse = await httpClient.PostAsync(new Uri("http://10.0.0.89/api/Location/UploadImage"), httpContent))
                    {
                        string returnData = httpResponse.Content.ReadAsStringAsync().Result;

                        SimpleResponse jsondoc = JsonConvert.DeserializeObject<SimpleResponse>(returnData);

                        ReturnValue.ID = jsondoc.ID;
                        ReturnValue.Message = jsondoc.Message;
                        ReturnValue.Success = jsondoc.Success;
                    }
                }
            }
        }
        catch(WebException ex)
        {
            ReturnValue.Success = false;

            if (ex.Status == WebExceptionStatus.Timeout)
            {
                ReturnValue.Message = "Request timed out.";
            }
            else
            {
                ReturnValue.Message = "Error";
            }

            tcs.SetResult(ReturnValue);
        }
        catch (Exception e)
        {
            ReturnValue.Success = false;
            ReturnValue.Message = "Something went wrong";
            tcs.SetResult(ReturnValue);
        }

        return ReturnValue;
    }
geolaw
  • 412
  • 1
  • 12
  • 26
  • At what point in making the request do you add the parts to the `httpContent`? Example code does not show anything so you are basically sending an empty `MultipartFormDataContent` – Nkosi Jan 23 '18 at 11:02
  • check the accepted answer in the link: https://forums.xamarin.com/discussion/105805/photo-json-in-xamarin-post-webservice#latest. I think it gives some light on your issue. – Sreejith Sree Jan 23 '18 at 11:31

2 Answers2

0

You are not adding the parts to the content before trying to send it

//...code removed for brevity

httpContent.Add(baContent, "uploadedImage");
httpContent.Add(stringContent, "imageDetails");

//...send content

On the server side you can check this answer

Http MultipartFormDataContent

on how to read the incoming multi-part request

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I feel stupid for missing that, must have removed it while messing with it. It didn't fix the issue, however the content length of the request does seem to match with that of request payload, so now I'm thinking that the body is populated, but the httpcontext isn't handling it properly. – geolaw Jan 23 '18 at 12:20
  • I am running into other issues, however it does seem to have fixed the issue I have mentioned in this question, if you would like to post the link as an answer I will accept. Thanks for taking the time – geolaw Jan 23 '18 at 12:33
-1
try
   {
var jsonData = "{your json"}";
var content = new MultipartFormDataContent();

content.Add(new StringContent(jsonData.ToString()), "jsonData");
try
   {
     //Checking picture exists for upload or not using a bool variable
     if (isPicture)
       {
         content.Add(new StreamContent(_mediaFile.GetStream()), "\"file\"", $"\"{_mediaFile.Path}\"");
       }
     else
      {
         //If no picture for upload
         content.Add(new StreamContent(null), "file");
      }
   }
  catch (Exception exc)
   {
      System.Diagnostics.Debug.WriteLine("Exception:>" + exc);
   }

var httpClient = new HttpClient();
var response = await httpClient.PostAsync(new Uri("Your rest uri"), content);

 if (response.IsSuccessStatusCode)
 {
   //Do your stuff  
 } 
}
catch(Exception e)
 {
 System.Diagnostics.Debug.WriteLine("Exception:>" + e);
}

Where _mediaFile is the file selected from gallery or camera. https://forums.xamarin.com/discussion/105805/photo-json-in-xamarin-post-webservice#latest

Sreejith Sree
  • 3,055
  • 4
  • 36
  • 105