I am trying to build a small desktop app (.NET 6.0) that gets data from .NET 6.0 minimal API server.
The API server code is:
//app.MapPost("/show/", async Task<string> (string myName) =>
app.MapPost("/show/", async Task<string> (HttpRequest request) =>
{
//return await Task.FromResult(myName);
return await Task.FromResult(request.Query["myName"]);
});
The postman is able to post to
http://127.0.0.1:5400/show?myName=Srikanth S
and gets a reply in the uncommented as well as commented version.
The desktop app's response object has
StatusCode: 500, ReasonPhrase: 'Internal Server Error'
after the call to
app.MapPost("/show/", async Task<string> (string myName)
version while it responds with
StatusCode: 400, ReasonPhrase: 'Bad Request'
in the response object and
Required parameter "string myName" was not provided from query string
on result object.
My desktop app has this simple code only
private async void button1_Click(object sender, EventArgs e)
{
Dictionary<string, string> content = new Dictionary<string, string>();
content.Add("myName", "Srikanth S");
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsync("http://127.0.0.1:5400/show", new FormUrlEncodedContent(content));
string result = await response.Content.ReadAsStringAsync();
Debug.WriteLine(result);
}
}
I tried the permutation, combinations of StringContent
, FormUrlEncodedContent
, Dictionary
, KeyvaluePair
...;
It looks like it is not capturing 'myName' in the HttpRequest
version.
Any guidance or solution please?
update: created new .net core web api project (weatherforecast sample), added
app.MapPost("/show/", (string myName) =>
{
return myName;
});
as well as tried
app.MapPost("/show/", async (string myName) =>
{
return await Task.FromResult(myName);
});
No changes in Desktop App, I get this in debug output as below
Microsoft.AspNetCore.Http.BadHttpRequestException: Required parameter "string myName" was not provided from query string.
at Microsoft.AspNetCore.Http.RequestDelegateFactory.Log.RequiredParameterNotProvided(HttpContext httpContext, String parameterTypeName, String parameterName, String source, Boolean shouldThrow)
at lambda_method2(Closure , Object , HttpContext )
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass36_0.<Create>b__0(HttpContext httpContext)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
--- End of stack trace from previous location ---
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
HEADERS
=======
Host: 127.0.0.1:5145
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
any help is highly appreciated. For background, i am using visual studio 2022 community edition Version 17.3.6 , dotnet 6.0.402
SOS
CodePudding user response:
When using Postman to check the API method, you can see the parameter is added to the request URL.
Dictionary<string, string> content = new Dictionary<string, string>(); content.Add("myName", "Srikanth S"); using (HttpClient client = new HttpClient()) { HttpResponseMessage response = await client.PostAsync("http://127.0.0.1:5400/show", new FormUrlEncodedContent(content)); string result = await response.Content.ReadAsStringAsync(); Debug.WriteLine(result); }
But in the desktop app, the parameter is added to the request body, so in this scenario, the API method can't find the value from the request URL, so the value is null and will throw an exception.
To solve this issue, you can try to send the parameter via the Query String: add the parameter at the end of the request URL, like this:
var name = "Srikanth S";
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsync($"http://127.0.0.1:5400?myName={name}", null);
string result = await response.Content.ReadAsStringAsync();
Debug.WriteLine(result);
}
the API method:
app.MapPost("/show/", async Task<string> (HttpRequest request) =>
{
//return await Task.FromResult(myName);
return await Task.FromResult(request.Query["myName"]);
});
Or you can send the parameter from request body: in the API method, you can get the parameter from the Form. Modify the API method as below:
app.MapPost("/show/", async Task<string> (HttpRequest request) =>
{
//return await Task.FromResult(myName);
return await Task.FromResult(request.Form["myName"]); //get value from form.
});
In this time, the HttpClient method like this:
Dictionary<string, string> content = new Dictionary<string, string>();
content.Add("myName", "Srikanth S");
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsync("http://127.0.0.1:5400/show", new FormUrlEncodedContent(content));
string result = await response.Content.ReadAsStringAsync();
Debug.WriteLine(result);
}
Refer to the following screenshot:
CodePudding user response:
The second parameter of PostAsync is meant for the body, not query string data. Just add the myName query parameter to the URL.
HttpResponseMessage response = await Client.PostAsync(
"http://127.0.0.1:5400/show?myName=Jose", // <----- Like this
new FormUrlEncodedContent(content)
);
CodePudding user response:
Thank you all for the guidance and help. Finally I cracked it :) Hope this will be useful to everyone. The key is post data is to be read from the request body on the api server. Here is how I did it
app.MapPost("/show/", async (HttpRequest request) =>
{
var body = new StreamReader(request.Body);
string postData = await body.ReadToEndAsync();
Dictionary<string, dynamic> keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, dynamic>>(postData) ?? new Dictionary<string, dynamic>();
// here after you can play as you like :)
return await Task.FromResult<string>(postData);
});
My desktop app code
private async void button1_Click(object sender, EventArgs e)
{
var client = new HttpClient();
using StringContent jsonContent = new(
JsonSerializer.Serialize(new {myName = "Srikanth S"}), Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.PostAsync("http://127.0.0.1:5145/show", jsonContent);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
// here after you can play as you like :)
Debug.WriteLine(jsonResponse);
}
UPDATE: Thank you Zhi. request.Form approach is way better than mine; It works with postdata as url parameter also :) Here is server side.
app.MapPost("/show/", async (HttpRequest request) =>
{
var body = new StreamReader(request.Body);
string myName = request.Form["myName"].ToString();
return await Task.FromResult<string>(myName);
});
On Desktop side
private async void button1_Click(object sender, EventArgs e)
{
var client = new HttpClient();
Dictionary<string, string> postParams = new Dictionary<string, string>();
postParams.Add("myName", "Srikanth S");
FormUrlEncodedContent content = new FormUrlEncodedContent(postParams);
using HttpResponseMessage response = await client.PostAsync("http://127.0.0.1:5145/show", content);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
Debug.WriteLine(jsonResponse);
}