I am trying to do a post with HttpClient to a standard REST API. The post method I am targeting requires two key/value pairs, name and size. When I perform the post using the code below, the API returns the error "Required keys missing: size." However, I've clearly added it to the FormUrlEncodedContent.
I'm wondering if there is an encoding issue, or if I am using one of wrong types of HttpContent.
public async Task<OHDrive> Create(OHDrive drive)
{
string json = "";
var values = new Dictionary<string, string>();
//name and size are required
if (string.IsNullOrEmpty(drive.Name) || drive.Size == 0)
throw new Exception("Name and Size are required for drive creation");
values["name"] = drive.Name;
values["size"] = drive.Size.ToString();
if (drive.UserID != null)
values["user"] = drive.UserID;
if (drive.Encryption != null)
values["encryption:cipher"] = drive.Encryption;
var content = new FormUrlEncodedContent(values);
using (HttpClient client = OHUtilities.CreateClient(userID, secretKey))
{
var url = urlBase + "create";
var response = await client.PostAsync(url, content);
json = await response.Content.ReadAsStringAsync();
}
return JsonConvert.DeserializeObject<OHDrive>(json);
}
Update:
I tried switching the order of the key/value pairs, like this:
values["size"] = drive.Size.ToString();
values["name"] = drive.Name;
I then got the opposite error message: "Required keys missing: name." It's like the API is not seeing the second param for some reason. I checked the request in Fiddler and everything appears to be correct.
Related
I am trying to send a request on a model on sagemaker using .NET. The code I am using is:
var data = File.ReadAllBytes(#"C:\path\file.csv");
var credentials = new Amazon.Runtime.BasicAWSCredentials("","");
var awsClient = new AmazonSageMakerRuntimeClient(credentials, RegionEndpoint.EUCentral1);
var request = new Amazon.SageMakerRuntime.Model.InvokeEndpointRequest
{
EndpointName = "EndpointName",
ContentType = "text/csv",
Body = new MemoryStream(data),
};
var response = awsClient.InvokeEndpoint(request);
var predictions = Encoding.UTF8.GetString(response.Body.ToArray());
the error that I am getting on awsClient.InvokeEndpoint(request)
is:
Amazon.SageMakerRuntime.Model.ModelErrorException: 'The service
returned an error with Error Code ModelError and HTTP Body:
{"ErrorCode":"INTERNAL_FAILURE_FROM_MODEL","LogStreamArn":"arn:aws:logs:eu-central-1:xxxxxxxx:log-group:/aws/sagemaker/Endpoints/myEndpoint","Message":"Received
server error (500) from model with message \"\". See
"https:// url_to_logs_on_amazon"
in account xxxxxxxxxxx for more
information.","OriginalMessage":"","OriginalStatusCode":500}'
the url that the error message suggests for more information does not help at all.
I believe that it is a data format issue but I was not able to find a solution.
Does anyone has encountered this behavior before?
The problem relied on the data format as suspected. In my case all I had to do is send the data as a json serialized string array and use ContentType = application/json because the python function running on the endpoint which is responsible for sending the data to the predictor was only accepting json strings.
Another way to solve this issues is to modify the python function which is responsible for the input handling to accept all content types and modify the data in a way that the predictor will understand.
example of working code for my case:
var data = new string[] { "this movie was extremely good .", "the plot was very boring ." };
var serializedData = JsonConvert.SerializeObject(data);
var credentials = new Amazon.Runtime.BasicAWSCredentials("","");
var awsClient = new AmazonSageMakerRuntimeClient(credentials, RegionEndpoint.EUCentral1);
var request = new Amazon.SageMakerRuntime.Model.InvokeEndpointRequest
{
EndpointName = "endpoint",
ContentType = "application/json",
Body = new MemoryStream(Encoding.ASCII.GetBytes(serializedData)),
};
var response = awsClient.InvokeEndpoint(request);
var predictions = Encoding.UTF8.GetString(response.Body.ToArray());
I'm having trouble getting any type of response from the Listing Recommendation API. I keep getting a generic 500 message on my return. I've set up my headers the way they recommend here: https://developer.ebay.com/devzone/listing-recommendation/Concepts/MakingACall.html
I've tried using the information from the documentation on the call here: https://developer.ebay.com/devzone/listing-recommendation/CallRef/itemRecommendations.html#Samples
But every variation of my code comes up bad. Below is a sample of the code. I've tried it with and without the commented out line of code with the same results. It always fails on the line response.GetResponseStream. Thanks for your help.
public static void test(string AuthToken, string listing, log4net.ILog log)
{
string url = "https://svcs.ebay.com/services/selling/listingrecommendation/v1/item/" + listing + "/itemRecommendations/?recommendationType=ItemSpecifics";
var listingRecommendationRequest = (HttpWebRequest)WebRequest.Create(url);
listingRecommendationRequest.Headers.Add("Authorization", "TOKEN " + AuthToken);
listingRecommendationRequest.ContentType = "application/json";
listingRecommendationRequest.Accept = "application/json";
listingRecommendationRequest.Headers.Add("X-EBAY-GLOBAL-ID", "EBAY-US");
listingRecommendationRequest.Method = "GET";
//listingRecommendationRequest.Headers.Add("recommendationType", "ItemSpecifics");
var response = (HttpWebResponse)listingRecommendationRequest.GetResponse();
string result;
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
var reader = new JsonTextReader(new StringReader(result));
while (reader.Read())
{
if (reader.Value != null)
{
// Read Json values here
var pt = reader.Path;
var val = reader.Value.ToString();
}
}
}
Edit: Adding image of what I'm trying to accomplish. I'm trying to get the item specifics recommendations that are suggested by eBay when manually editing a listing. These suggestions change based on what is in your title.
This should be pretty straightforward, but I can't get it to work. I'm using these instructions. I've tried using the Nuget package in my PCL targeting 259 as as well as in another PCL targeting 7. Both PCLs never return after these lines. I've tried both.
//text = await client.RecognizeTextAsync(imageURL);
text = await client.RecognizeTextAsync(imageURL, languageCode: "en", detectOrientation: true);
Here is the REST code I've also tried. This works with my HTTP Helper.
public static async Task<OcrResults> PostReceiptAsync(string imageURL)
{
try
{
var apiKey = "KEY 1";
var content = await HttpHelper.Request(null, String.Format("https://eastus2.api.cognitive.microsoft.com/vision/v1.0?language=en&detectOrientation=true&subscription-key={0}&url={1}", apiKey, imageURL), null, HttpRequestType.POST);
return Mapper<OcrResults>.MapFromJson(await content.ReadAsStringAsync());
}
catch (Exception e)
{
throw;
}
}
In both methods, I'm trying to use a Blob image URL. Here is my calling method.
takePhoto.Clicked += async (sender, args) =>
{
}
It's almost seems like a deadlock issue.
Any help is much appreciated. Thanks!
UPDATE: I finally got the REST method to work with Android and UWP using this bit of code, but it fails in my iOS app. The main problem I had is that the URL that you copy out of Azure does not include the ocr? parameter. The Json Mapper is something that my SDK uses. OcrResults comes from the Project Oxford Nuget package.
public static async Task<OcrResults> ProcessReceiptAsync(string imageUrl)
{
// Instantiate a HTTP Client
var client = new HttpClient();
var apiKey = "KEY 1";
// Request parameters and URI
string requestParameters = "language=en&detectOrientation =true";
string uri = "https://eastus2.api.cognitive.microsoft.com/vision/v1.0/ocr?" + requestParameters;
// Pass subscription key thru the HTTP Request Header
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
// Format Request body
byte[] byteData = Encoding.UTF8.GetBytes($"{{\"url\": \"{imageUrl}\"}}");
using (var content = new ByteArrayContent(byteData))
{
// Specify Request body Content-Type
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// Send Post Request
HttpResponseMessage response = await client.PostAsync(uri, content);
// Read Response body into the model
//return await response.Content.ReadAsStringAsync(OcrResults);
var result = Mapper<OcrResults>.MapFromJson(await response.Content.ReadAsStringAsync());
return result;
}
}
UPDATE 2: This method also works on Android and UWP, but fails with a BadRequest in my iOS app. I wanted to change up how the image URL gets encoded.
public static async Task<OcrResults> ProcessReceiptAsync2(string imageUrl)
{
// Instantiate a HTTP Client
var client = new HttpClient();
var apiKey = "KEY 1";
// Request parameters and URI
string requestParameters = "language=en&detectOrientation =true";
string uri = "https://eastus2.api.cognitive.microsoft.com/vision/v1.0/ocr?" + requestParameters;
// Pass subscription key thru the HTTP Request Header
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", apiKey);
// Format Request body
var dict = new Dictionary<string, string>();
dict.Add("url", imageUrl);
//var param = JsonConvert.SerializeObject($"{{\"url\": \"{imageUrl}\"}}");
var param = JsonConvert.SerializeObject(dict);
HttpContent contentPost = new StringContent(param, Encoding.UTF8, "application/json");
// Specify Request body Content-Type
//contentPost.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Send Post Request
HttpResponseMessage response = await client.PostAsync(uri, contentPost);
// Read Response body into the model
//return await response.Content.ReadAsStringAsync(OcrResults);
var result = Mapper<OcrResults>.MapFromJson(await response.Content.ReadAsStringAsync());
return result;
}
UPDATE 3: I tried this code, but it failed with a Bad Request on iOS. I'm using the ExtractTextFromImageUrlAsync. I did get a "file too large" error using the ExtractTextFromImageStreamAsync, so that method appears to have worked.
https://github.com/HoussemDellai/Microsoft-Cognitive-Services-API/blob/master/ComputerVisionApplication/ComputerVisionApplication/Services/ComputerVisionService.cs
UPDATE 4: I removed Microsoft.Net.Http 2.9 from all the Projects and commented out System.Net.Http from all the app.config files. Both methods now work in the Android and UWP app. I still can't get the iOS app to work. The Project Oxford Nuget fails at this line requestObject.url = imageUrl; (see GitHub). The Rest method fails with a Bad Request, but I managed to catch a "InvalidImageUrl" message, so something is happening when the imageUrl is encoded. At this stage, I believe this is a Xamarin issue that's specific to iOS 10.4.0.
I'm attempting to send back my token to request further information about the user logging in. I want to get json back.
using the code below I get an exception "The given key was not present in the dictionary."
What am I doing wrong?
public void Login(Action<Mobile.Google.Account> googleLoginCompleted, Action googleLoginFailed)
{
var auth = new OAuth2Authenticator(
clientId: "myid",
clientSecret: "secret",
scope: "https://www.googleapis.com/auth/plus.login",
authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
redirectUrl: new Uri("http://adults.wicareerpathways.org"),
accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token"));
auth.Completed += (sender, e) =>
{
if (e.IsAuthenticated)
{
var values = e.Account.Properties;
var access_token = values["access_token"];
var googleAccount = new Mobile.Google.Account
{
Username = e.Account.Username,
Properties = e.Account.Properties
};
try
{
var request = HttpWebRequest.Create(string.Format(#"https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + access_token + "&format=json", ""));
request.ContentType = "application/json";
request.Method = "GET";
var user_ID = values["user_id"];
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
System.Console.Out.WriteLine("Stautus Code is: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
GoogleAccountDetails result = Newtonsoft.Json.JsonConvert.DeserializeObject<GoogleAccountDetails>(content);
googleAccount.Username = result.id;
if (googleLoginCompleted != null)
{
googleLoginCompleted(googleAccount);
}
}
}
}
catch (Exception exx)
{
System.Console.WriteLine(exx.ToString());
}
}
Rather than directly solving your problem, let me give you some advice on how to solve this yourself.
It seems obvious that a key isn't present. The first important thing is to figure out which line exactly the problem is. I see two possibilities, "user_id" and "access_token". One of those two items must not be included. Both come from values. Try printing out values, and see what you come up with. In particular, print the keys, and see if you have a typo.
Looking at your code, I suspect your error is in the second, var user_ID = values["user_id"]; First of all, you don't seem to use it anywhere. Secondly, it is different than the clientId used earlier. In fact, I would suggest just using clientId, if that is what you intend, which will give you consistency in your requesting values.
I can use the following code which works fine to log in using my Web API. However, whensomething goes wrong and an error is returned, I don't know how to get at the contect of the HttpResponseMessage. If I just use the ReadAsStringAsync() method, I get the error in the string, but what type is it? If I know the type I can get the object.
HttpResponseMessage response = await client.PostAsJsonAsync("api/Login", loginObject);
if (response.IsSuccessStatusCode)
{
var _logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
}
else
{
// an error has occured, but what is the type to read?
var test = await response.Content.ReadAsStringAsync();
}
On the server it is returning;
BadRequest(ModelState).
Thanks for any help.
EDIT: I have since resolved the issue like this;
var value = await response.Content.ReadAsStringAsync();
var obj = new { message = "", ModelState = new Dictionary<string, string[]>() };
var x = JsonConvert.DeserializeAnonymousType(value, obj);
The result returned back is an JSON object with a "Message" and a "ModelState" properties.
The "ModelState" state value is an object, whose properties are arrays of strings. The property list of "ModelState" varies from time to time depending on which property is invalid.
Hence, to get a strong-type returned response, why don't you manipulate the ModelState yourself on the server side, and then pass the object to the BadRequest() method
Here is just grabbing raw json in text of error message...
if (!response.IsSuccessStatusCode)
{
dynamic responseForInvalidStatusCode = response.Content.ReadAsAsync<dynamic>();
Newtonsoft.Json.Linq.JContainer msg = responseForInvalidStatusCode.Result;
result = msg.ToString();
}
Try IOStreamReader. This is vb.net, but that's not too hard to convert:
IOStreamReader = New IO.StreamReader(Response.GetResponseStream)
RespStr = IOStreamReader.ReadToEnd
Or
Dim HttpReq As Net.HttpWebRequest = Nothing
Dim HttpStatus As Net.HttpStatusCode = Nothing
HttpResp = CType(HttpReq.GetResponse(), Net.HttpWebResponse)
'verify the response
HttpStatus = HttpResp.StatusCode
try following :
try
{
HttpResponseMessage response = await client.PostAsJsonAsync("api/Login", loginObject);
response.EnsureSuccessStatusCode();
var _logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
return _logonResponse;
}
catch (Exception ex)
{
throw ex;
}