dotnet core webapi calling .net webapi2 - .net-core

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

Related

Web API PUT,DELETE, POST methods via URI

I am very new to the whole consept of API's. So far, I managed to build a web api that has GET,POST,PUT and DELETE methods.
Now, from an ASP.NET project, I try to finally use my web api.
Here's what I do for GET method:
string info = new WebClient() { }.DownloadString("https://mywebapisite.com/item/" + id);
Item item = JsonConvert.DeserializeObject<Item>(info);
This functions all fine. As you can see, all the GET method needs is an id.
However, for the POST method, I have no clue what to do.
I can create a new Item instance, but don't know what to do with it.
By the way, I also used ASP.NET to make my web.api.
There is a built-in feature in ASP.NET 5 called Swagger. It can perform all the tasks very succesfully. Is there like a code-behind for what Swagger does.
PS: I know that this question must be very common and basic. If you could refer me to another question in stackoverflow or simply tell me what to search on google I would appreciate it. (As you may guess, I don't even know what to search for)
pseudo code to consume post request in C#
var requestObj = GetDummyDataTable();
using (var client = new HttpClient())
{
// Setting Base address.
client.BaseAddress = new Uri("https://localhost:8080/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = new HttpResponseMessage();
// HTTP POST
response = await client.PostAsJsonAsync("api/product", requestObj).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
// Reading Response.
string result = response.Content.ReadAsStringAsync().Result;
var responseObj = JsonConvert.DeserializeObject<DataTable>(result);
}
}
You can refer the following code to call the API using HttpClient:
////using System.Net.Http.Headers;
////using System.Text;
using (var client = new HttpClient())
{
var requesturi = "https://localhost:7110/api/ToDo/relativeAddress";
var item = new TestUserViewModel()
{
Name = "John Doe",
Age = 33
};
////using System.Text.Json; // use JsonSerializer.Serialize method to convert the object to Json string.
StringContent content = new StringContent(JsonSerializer.Serialize(item), Encoding.UTF8, "application/json");
//HTTP POST
var postTask = client.PostAsync(requesturi, content);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
var Content = await postTask.Result.Content.ReadAsStringAsync();
return RedirectToAction("Privacy");
}
}
The API method like this:
[Route("api/[controller]")]
[ApiController]
public class TodoController : ControllerBase
{
[HttpPost]
[Route("relativeAddress")]
public string GetAddress([FromBody] TestUserViewModel testUser)
{
return "Address A";
}
And the result like this:
You can also refer this link to set the Content-Type.
You seem a little bit lost, and I get it. Api learning path is kinda weird, I recommend you watch a tutorial (My favorite https://www.youtube.com/playlist?list=PLLWMQd6PeGY0bEMxObA6dtYXuJOGfxSPx)
But if you need code asap, you could refer the following code.
Ps: The others answers are really good!
using System.Net.Http;
using System.Net.Http.Headers;
public class ApiHelper
{
public HttpClient ApiClient { get; set; }
public void InitializeClient()
{
ApiClient = new HttpClient();
ApiClient.BaseAddress = new Uri("https://mywebapisite.com/");
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task PostSomething(FormUrlEncodedContent data)
{
using (HttpResponseMessage response = await ApiClient.PostAsync("/item",data)
{
var result = await response.Content.ReadAsAsync<string>();
}
}
}

How do I make a http request from an asp.net middleware?

I have a middleware which needs to call off to an external service to check for some state and then act on it. I'm wondering how I go about making a request from middleware?
I've seen some docs about having a HttpClientFactory service, but I'm not really sure how I make that available to my middleware?
You can use the default HttpClient
This allows you to create a client in your middleware and send any request you need.
Example:
using(var client = new HttpClient()){
try
{
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
// Above three lines can be replaced with new helper method below
// string responseBody = await client.GetStringAsync(uri);
Console.WriteLine(responseBody);
}
catch(HttpRequestException e)
{
Console.WriteLine("\nException Caught!");
Console.WriteLine("Message :{0} ",e.Message);
}
}

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.

PostAsJson could not post data

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.

Set default Media Formatter for a WebAPI action

I have implemented a custom media formatter and it works great when the client specifically requests "csv" format.
I have tested my api controller with this code:
HttpClient client = new HttpClient();
// Add the Accept header
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/csv"));
However, when I open the same URL from a web browser it returns JSON not CSV. This is probably due to standard ASP.NET WebAPI configuration that sets JSON as the default media formatter unless otherwise specified by the caller. I want this default behavior on every other web service I have but NOT on this single operation that returns CSV. I want the default media handler to be the CSV handler that I implemented. How do I configure the Controller's endpoint such that it returns CSV by default and only returns JSON/XML if requested by the client?
Which version of Web API are you using?
If you are using 5.0 version, you could use the new IHttpActionResult based logic like below:
public IHttpActionResult Get()
{
MyData someData = new MyData();
// creating a new list here as I would like CSVFormatter to come first. This way the DefaultContentNegotiator
// will behave as before where it can consider CSVFormatter to be the default one.
List<MediaTypeFormatter> respFormatters = new List<MediaTypeFormatter>();
respFormatters.Add(new MyCsvFormatter());
respFormatters.AddRange(Configuration.Formatters);
return new NegotiatedContentResult<MyData>(HttpStatusCode.OK, someData,
Configuration.Services.GetContentNegotiator(), Request, respFormatters);
}
If you are using 4.0 version of Web API, then you could the following:
public HttpResponseMessage Get()
{
MyData someData = new MyData();
HttpResponseMessage response = new HttpResponseMessage();
List<MediaTypeFormatter> respFormatters = new List<MediaTypeFormatter>();
respFormatters.Add(new MyCsvFormatter());
respFormatters.AddRange(Configuration.Formatters);
IContentNegotiator negotiator = Configuration.Services.GetContentNegotiator();
ContentNegotiationResult negotiationResult = negotiator.Negotiate(typeof(MyData), Request, respFormatters);
if (negotiationResult.Formatter == null)
{
response.StatusCode = HttpStatusCode.NotAcceptable;
return response;
}
response.Content = new ObjectContent<MyData>(someData, negotiationResult.Formatter, negotiationResult.MediaType);
return response;
}

Resources