I have built a small windows forms application just to play around with image recognition. I have trained a model, and I have built a windows forms application that takes my webcam image stream, uploads frames as JPG to an AWS S3 bucket, then passes the publicly readable URL to the Vision API to provide scoring on the tags.
If I pass the image via a POSTMAN call, it works fine, however within my code, I get the following error:
{StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
apim-request-id: 8d5759e5-d32a-4ba2-8b54-16f3a3f1aa40
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
x-content-type-options: nosniff
Date: Thu, 20 Sep 2018 00:49:39 GMT
Content-Length: 116
Content-Type: application/json; charset=utf-8
}}
According to the documentation:
Response 415 Unsupported media type error. "Content-Type" does not match the post content.
For image URL, "Content-Type" should be application/json For binary image data, "Content-Type" should be application/octet-stream
As you will see from my code below, I am setting the right content type. I will just post the function dealing with posting to the API as that's where the issue must be. Any help would be appreciated.
Firstly, the evidence that the call via POSTMAN works:
Here is my method that fails. You can see I set the right content type, and I'm using the same images for testing between my code and POSTMAN.
/// <summary>
/// Take a provided image url and return the Vision API results
/// </summary>
/// <param name="url">a valid HTTP or HTTPS url containing a reference to a JPG image</param>
/// <returns></returns>
static async Task<string> GenerateVisionResult(string url)
{
string visionResult = null;
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://southcentralus.api.cognitive.microsoft.com/customvision/v2.0/Prediction/3dd9e6c3-aac4-4da1-8c74-cd2d581d4782/url?iterationId=5c1b1548-98d7-45b0-a0e7-f397f35ffcd9");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Prediction-Key", "<redacted for post on StackOverflow>");
var UrlBody = new UrlBody
{
Url = url
};
var httpRequestMessage = new HttpRequestMessage
{
Content = new StringContent(JsonConvert.SerializeObject(UrlBody),Encoding.UTF8),
Method = HttpMethod.Post
};
HttpResponseMessage httpResponse = await client.PostAsync(client.BaseAddress, httpRequestMessage.Content);
if (httpResponse.IsSuccessStatusCode)
{
visionResult = await httpResponse.Content.ReadAsStringAsync();
}
else
{
visionResult = httpResponse.StatusCode.ToString();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
return visionResult;
}
EDIT: I think I just realised my mistake, in the way I'm handling adding the url to the POST method. I'll report back once i've tried to fix it.
EDIT2: Nope. Still no dice. I thought maybe I wasn't seralising the URL object correctly, but i've tested and the content is as expected, just serialising the following object:
public class UrlBody
{
public string Url { get; set; }
}
Thanks to Jamie for his help with Fiddler, but I found the issue. You can't pass the content to the POST method the way I have in my original code. You have to convert the content into something that can be passed as HttpContent to the post method, e.g.
var content = JsonConvert.SerializeObject(url);
var buffer = System.Text.Encoding.UTF8.GetBytes(content);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Then you will get a valid response.