c# creating HTTP POST web request - c#

hello guys i have swagger url http://somehost/swagger/index.html
end methods there as shown on image:
I am trying to create HTTP POST web request refer to one of the post method.
using System;
using System.Collections.Generic;
using System.Net.Http;
namespace SwaggerConsoleAPP
{
class Program
{
static void Main(string[] args)
{
postRequest("http://somehost/api/Referral/GetReferralsByPersonalIdNumber");
Console.ReadKey();
}
async static void postRequest (string url){
IEnumerable<KeyValuePair<string, string>> queries = new List<KeyValuePair<string, string>>() {
new KeyValuePair<string, string>("1133221221","5642")
};
HttpContent q = new FormUrlEncodedContent(queries);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage responce = await client.PostAsync(url,q))
{
using (HttpContent content = responce.Content)
{
string mycontent = await content.ReadAsStringAsync();
Console.WriteLine(mycontent);
}
}
}
}
}
}
this is console application but console writes error:
{"":["The input was not valid."]}
This is model of method
Any help?

In my opinion, key value pair is causing the problem.
The API is expecting personalIDNumber and pharmacyID as two separate parameters.
Simplest solution is to just create a class with these two properties:
public class InputFields
{
public string personalIDNumber{get;set;}
public string pharmacyId{get;set;}
}
This will work as long as your API has mode of this type as input parameter.
Please note that sending two parameters in HTTP body as post you may have to code additionally as mentioned here.
Hope this helps.

Related

Bad Request When Using PostAsJsonAsync C#

I am trying to Post a model to my MongoDB, but my Blazor app's post command does not even reach my API's Post method in the Controller. I've seen some topics about this and also tried to debug this issue for hours.
I am trying to post a really simple model.
public string MomentCategoryName { get; set; }
The button click activates this method. (I hardcoded a CategoryName for this one.)
public async Task PostAsync(MomentCategoryModel momentCategory)
{
using (HttpResponseMessage response = await _api.ApiClient.PostAsJsonAsync("api/MomentCategory/Create", momentCategory))
{
if (!response.IsSuccessStatusCode)
{
throw new Exception();
}
}
}
The baseuri and the client is initialized in another class, but the whole uri will look like this: https://localhost:7141/api/MomentCategory/Create
The PostMomentCategory in the MomentCategoryController gets a MomentCategoryModel from another library:
[BsonId]
[BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)]
public string Id { get; set; }
public string MomentCategoryName { get; set; }
And then in the this code should run, but the compiler never gets to this, it never runs:
[HttpPost]
[Route("Create")]
public void PostMomentCategory(MomentCategoryModel model)
{
_momentCategoryData.CreateMomentCategory(model);
}
Because of that I get back a Bad Request. But when I do a GET request that almost the same, it runs correctly. Example:
public async Task<List<MomentCategoryModel>> GetAllAsync()
{
using (HttpResponseMessage response = await _api.ApiClient.GetAsync("api/MomentCategory/GetAll"))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<List<MomentCategoryModel>>();
return result;
}
else
{
throw new Exception();
}
}
}
Thank you for your help and your time in advance!
In MomentCategoryController I adjusted the Post method as the following:
[HttpPost]
[Route("Create")]
[Consumes("application/x-www-form-urlencoded")]
public void PostMomentCategory([FromForm] TestMCategoryModel model)
{
_momentCategoryData.CreateMomentCategory(model);
}
In public async Task PostAsync(MomentCategoryModel momentCategory) method I added a list of KeyValuePair with one element. And also added an HttpContent like this: HttpContent httpcontet = new FormUrlEncodedContent();
This way it works, I get an OK code back (Status code 200). Maybe it would work with the suggested string content if I adjust what the API should consume. I will do a test with that as well, but right now it works this way.
I would like to thank everyone who took the time to read thru the issue and suggest a possible solution!
You can try PostAsync method to POST your Model to your api endpoint. You can do this:
public async Task PostAsync(MomentCategoryModel momentCategory)
{
string jsonData = JsonConvert.SerializeObject(momentCategory);
StringContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
using (HttpResponseMessage response = await _api.ApiClient.PostAsync("api/MomentCategory/Create", content))
{
if (!response.IsSuccessStatusCode)
{
throw new Exception();
}
}
}
And in your API method, you need to add [FromBody] attribute to get your posted data:
public void PostMomentCategory([FromBody]MomentCategoryModel model)
For the json post you need a frombody option
public void PostMomentCategory([FromBody] MomentCategoryModel model)

WebApi HttpPost not receiving the posted content from HttpClient

Web API:
I tried using [FormBody] and [FromForm] before the string stringcontent as well.
// POST api/<MAUserController>
[HttpPost("AuthenticateUser")]
public async Task<ActionResult<MAUser>> PostAsync(string stringcontent)
{
//stringcontent is null
}
Client Code:
List<KeyValuePair<string, string>> postParameters = new List<KeyValuePair<string, string>>();
postParameters.Add(new KeyValuePair<string, string>("Email", Email));
postParameters.Add(new KeyValuePair<string, string>("Password", Password));
var jsonString = JsonConvert.SerializeObject(postParameters);
var stringContent = new StringContent(jsonString, Encoding.UTF8, "application/json");
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenString);
//httpClient.DefaultRequestHeaders.Add("Email", Email);
//httpClient.DefaultRequestHeaders.Add("Password", Password);
using (var response = await httpClient.PostAsync(API_URL, stringContent))
{
if (response.IsSuccessStatusCode)
{
//Success code
}
else
{
//Handle unsuccessful here
}
}
You're posting structured data in JSON format from the client. So why is the API just trying to accept a plain string? That doesn't make a lot of sense.
You should make it accept a model object with the correct structure, instead. ASP.NET will take care of binding the JSON properties to the model properties. However, you should also simplify the postParameters in the client as well - you're over-complicating the structure.
e.g.
Client:
var postParameters = new { Email = "abc#example.com", Password = "xyz" };
Server:
public async Task<ActionResult<MAUser>> PostAsync([FromBody] UserCredentials credentials)
{
}
where UserCredentials is a DTO class like this:
public class UserCredentials
{
public string Email { get; set; }
public string Password { get; set; }
}
ok so when using content type you are basically telling the receiving API how to parse the request payload and so by telling it application/json the framework is trying to parse the payload from JSON to an object and assign it to your parameter ( not really since you need to add an attribute [FromBody] ) but it can not do it since you are expecting a string, so either change your content type or change your parameter type ( i would suggest your parameter type ) .

Can't figure how to consume this API in C#

Forgive my lack of knowlegde, I'm a database guy although I dabbled a bit with C# a while back. I am trying to figure how to get this API running.
The API I'm trying to consume is from https://rapidapi.com/api-sports/api/api-nba/. There is barely any documentation to guide me.
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using unirest_net.http;
using unirest_net;
namespace NBA_test
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
Task<HttpResponse<MyClass.RootObject>> response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJsonAsync<MyClass.RootObject>();
var status = response.Status;
Console.WriteLine("End ....");
}
}
public class MyClass
{
public class Result
{
public string seasonYear { get; set; }
public int gameId { get; set; }
public string arena { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
}
}
var status goes from Created to Running and then that's it, program closes. No error message but I don't know how to get the JSON out of this API. I know I'm missing something but don't know what.
You are in a console application with a sync main method. You should not call an async method inside a sync method. I made your async call into a sync call :
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
var response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJson<RootObject>();
var status = response.Status;
Console.WriteLine("End ....");
}
you still might ask where is your deserialized JSON?
According to Unirest docs:
Response
Upon recieving a response Unirest returns the result in the
form of an Object, this object should always have the same keys for
each language regarding to the response details.
.Code - HTTP Response Status Code (Example 200)
.Headers - HTTP
Response Headers
.Body - Parsed response body where applicable, for
example JSON responses are parsed to Objects / Associative Arrays.
.Raw - Un-parsed response body
Basically, you can access your result like this:
if (response.Code == 200) // Success, OK in HTTP Codes
{
response.Body; // which body has the type of MyClass.RootObject
}
The complete example:
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
var response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJson<RootObject>();
if (response.Code == 200) // Success, OK in HTTP Codes
{
response.Body; // which body has the type of MyClass.RootObject
}
Console.WriteLine("End ....");
Console.ReadLine(); // to force command line stay up for an input and not close applicaiton immediately aftter runing it.
}
Update 1:
Here is a live and working of Unirest on .NET Fiddle:
https://dotnetfiddle.net/EZDopa
You can use httpClient
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
var response = httpClient.GetStringAsync(new Uri(url)).Result;
var releases = JArray.Parse(response);
}
As the others pointed out you need to add some kinda wait statment. I did many calls to api that took time to process in one of my applications. Based on the others comments I updated to allow for logic to be executed while your waiting for the call to come back. an edit for your code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using unirest_net.http;
using unirest_net;
namespace NBA_test
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start ...");
//the added Wait() causes the thread to hold until the task is finished.
Task<HttpResponse<MyClass.RootObject>> response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/5162")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJsonAsync<MyClass.RootObject>();
//if need to perform other logic while you are waiting
while(response.Status == TaskStatus.Running)
{
// perform other logic like gui here
}
var status = response.Status;
Console.WriteLine("End ....");
}
}
public class MyClass
{
public class Result
{
public string seasonYear { get; set; }
public int gameId { get; set; }
public string arena { get; set; }
}
public class RootObject
{
public List<Result> results { get; set; }
}
}
}
Turns out the API's documentation is flawed. I managed to make it work by simply using string (and Newtonsoft's Json.NET). Thx for the help #AliBahrami. Code looks like this now:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using unirest_net.http;
using unirest_net;
using Newtonsoft.Json.Linq;
namespace NBA_test
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start of Program...");
HttpResponse<string> response = Unirest.get("https://api-nba-v1.p.rapidapi.com/gameDetails/9999")
.header("X-RapidAPI-Host", "api-nba-v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "myKey")
.asJson<string>();
var result = response.Body;
JObject parsedString = JObject.Parse(result);
RootObject myGame = parsedString.ToObject<RootObject>();
// Get game id
Console.WriteLine(myGame.results[0].gameId);
Console.WriteLine("End of Program....");
}
}
}

Is it better to use HttpClient or WebRequest in ASP.NET Core to read the content of a file line by line asynchronously remotely?

I plan to read a remote file line by line asynchronously using https://github.com/Dasync/AsyncEnumerable (since there is not yet Async Streams [C# 8 maybe]: https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md):
public static class StringExtensions
{
public static AsyncEnumerable<string> ReadLinesAsyncViaHttpClient(this string uri)
{
return new AsyncEnumerable<string>(async yield =>
{
using (var httpClient = new HttpClient())
{
using (var responseStream = await httpClient.GetStreamAsync(uri))
{
using (var streamReader = new StreamReader(responseStream))
{
while(true)
{
var line = await streamReader.ReadLineAsync();
if (line != null)
{
await yield.ReturnAsync(line);
}
else
{
return;
}
}
}
}
}
});
}
public static AsyncEnumerable<string> ReadLinesAsyncViaWebRequest(this string uri)
{
return new AsyncEnumerable<string>(async yield =>
{
var request = WebRequest.Create(uri);
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var streamReader = new StreamReader(responseStream))
{
while(true)
{
var line = await streamReader.ReadLineAsync();
if (line != null)
{
await yield.ReturnAsync(line);
}
else
{
return;
}
}
}
}
}
});
}
}
It seems that they both run just fine in a simple Console application like below:
public class Program
{
public static async Task Main(string[] args)
{
// Or any other remote file
const string url = #"https://gist.githubusercontent.com/dgrtwo/a30d99baa9b7bfc9f2440b355ddd1f75/raw/700ab5bb0b5f8f5a14377f5103dbe921d4238216/by_tag_year.csv";
await url.ReadLinesAsyncViaWebRequest().ForEachAsync(line =>
{
Console.WriteLine(line, Color.GreenYellow);
});
await url.ReadLinesAsyncViaHttpClient().ForEachAsync(line =>
{
Console.WriteLine(line, Color.Purple);
});
}
}
... but I have some concerns if it is used as part of an ASP.NET Core WebAPI to process the lines and then push them using PushStreamContent:
https://learn.microsoft.com/en-us/previous-versions/aspnet/hh995285(v=vs.108)
https://blog.stephencleary.com/2016/10/async-pushstreamcontent.html
The idea would be to have a pipeline of data which leverages async / await so that the number of threads in use is as low as possible and also to avoid an increase in memory (which leverage the enumerable-like feature of AsyncEnumerable).
I read several articles but it seems it's all non .NET Core versions and I don't really know if there would be some potential performance issues / caveats in regard to what I would like to achieve?
Difference between HttpRequest, HttpWebRequest and WebRequest
http://www.diogonunes.com/blog/webclient-vs-httpclient-vs-httpwebrequest/
An example of "business" case would be:
using System;
using System.Collections.Async;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace WebApplicationTest.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DumbValuesController : ControllerBase
{
private static readonly Random Random = new Random();
// GET api/values
[HttpGet]
public async Task<IActionResult> DumbGetAsync([FromQuery] string fileUri)
{
using (var streamWriter = new StreamWriter(HttpContext.Response.Body))
{
await fileUri.ReadLinesAsyncViaHttpClient().ForEachAsync(async line =>
{
// Some dumb process on each (maybe big line)
line += Random.Next(0, 100 + 1);
await streamWriter.WriteLineAsync(line);
});
}
return Ok();
}
}
}
We have access to the source code for .NET Core. So you can look.
The underlying implementation of both end up using HttpClientHandler (the implementation of that class is split up into 4 files).
You can see this from the source code of both HttpClient and HttpWebRequest (which WebRequest uses).
So I suspect you won't notice any difference in the performance of either.
HttpClient is the latest one to be written, so that's why its use is encouraged. And for the reasons mentioned in the article you linked to: http://www.diogonunes.com/blog/webclient-vs-httpclient-vs-httpwebrequest/
With the latest release of .Net Core 6.0, WebRequest will be declared as deprecated. Microsoft recommended to use HttpClient instead
https://learn.microsoft.com/en-us/dotnet/core/compatibility/networking/6.0/webrequest-deprecated

Difference between HttpClient.GetStringAsync and WebClient.DownloadStringAsync

I have the following code
static void Main(string[] args)
{
string url = "http://www.google.com";
Console.WriteLine(GetUrl(url).Result); // throws TaskCanceledException
Console.WriteLine(GetUrl2(url).Result);
}
public static Task<string> GetUrl(string url)
{
using (var client = new HttpClient())
{
return client.GetStringAsync(url);
}
}
public static Task<string> GetUrl2(string url)
{
using (var client = new WebClient())
{
return client.DownloadStringTaskAsync(url);
}
}
I'm trying to get the string of an url, the problem is GetUrl method (uses HttpClient's GetStringAsync) throws an TaskCacelledException, but GetUrl2 method (uses WebClient's DownloadStringTaskAsync) runs correctly. Is this caused due to using statement? What am I missing?
Edit. In this example I'm calling Result on the task because this is a console application, I know that it is best to await the result in a event handler for example.
Is this caused due to using statement?
Yes. In both code examples, you're disposing the underlying client before the operation completes. Both code examples should be changed as such:
public static async Task<string> GetUrlAsync(string url)
{
using (var client = new HttpClient())
{
return await client.GetStringAsync(url);
}
}
public static async Task<string> GetUrl2Async(string url)
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(url);
}
}
The behavior of asynchronous downloads when their underlying clients are disposed is undocumented. It's best not to dispose the clients until your code is done using them.

Categories

Resources