PostAsJson could not post data - asp.net

I have Web api method like below. The Web API is developed inClassic .Net 4.6.2
[HttpPost]
public async Task<IEnumerable<DocumentDTO>> GetDocuments([FromBody]IEnumerable<string> documentNames)
{
return await _domainService.GetDocuments(documentNames);
}
Then I have ASP.Net Core client that was using HttpClient to post data. I have my own extension method that serializes the input using Newtonsoft.Json.JsonConvert and post it and then de-serializes the response
public static async Task<TResult> MyPostMethodAsync<TSource, TResult>(this HttpClient httpClient, TSource source, string url)
{
// serialize the input
var content = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(source)).ConfigureAwait(false);
var stringContent = new StringContent(content, Encoding.UTF8, "application/json");
//post json string
var httpResponse = await httpClient.PostAsync(url, stringContent).ConfigureAwait(false);
//ensures ok response
httpResponse.EnsureSuccessStatusCode();
// get response string
var result = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
//de-serialize the response
return await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<TResult>(result)).ConfigureAwait(false);
}
The method above working fine. Note that its using PostAsync method.
Then I changed the above method to make use of PostAsJsonAsync extension method that is available in Microsoft.AspNet.WebApi.Client. So the new method looks like below
public static async Task<TResult> MyPostMethodAsync<TSource, TResult>(this HttpClient httpClient, TSource source, string url)
{
// post as json
var httpResponse = await httpClient.PostAsJsonAsync<TSource>(url, source).ConfigureAwait(false);
// Ensures response is okay
httpResponse.EnsureSuccessStatusCode();
// get response string
var result = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
// de-seriazlize the response
return await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<TResult>(result)).ConfigureAwait(false);
}
However the PostAsJsonAsync extension method does not post any data? The Web API Method always receive empty collection for documentnames parameter. (I'm also using my extension method to POST data to additional web api methods as well, but all POST methods receives null values or empty collection)
I am guessing its serialization/deserialization issue but I am not sure which serializer .Net 4.6.2 & .Net Core uses by default.

Related

sending data(model with register) to API with PutAsync but arrives without registration only Id

I am trying to update a record through my API and upon arrival the data is empty.
Am I using the shipping method correctly?
What registration time should I send?
CONTROLLER
// POST: Category/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(string id, Category_Model cate)
{
HttpClient client = new HttpClient();
var category = new StringContent(JsonConvert.SerializeObject(cate, Formatting.Indented));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PutAsync(new Uri("http://localhost:26457/api/Category/" + id), category);
string result = await response.Content.ReadAsStringAsync();
return RedirectToAction(nameof(Index));
}
API
// PUT: api/Category/5
[HttpPut("{id}")]
public void Put(string id, Category_Model category)
{
var data = Builders<Category_Model>.Filter.Eq(d => d.Id, id);
var update = Builders<Category_Model>.Update
.Set(d => d.cod_Category, category.cod_Category)
.Set(d => d.description_Category, category.description_Category)
.Set(d => d.state_Category, category.state_Category);
context.Category.UpdateOne(data, update);
}
I also tried:
1- PutAsync doesn't send request to web api, but fiddler works fine
2- HttpClient PutAsync doesn't send a parameter to api
I think you are not communicating the content type of the data in the call to the API.
You are creating a StringContent but nowhere are you informing the API that the content is actually JSON, so it probably just thinks it's a string and won't attempt to deserialize it into your object.
My suggestion would be to either...
Not serialize the object manually and let the HTTP client do the serialization work:
HttpResponseMessage response = await client.PutAsync(new Uri("http://localhost:26457/api/Category/" + id), cate);
Or, add content type information to the StringContent:
var category = new StringContent(JsonConvert.SerializeObject(cate, Formatting.Indented), Encoding.UTF8, "application/json");
See this documentation for the constructor of StringContent.

POSTing data while redirecting to a third-party URL using Response.Redirect()

In ASP.Net Core 2.0, how can I POST data while redirecting to a third-party URL using Response.Redirect()?
Note: I have to POST this data without using the query-string.
Response.Redirect triggers a GET request which means that the only option is using a query string.
Can you trigger the redirection from the client (if any) in order to make a POST request?
You must use object if you want post data without using query string.
[HttpPost]
public IActionResult Search([FromBody] CustomerSearchRequestApiModel request)
{
if (request == null)
{
return BadRequest();
}
return Ok(request);
}
It is impossible to use Response.Redirect() to send Post request.
For a workaround, you could try HttpClient to send Post request and then return the reponse to the web browser with ContentResult as text/html.
Here is a demo code:
public async Task<ContentResult> HtmlView()
{
using (var formDataContent = new MultipartFormDataContent())
{
HttpClient client = new HttpClient();
Article article = new Article { ArticleName = "AN" };
formDataContent.Add(new StringContent("AN", Encoding.UTF8, "application/json"), "ArticleName");
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.PostAsync(#"https://localhost:44393/Articles/Create", formDataContent);
return new ContentResult
{
ContentType = "text/html",
StatusCode = (int)response.StatusCode,
Content = await response.Content.ReadAsStringAsync()
};
}
}
}
Note
Change the HttpClient part to send the right request to your own third party url with validate parameters.

dotnet core webapi calling .net webapi2

I am calling a .NET WebApi2 endpoint from a dotnet core webapi. When I debug into the .NET WebApi2 POST endpoint, my value is always null. Is this not possible to do?
When I call the GET endpoint with an ID, the ID is passed with no issues.
I have used both Postman and Fiddler to debug. Whenever I pass my JSON object from Postman to the .NET WebApi2 POST endpoint, my value is populated.
Beyond frustrated as this seems pretty simple. :,(
Updated to include code
dotnet core web api (calling from Postman)
[HttpPost]
public async Task PostAsync([FromBody] string value)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var jsonObject = new JObject();
jsonObject.Add("text", "Rich");
var response = await client.PostAsJsonAsync("http://localhost:54732/api/Rich", jsonObject);
var responseResult = response.Content.ReadAsStringAsync().Result;
}
.NET WebApi2 (JObject is always null)
// POST: api/Rich
public void Post(JObject value)
{
}
This boils down to using JObject basically. For your older Web Api action, JObject works merely because you're posting JSON, and JObject is a dynamic. However, that is an entirely incorrect approach. You should be binding to a concrete class that represents the JSON being posted. That said, you may or may not be able to change anything there, and its not technically the source of your current issue.
The actual source is that you're attempting to send a JObject, which is not doing what you think it is. Again, JObject is a dynamic. It has accessors to parse and access the underlying JSON, but it does not actually expose the members of that JSON object directly. As a result, if you attempt to serialize it, you won't get anything usable from it. Passing it to PostAsJsonAsync causes it to be serialized.
What you actually need is something like:
var jsonObject = new { text = "Rich" };
Then, what you're passing to PostAsJsonAsync will be an anonymous object with actual members that can be serialized.
My "REAL" issue turned out to be Transfer-Encoding: chunked was being sent in the request header.
Here is my corrected code (dotnet core web api):
public async Task PostAsync([FromBody] JObject value)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
var jsonObject = new { variable1 = "Rich" };
var json = JsonConvert.SerializeObject(jsonObject);
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentLength = json.Length;
var response = await client.PostAsync("http://localhost:54732/api/Rich", content);
var responseResult = response.Content.ReadAsStringAsync().Result;
}
Here is my .NET WebApi2 code:
public IHttpActionResult Post([FromBody]RichTest value)
{
return Ok(value.variable1 + " done");
}
public class RichTest
{
public string variable1 { get; set; }
}
When I set the content.Headers.ContentLength, the Transfer-Encoding: chunked is removed. Now my code is working!!
I am still curious why the original PostAsJsonAsync does not work...

Web API External Bearer Unauthorized

I am trying to call the RegisterExternal method in Web API, after having retrieved a token from facebook. But I keep getting a 401 Unauthorized from my Web API. I am not sure I am correctly implementing the logic flow. My code is;
Ask for supported external login providers;
public async Task<List<ExternalLoginViewModel>> GetExternalLoginsAsync()
{
using (var client = GetNewHttpClient(false))
{
var response = await client.GetAsync("api/account/externalLogins?returnUrl=/&generateState=true");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsAsync<List<ExternalLoginViewModel>>();
}
}
From this, I am returned a facebook URL. I follow this and then enter in my facebook username and password. I return back to my app via a deep link and then try and call the RegisterExternal method in the web API like this, passing the facebook "access token" that is returned.
public async Task<bool> SendSubmitRegisterExternalAsync(RegisterExternalBindingModel ro, string accessToken)
{
using (var client = GetNewHttpClient(true))
{
client.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", accessToken));
HttpResponseMessage response = await client.PostAsJsonAsync("api/Account/RegisterExternal", ro);
if (response.IsSuccessStatusCode) return true;
var value = await response.Content.ReadAsStringAsync();
throw new ResponseErrorException(ErrorHelper.GetErrorString(value));
}
}
I receive 'Unauthorized' every time. I do not know what is wrong. My Web API method looks like this, and the class is marked with the [Authorize] attribute.
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
...
I have found three different posts this evening of people asking this exact same question, and in all cases there are no replies, so I am not hopeful but if anyone can shed some light on this it would be great!
EDIT: I have also changed the method signature to 'allowanonymous' and still get unauthorized!
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[AllowAnonymous]
[Route("RegisterExternal")]
public async Task<IHttpActionResult> RegisterExternal(RegisterExternalBindingModel model)
{
I have sorted this by not using FacebookSessionClient and doing it via a WebBrowser control instead.
I use the URL from the first step (provided to me by the WebAPI). Then on the Navigated event from the WebBrowser control, i parse the Url for the access token;
public async void ParseUrlForAccessToken(string url)
{
string fieldName = "access_token";
int accessTokenIndex = url.IndexOf(fieldName, StringComparison.Ordinal);
if (accessTokenIndex > -1)
{
int ampersandTokenIndex = url.IndexOf("&", accessTokenIndex, StringComparison.Ordinal);
string tokenField = url.Substring(accessTokenIndex, ampersandTokenIndex - accessTokenIndex);
string token = tokenField.Substring(fieldName.Length);
token = token.Remove(0, 1);
await _dataService.SubmitLoginExternal("Test", token);
}
}
Then as shown above, I call SubmitLoginExternal, which is a call to the following code which uses the access token retrieved from the WebBrowser control Url to register the account (in this case a 'Test' account);
using (var client = GetNewHttpClient(true))
{
client.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", accessToken));
HttpResponseMessage response = await client.PostAsJsonAsync("api/Account/RegisterExternal", ro);
if (response.IsSuccessStatusCode) return true;
var value = await response.Content.ReadAsStringAsync();
throw new ResponseErrorException(ErrorHelper.GetErrorString(value));
}
This has worked and now I have the user registered in my database.
The key was to use a WebBrowser control and not the FacebookSessionClient object or a WebBrowserTask. You cannot use a WebBrowserTask as you need to hook in to the navigated event once the page has loaded to call ParseUrlForAccessToken().

What part of the ASP.NET Web API framework translates a user-defined object to a HttpResponseMessage?

If you have a action in an ApiController like so:
public Foo GetFoo(int id)
{
// ...
}
And from another .NET application of any type, you call this API end-point like so:
using (var client = new HttpClient())
{
var response = await client.PostAsJsonAsync("http://localhost/api/foo/1", value);
// ...
}
Then, the HttpClient.PostAsJsonAsync method returns a Task<HttpResponseMessage> even when the API action returned a user-defined object of class Foo.
What part of the Web API does this translation? Does the client code always receive a HttpResponseMessage?
HttpClient doesn't know that the response maps to the foo class. It simply returns the HTTP response encapsulated as a HttpResponseMessage.
If you want to map the response to a Foo object you must tell it to do so.
Example:
using (var client = new HttpClient())
{
var response = await client.PostAsJsonAsync("http://localhost/api/foo/1", value);
var stream = await response.Content.ReadAsStreamAsync(); // Read the request body into a stream.
// Map the body to a object of type Foo
var foo = new JsonMediaTypeFormatter().ReadFromStream(typeof(Foo), stream, Encoding.UTF8, null);
// Do whatever you want with foo...
}

Resources