WebApi HttpPost not receiving the posted content from HttpClient - c#

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 ) .

Related

Send binarydata to Azure webAPI POST endpoint

Currently my webAPI has the following POST endpoint:
public async Task<ActionResult<string>> AddUserImage([FromRoute] string userId, [FromHeader] bool doNotOverwrite, [FromBody] byte[] content, CancellationToken ct)
My goal is to send an image file to the endpoint. However, I cannot find a correct way to send an octect-stream or ByteArrayContent or some other type over the internet. All attempts end in an HTTP 415.
This is my best attempt to send the image over the internet:
public async Task<bool> AddOrReplaceImage(string id, string endpoint, byte[] imgBinary)
{
if (imgBinary is null) throw new ArgumentNullException(nameof(imgBinary));
var request = new HttpRequestMessage(HttpMethod.Post, endpoint);
request.Headers.Add("doNotOverwrite", "false");
request.Content = JsonContent.Create(imgBinary);
// I also tried: request.Content = new ByteArrayContent(imgBinary);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); // Does not seem to change a thing
var apiResult = await new HttpClient().SendAsync(request); // Returns 415
return apiResult.IsSuccessStatusCode;
}
I doubt both the parameters of the endpoint and the way I send the HTTP request. How can I simply receive and send an image over the internet?
Frist Solution :- Which worked in my case.
You can try [FromForm] and IFormFile Like this :-
If controller is annotated with [ApiController] then[FromXxx] is required. For normal view controllers it can be left.
public class PhotoDetails
{
public string id {get;set;}
public string endpoint {get;set;}
public IFormFile photo {get;set;}
}
public async Task<ActionResult<string>> AddUserImage([FromForm] PhotoDetails photoDetails, CancellationToken ct)
I tried this in .net core and it worked but i needed array of files so i used [FromForm] and IFormFile[] and sending from angular.
Second Solution :-
I tried replicate question scenario with question code.
and then changed the implementation and it worked. Please find the below
code
PhotoDetails photopara = new PhotoDetails();
photopara.id = id;
photopara.endpoint = endpoint;
photopara.photo = imgdata;
string json = JsonConvert.SerializeObject(photopara);
var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
{
var response = await client.PostAsync("http://localhost:57460/WeatherForecast", stringContent);
if (!response.IsSuccessStatusCode)
{
return null;
}
return (await response.Content.ReadAsStreamAsync()).ToString();
}
public class PhotoDetails
{
public string id {get;set;}
public string endpoint {get;set;}
public byte[] photo {get;set;}
}
In this solution, I changed IformFile to byte[] in photodetail class because httpresponsemessage creating problem.
Get Image or byte array in Post Method
Please try this without json serialization
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(idContent, "id", "param1");
formData.Add(endpointContent, "endpoint", "file1");
formData.Add(bytesContent, "photo", "file2");
var response = await client.PostAsync("http://localhost:57460/WeatherForecast", formData);
if (!response.IsSuccessStatusCode)
{
return null;
}
return (await response.Content.ReadAsStreamAsync()).ToString();
}
public async Task<ActionResult<int>> AddUserImage([FromForm] PhotoDetails photo, CancellationToken ct)
{
// logic
}
Still Not working then You can try the below link also
Send Byte Array using httpclient

Send Http Request from Blazor Component

I'm using .Net Core 3.1 and I'm having trouble sending requests from a Blazor component. I want to send a request to a controller I have, and these requests systematically end up in 400 Bad request.
In my Startup.cs, I have
if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
{
services.AddScoped<HttpClient>(s =>
{
var uriHelper = s.GetRequiredService<NavigationManager>();
return new HttpClient
{
BaseAddress = new Uri(uriHelper.BaseUri)
};
});
}
In my Blazor component, I have:
var json2 = Newtonsoft.Json.JsonConvert.SerializeObject(_Model);
var stringContent2 = new StringContent(json2, System.Text.Encoding.UTF8, "application/json");
var response2 = await Http.PostAsync("/[controllerName]/[Method]", stringContent2);
if (response2.IsSuccessStatusCode)
{
var resultContent = response2.Content.ReadAsStringAsync().Result;
return resultContent;
}
else
return "failed";
And here is my Controller Method prototype:
[HttpPost]
public IActionResult Method([FromBody] Model form)
{...}
Would you happen to see what's wrong with the code?
You are passing a StringContent object in your PostAsync method, but in your action you have your Model as a parameter.
You have two options :
To change your action parameter to a StringContent.
To parse the Json as your Model to pass it to the PostAsync method content parameter.
Regards,
these requests systematically end up in 400 Bad request.
Please check you provide correct request header(s) and well-formatted data while you make request from your Blazor app to backend service.
I did a test using following code snippet with simple testing data, which work well on my side. If possible, you can create a new component and test if the code snippet can work for you.
var _Model = new Model { Id = 1, Name = "fehan" };
var json2 = Newtonsoft.Json.JsonConvert.SerializeObject(_Model);
var stringContent2 = new StringContent(json2, System.Text.Encoding.UTF8, "application/json");
var response2 = await Http.PostAsync("Home/Method", stringContent2);
if (response2.IsSuccessStatusCode)
{
var resultContent = response2.Content.ReadAsStringAsync().Result;
}
Model class
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
}
Test Result
Besides, if you are making request to MVC controller action, please check if you enabled antiforgery validation on controller or action(s).

Returning JSON via WebApi

I am using the HttpClient in C# to try and integrate SSO (Single Sign On) into some of our custom Applications.
I have done this successfully in our JavaScript Apps, but I'm having some difficulty integrating it into some of our Umbraco sites.
My code so far:
using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using Newtonsoft.Json;
using System.Collections.Generic;
using Umbraco.Web;
using Umbraco.Web.WebApi;
namespace Umbraco.WebApi
{
public class TestController : UmbracoApiController
{
public HttpClient client = new HttpClient();
[HttpPost]
public async Task<Object> GetRefreshToken(Token t)
{
try {
string refToken = t.refresh_token;
var values = new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "client_id", "CLIENTID" },
{ "client_secret", "CLIENTSECRET" },
{ "refresh_token", refToken }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("https://URL.org/Token", content);
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
} catch(HttpRequestException e) {
return e;
}
}
public class Token
{
public string refresh_token { get; set; }
}
public class AuthData
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_int { get; set; }
public string refresh_token { get; set; }
public string userName { get; set; }
public string client_id { get; set; }
public DateTime issued { get; set; }
public DateTime expires { get; set; }
}
}
}
Which does successfully return the data I'm after but there are issues with the returned data (Removed sensitive data):
<z:anyType xmlns:d1p1="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" i:type="d1p1:string">
{"access_token":"XXXXXXXX","token_type":"bearer","expires_in":1199,"refresh_token":"XXXXXX","userName":"XXXXXX","as:client_id":"XXXXX",".issued":"Fri, 20 Sep 2019 13:23:48 GMT",".expires":"Fri, 20 Sep 2019 13:43:48 GMT"}
</z:anyType>
It also seems to be getting returned as XML instead of JSON?
C# is not my strongest of languages, so I may be completely wrong.
Any help appreciated.
In your code, after you get the JSON string responseString, instead of returning it, try the following code.
...
string responseString = await response.Content.ReadAsStringAsync();
response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(responseString, Encoding.UTF8, "application/json");
return response;
You need to change your method return value from Task<Object> to Task<HttpResponseMessage>
Edit:
To access the properties, install the Newtonsoft.Json package and try below code.
var jsonString = ...
JObject jo = JObject.Parse(jsonString);
Console.WriteLine(jo["access_token"]);
One of the things you can do to resolve this is to specifically request a json format on your response by adding the corrent request headers
Accept: application/json
try it like so
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Now if the server pays attentions to the request headers it will return a json back to you.
It might be defaulting to xml because there is no such header in your request OR the server only supports returning xml responses.
EDIT: If you can not get the server to return json, you can convert your xml string response to json string. Take a look at this example. After the convertsion you can return your json string normally from your controller.
Edit:
Ok, try out this sample below:
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", ""),
new KeyValuePair<string, string>("scope", ""),
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("redirect_uri", ""),
new KeyValuePair<string, string>("code", ""),
new KeyValuePair<string, string>("client_secret","")
});
AADTokenResponse TokenResponse = null;
string _baseAddress = string.Format("https://yourTargetDomain.com/");
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var responseMessage = await client.PostAsync("targetApiSegment", content);
if (responseMessage.IsSuccessStatusCode)
{
var responseString = await responseMessage.Content.ReadAsStringAsync();
TokenResponse = JsonConvert.DeserializeObject<AADTokenResponse>(responseString);
}
}
I think in this case, FormUrlEncodedContent is the wrong choice, and you'd want to use StringContent instead, similar to this:
var content = new StringContent(HttpUtility.UrlEncode(values), Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://URL.com/Token", content);
The reason being, I dont think FormUrlEncodeContent has an overload to accept adding content type.
Another alternative would be to switch to using SendAsync rather than PostAsync, as SendAsync has some additional flexibility.

ObjectContent not working but StringContent does

I'm trying to write an overload for HttpClient.PutAsJsonAsync<T> which allows headers to be set on each request. Looking at the source code for that method, it creates an ObjectContent<T> internally, which I tried to emulate below.
However, my target server always receives a null body when binding (using [FromBody] Form form).
If I change the request to be a StringContent and manually serialize the body, the request succeeds! What could be the difference between the two implementations?
Even more strangely, if I call await content.ReadAsStringAsync() before SendAsync then it also works!
My thoughts are it could be either:
The JSON is being serialized differently (But I'm not configuring any Json.NET globals anywhere.) The obvious problem would be different capitalization, but as far as I know, JSON.net is case insensitive when parsing, and my server will be using JSON.net
I'm not awaiting something and the content is being sent before it serialises fully. I can't spot any un-awaited Tasks in my code though, and the entrypoint is a standard async Main()
My environments is:
Client is a simple netcoreapp2.1 exe, running in a microsoft/dotnet:2.1-sdk-alpine docker image.
Server is a .NET framework MVC5 WebApi, with many similar APIs which other clients can call fine
The code below is a trimmed down version of what my client is calling.
public static class UserUpdater
{
public static async Task UpdateAsync(List<string> users)
{
using (var client = new HttpClient())
{
var form = new { Users = users };
var headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " // Real JWT here
};
var response = await client.PutAsJsonAsync(new Uri("http://localhost/api/app/v1/gateway-users"), form, headers);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException("Response status code does not indicate success: " +
response.StatusCode + "\n\n" +
await response.Content.ReadAsStringAsync());
}
}
}
public static async Task<HttpResponseMessage> PutAsJsonAsync<T>(this HttpClient client, Uri requestUri, T value,
IEnumerable<KeyValuePair<string, string>> headers, CancellationToken cancellationToken = default)
{
var content = new ObjectContent<T>(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null);
////var content = new StringContent(JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json"); // This works fine
var message = MakeRequestMessage(HttpMethod.Put, requestUri, headers, content);
return await client.SendAsync(message, cancellationToken);
}
private static HttpRequestMessage MakeRequestMessage(
[NotNull] HttpMethod method,
[NotNull] Uri requestUri,
[CanBeNull] IEnumerable<KeyValuePair<string, string>> headers,
[CanBeNull] HttpContent content = null)
{
var message = new HttpRequestMessage(method, requestUri);
if (headers != null)
{
foreach (var header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
if (content != null)
{
message.Content = content;
}
return message;
}
If it helps, this is the server side (MVC5 WebApi):
[Route("")]
[ValidateModel]
public IHttpActionResult Put([Required] [FromBody] GatewayUserCollectionForm form)
{
Ensure.NotNull(form, nameof(form)); // This throws since form is null
this.manager.UpdateGatewayUsers(form.Users.ToList());
return this.Ok();
}
public class GatewayUserCollectionForm
{
[Required]
public List<string> Users { get; set; }
}

WebApi - why is my post variable always null?

I'd like to be able to read a post variable from my controller method.
I currently have the below code:
[HttpPost]
public IHttpActionResult BuildPartitions([FromBody]string PartitionBuildDate)
{
}
I'm using the below code to test:
using (HttpClient httpClient = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "PartitionBuildDate", "24-May-2017" }
};
var content = new FormUrlEncodedContent(values);
var response = httpClient.PostAsync("http://localhost:55974/api/Controller/BuildPartitions", content);
var responseString = response.Result.Content;
}
Looking online, this looks correct for both sending and receiving a post variable in C#, however the PartitionBuildDate variable is always null.
Try adding the content-type header. I have used Newtonsoft JSON.NET for JSON conversion:
string postBody = JsonConvert.SerializeObject(yourDictionary);
var response = client.PostAsync(url, new StringContent(postBody, Encoding.UTF8, "application/json"));
var responseString = response.Result.Content;
Also, on your web API side, try wrapping your POST parameters inside a class:
public class PostParameters
{
public string PartitionBuildDate {get;set;}
}
[HttpPost]
public IHttpActionResult BuildPartitions([FromBody]PostParameters parameters)
{
//you can access parameters.PartitionBuildDate
}

Categories

Resources