What is wrong with this request (Azure management.azure.com API) - c#

I am trying to get this azure API to work:
https://learn.microsoft.com/en-us/rest/api/machinelearning/webservices/createorupdate
The following code runs a GET request which returns successfully (200 OK and return body), but the Put request to the exact same uri at the bottom fails with a 400 "Bad request".
The code inbetween basically just unwraps the json output of the first request and tried to send the same unchanged data in the input format of the PUT on the same uri.
Could anyone help me see why this fails?
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await httpClient.GetAsync($"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.MachineLearning/webServices/{serviceName}?api-version=2016-05-01-preview");
var json = await response.Content.ReadAsStringAsync();
var f = JsonConvert.DeserializeObject<WebService>(json);
var requestBody = new RequestBody()
{
location = f.Location,
name = f.Name,
tags = f.Tags,
properties = f.Properties
};
var jsoncontent = JsonConvert.SerializeObject(requestBody, Formatting.None);
var content = new StringContent(jsoncontent, Encoding.UTF8, "application/json"); //tried simpler things like "{\"location\":\"West Europe\"}" with no result
var response2 = await httpClient.PutAsync($"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.MachineLearning/webServices/{serviceName}?api-version=2016-05-01-preview", content, new JsonMediaTypeFormatter());
// last line returns a 400 bad request only.
And then I use this custom object;
private class RequestBody
{
public string location { get; set; }
public string name { get; set; }
public WebServiceProperties properties { get; set; }
public IDictionary<string, string> tags { get; set; }
}

It seems it was one of those obvious things.
The ", new JsonMediaTypeFormatter()" snippet at the end ruined it. Removing it fixed the issue and it returns 200 OK without it.

Related

Integration Testing multipart/form-data c#

I have trouble trying to create an integration test for my post call that accepts a viewmodel that has amongst other values, an IFormFile, which makes this call from an application/json to a multipart/form-data
My IntegrationSetup class
protected static IFormFile GetFormFile()
{
byte[] bytes = Encoding.UTF8.GetBytes("test;test;");
var file = new FormFile(
baseStream: new MemoryStream(bytes),
baseStreamOffset: 0,
length: bytes.Length,
name: "Data",
fileName: "dummy.csv"
)
{
Headers = new HeaderDictionary(),
ContentType = "text/csv"
};
return file;
}
My Test Method
public async Task CreateAsync_ShouldReturnId()
{
//Arrange
using var content = new MultipartFormDataContent();
var stringContent = new StringContent(
JsonConvert.SerializeObject(new CreateArticleViewmodel
{
Title = "viewModel.Title",
SmallParagraph = "viewModel.SmallParagraph",
Url = "viewModel.Url",
Image = GetFormFile()
}),
Encoding.UTF8,
"application/json");
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
//Act
var response = await httpClient.PostAsync($"{Url}", content);
//Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
int id = int.Parse(await response.Content.ReadAsStringAsync());
id.ShouldBeGreaterThan(0);
}
My Controller Method
[HttpPost]
public async Task<IActionResult> CreateArticleAsync([FromForm] CreateArticleViewmodel viewModel)
{
var id = await _service.CreateAsync(viewModel).ConfigureAwait(false);
if (id > 0)
return Ok(id);
return BadRequest();
}
It throws a BadRequest without getting inside the method.
The way you are posting the request contents to the API, in your code, is not correct.
When the API expects a FileInfo in the request payload, posting JSON content never works. You need to send the payload as MultipartFormData and not as JSON.
Consider following example.
This is a an API endpoint which expects and model with FileInfo in it as payload.
[HttpPost]
public IActionResult Upload([FromForm] MyData myData)
{
if (myData.File != null)
{
return Ok("File received");
}
else
{
return BadRequest("File no provided");
}
}
public class MyData
{
public int Id { get; set; }
public string Title { get; set; }
// Below property is used for getting file from client to the server.
public IFormFile File { get; set; }
}
This is pretty much the same API as yours.
Following is the client code which calls the above API with file and other model properties.
var apiURL = "http://localhost:50492/home/upload";
const string filename = "D:\\samplefile.docx";
HttpClient _client = new HttpClient();
// Instead of JSON body, multipart form data will be sent as request body.
var httpContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(File.ReadAllBytes(filename));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// Add File property with file content
httpContent.Add(fileContent, "file", filename);
// Add id property with its value
httpContent.Add(new StringContent("789"), "id");
// Add title property with its value.
httpContent.Add(new StringContent("Some title value"), "title");
// send POST request.
var response = await _client.PostAsync(apiURL, httpContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
// output the response content to the console.
Console.WriteLine(responseContent);
The client code is running from a Console application. So when I run this, the expectation is to get File received message in the console and I am getting that message.
Following is the screen capture of the model content at the API end while debugging it.
And if I am calling this API from postman, it would look like following.
I hope this will help you solve your issue.

I am unable to save my Token once I get it from the API call

I am learning as I create, that being said, I have spent quite a few hours on JUST the login/register pages in the app I am trying to make.
I have finally got to the point where I am able to make the API call to get the response back with the information I need.
I just don't know how to save the token once it comes back.
I am using SQLite for local storage, and I have a "Token" nclass to save it to, but I can't figure out how to actually save it and continue forward.
(I could be completely wrong and it doesn't work at all, but that's all part of learning, I guess.)
anyways, here is my Token class
public class Token
{
[PrimaryKey]
public int Id { get; set; }
public string accessToken { get; set; }
public string errorDescription { get; set; }
public DateTime expireDate { get; set; }
public int expireIn { get; set; }
public Token() { }
}
and here is my APIServices class (some stuff is commented because I am working with my buddy to get everything sorted on the API side)
public class ApiServices
{
public string JsonResult { get; private set; }
public async Task<bool> RegisterUserAsync(string email, string name, /*string first_name, string last_name,*/ string password)
{
var client = new HttpClient();
var model = new RegisterBindingModel
{
Email = email,
//FirstName = first_name,
//LastName = last_name,
Name = name,
Password = password,
};
var json = JsonConvert.SerializeObject(model);
HttpContent httpContent = new StringContent(json);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("https://myurl/v1/auth/register", httpContent);
if (response.IsSuccessStatusCode)
{
var result = JsonConvert.DeserializeObject(JsonResult);
return true;
}
return false;
}
public async Task<string> LoginAsync(string email, string password)
{
var keyValues = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("email", email),
new KeyValuePair<string, string>("password", password),
new KeyValuePair<string, string>("grant_type", "password")
};
var request = new HttpRequestMessage(HttpMethod.Post, "https://myurl/auth/login" + "Token");
request.Content = new FormUrlEncodedContent(keyValues);
var client = new HttpClient();
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
JObject jwtDynamic = JsonConvert.DeserializeObject<dynamic>(content);
var accessTokenExpiration = jwtDynamic.Value<DateTime>(".expires");
var accessToken = jwtDynamic.Value<string>("access_token");
//Settings.AccessTokenExpirationDate = accessTokenExpiration;
Debug.WriteLine(accessTokenExpiration);
Debug.WriteLine(content);
return accessToken;
}
}
Lets explain it step by step:
1.Install the sqlite-net-pcl package(Nuget URL). This package will help you to work with database in an easy way.
2.After installing it successfully, You need to add the using statement of SQLite to your class:
using SQLite;
3.In order to make a request to database, you will need to first make a connection to database. to do so, you need to declare a variable for aforementioned goal:
var db = new SQLiteConnection (dbPath);
4.Once you have the database connection object, you are ready to make requests to database. for example to save an object of type Token class, just do this:
var tokenInfo= new Token();
tokenInfo.accessToken="...";//set value to other properties like this
db.Insert(tokenInfo);
These are all you need to do an Insert request to database!
You might want to read more about aforementioned package in these urls:
https://github.com/praeclarum/sqlite-net
https://learn.microsoft.com/en-us/xamarin/android/data-cloud/data-access/using-sqlite-orm

How to Seriailize an c# object into Json, while using httpClient?

I have a little program which should communicate with "Slack". In an older Version I used "Dictionary<string, string>" and then put them into UrlEncodedContent - which worked fine.
Now I am trying to create a Json-object, using Newtonsoft's Nuget-package and (in my opinion) formatting my object the way they say on their website.
Problem is, when I try to make a simple request, my program just runs to one specific line in the code(var response = await _httpClient.SendAsync(request);) and then it just ends. It doesn't throw an exception or display any kind of message, it simply ends on this line. I went through my code step by step while debugging, that's how I know it ends on exactly this line. And I just don't know why!
Now my code:
First, my object...
namespace BPS.Slack
{
public class JsonObject
{
//generally needed parameters
[JsonProperty("ok")]
public bool ok { get; set; }
[JsonProperty("error")]
public string error { get; set; }
[JsonProperty("channel")]
public string channel { get; set; }
[JsonProperty("token")]
private string token = "xoxp-MyToken";
[JsonProperty("as_user")]
public bool as_user = false;
[JsonProperty("username")]
public string username { get;set; }
//--------------------------------
//only needed for textmessages
[JsonProperty("text")]
public string text { get; set; }
//--------------------------------
//for posting messages with data attached
[JsonProperty("initial_comment")]
public string initial_comment { get; set; }
[JsonProperty("file")]
public string file { get; set; }
[JsonProperty("channels")]
public string channels { get; set; }
//--------------------------------
//for getting the latest message from a channel
[JsonProperty("count")]
public string count = "1";
[JsonProperty("unreads")]
public bool unreads = true;
}
}
now the client:
namespace BPS.Slack
{
public class BpsHttpClient
{
private readonly HttpClient _httpClient = new HttpClient { };
public Uri UriMethod { get; set; }
public BpsHttpClient(string webhookUrl)
{
UriMethod = new Uri(webhookUrl);
}
public async Task<HttpResponseMessage> UploadFileAsync(MultipartFormDataContent requestContent)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, UriMethod);
request.Content = requestContent;
var response = await _httpClient.SendAsync(request);
return response;
}
}
}
and the main
namespace TestArea
{
class MainArea
{
public static void Main( string[] args)
{
try
{
Task.WhenAll(SendMessage());
}
catch(Exception ass)
{
Console.WriteLine(ass);
Console.ReadKey();
}
}
private static async Task SendMessage()
{
var client = new BpsHttpClient("https://slack.com/api/im.history");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
var Json = JsonConvert.SerializeObject(JO);
var StringJson = new StringContent(Json, Encoding.UTF8);
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(StringJson);
var Response = await client.UploadFileAsync(content);
string AnswerContent = await Response.Content.ReadAsStringAsync();
Console.WriteLine(AnswerContent);
Console.ReadKey();
}
}
}
I had the same problem in my older version, BUT only as I wanted to DEserialize an answer I got from Slack. It had to do with my object I tried do deserialize the answer into. But this time I can not figure out what's wrong. But, as I said, I do not have any experience with using serialized objects as Json-property to send requests... anyone has an idea what is wrong with my code?
EDIT: This problem is kinda solved. But there is a follow up problem.
Okay, I found out that the reason for the abprubt termination was the
Task.WhenAll(SendMessage());
it should be
Task.WaitAll(SendMessage()); Why??? Somebody said I should use WhenAll, but obviously it doesn't work properly in this case...
Now I get a response from Slack, but now a different problem has arisen. When I use this method:
public async Task<HttpResponseMessage> UploadFileAsync(MultipartFormDataContent requestContent)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UriMethod);
request.Content = requestContent;
var response = await _httpClient.SendAsync(request);
return response;
}
I allways get the answer:
{"ok":false,"error":"invalid_form_data"}
so I tried to explicitly tell it the 'mediaType', I tried "application/json" and others, but with all of them I get the same error. Here is the full method that calls the upper mehtod:
private static async Task SendMessage()
{
var client = new BpsHttpClient("https://slack.com/api/chat.postMessage");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
JO.text = "This is so much fun :D !";
var Json = JsonConvert.SerializeObject(JO, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
var StringJson = new StringContent(Json, Encoding.UTF8, "application/json");
var requestContent = new MultipartFormDataContent();
requestContent.Add(StringJson);
var Response = await client.UploadFileAsync(requestContent);
string AnswerContent = await Response.Content.ReadAsStringAsync();
}
When I use this method:
public async Task<HttpResponseMessage> SendMessageAsync(FormUrlEncodedContent content)
{
var response = await _httpClient.PostAsync(UriMethod, content);
return response;
}
so bascially I am passing "FormUrlEncodedContent" instead of "MultipartFormDataContent" in this, and then I get the response I want and can work wiht it. BUT this i of little use to me since I have to use "MultipartFormDataContent" to be able to send files with my requests.
Anyone have an idea what is failing here? Why does it not like the one content-type but the other one? I'd be gratefull for tipps and ideas!
You are serializing your object to Json and then adding it to a Multipart body, that's quite strange. Unless you're uploading binary data (eg Files), there is no need to use MultipartFormDataContent.
You are can directly post your JsonObject serialized as JSON:
public async Task<HttpResponseMessage> PostJsonAsync(StringContent content)
{
var response = await client.PostAsync(url, content);
return response;
}
var client = new BpsHttpClient("https://slack.com/api/im.history");
JsonObject JO = new JsonObject();
JO.channel = "DCW21NBHD";
var Json = JsonConvert.SerializeObject(JO);
var StringJson = new StringContent(Json, Encoding.UTF8);
var Response = await client.PostJsonAsync(content);
Also this is should be POST on the UploadFileAsync function.
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, UriMethod);
so I figured out that in the Main() the problem was this:
Task.WhenAll(SendMessage());
I should instead use:
Task.WaitAll(SendMessage());
Anyone who has more knowledge on this, please elaborate why!

C# Api controller not receiving json parameter

I am trying to create a basic test web api, and use a standard controller to test call it.
When I run it, by putting
http://localhost:55144/home/testapi
it'll run the catcher function and completely ignore the parameter.
Then, the catcher will happily return a value, which can be seen in the calling code.
I have tried various combinations of putting [FromBody], changing the type of the parameter in TestApiMethod, and seeing if making a list or array makes any difference.
I've noticed a couple of weird things:
- I'm not using the parameter in the code of TestApiMethod, but Visual Studio is not giving me an unused variable warning.
- If I make the type of the parameter testString a string or even an int, the code below will route to the catcher. If I make it some variation of a model or a Jobject, it will not. It gets as far as running
HttpResponseMessage response = await client.PostAsJsonAsync("api/activity", sendData);
then just returns to the web page.
Here's the code:
Models
public class testStringModel
{
public string testString { get; set; }
}
public class apiResponse
{
public string response { get; set; }
}
Home controller calling Api:
public void TestApi()
{
Task myTask = testApiCall();
}
private async Task<string> testApiCall()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:55144");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
testStringModel data = new testStringModel { testString = "cheese" };
string jsonData = JsonConvert.SerializeObject(data);
var sendData = new StringContent(jsonData, Encoding.UTF8, "application/json");
//var sendData = new Dictionary<string, string>
//{
// {"testString", "cheese"}
//};
HttpResponseMessage response = await client.PostAsJsonAsync("api/activity", sendData);
string responseBodyAsText = await response.Content.ReadAsStringAsync();
dynamic stuff = JObject.Parse(responseBodyAsText);
string finalResponse = stuff.response;
return finalResponse;
}
}
The api:
namespace ApplicationActivity
{
public class ActivityController : ApiController
{
[System.Web.Http.HttpPost]
public HttpResponseMessage Catcher()
{
apiResponse apiResponseObject = new apiResponse();
apiResponseObject.response = "You have somehow wound up in the catcher";
string json = JsonConvert.SerializeObject(apiResponseObject);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent(json, Encoding.Unicode, "application/json");
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(20)
};
return response;
}
[System.Web.Http.HttpPost]
public HttpResponseMessage TestApiMethod(string testString)
{
apiResponse apiResponseObject = new apiResponse();
apiResponseObject.response = "OK from test";
string json = JsonConvert.SerializeObject(apiResponseObject);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent(json, Encoding.Unicode, "application/json");
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(20)
};
return response;
}
}
}
Please will you tell me what I'm doing wrong with my code, how to fix it and what is happening when the code doesn't get to the catcher?
Thanks.
It turns out that I was using an older version of visual studio and as a result the whole thing got really confused with whether is was running .net core or not.
Upgrading to the latest and making sure the latest .net core is installed solved most of my troubles

Is there a way to send a List<> object through POST Method of REST API from one project through other?

I have a class Contacts.cs which contains class ContactsDTO, as shown in the below code
namespace WindowsScheduling
{
public class ContactsDTO
{
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactAddress1 { get; set; }
public string Class { get; set; }
}
}
Now I want to send an object List<ContactsDTO> to an another project through REST API.
The method which I have implemented for this purpose is :-
public string SendContactToKentico( List<ContactsDTO> objDeserializedMessage)
{
var RemoteURL = ConfigurationManager.AppSettings["RemoteURL"].ToString();
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(RemoteURL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage responseMessage = client.GetAsync(RemoteURL + "/Schedule/GetContactsByScheduler",objDeserializedMessage).Result;
return msg;
}
But here my objectDeserializedobject is showing an error :-
Cannot convert from 'System.Collection.Generic.List' to 'System.Net.Http.HttpCompletionOption'
You can't send a body with a GET request. Make sure to read the documentation for the classes you are using. The error message is telling you that none of the overloads for GetAsync take an argument representing body data. Choose the appropriate http verb for sending content; probably POST.
You could probably try something like this...
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(RemoteURL);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string url = $"endPoint";
ObjectContent content = new ObjectContent<List<ContactsDTO>>(objMessage, new JsonMediaTypeFormatter());
HttpResponseMessage response = await client.PostAsync(url, content);
Please note: you may not need to use objDeserializedMessage, and you could just use objMessage. And also, I have done PostAsync opposed to GetAsync.
Also, do you want to make a GET request or a POST?
UPDATE 1 : Also check the response's status code like below
if(response.StatusCode == HttpStatusCode.OK){
// handle the response
ExpectedResponseModel responseModel = await response.Content.ReadAsAsync<ExpectedResponseModel >();
}
else {
// request failed, handle error
}
Here, ExpectedResponseModel could be made of the response you're expecting.

Categories