I'm trying to read a JSON string from Web API controller that is send through a HttpClient.PostAsync() method. But for some reason the RequestBody is always null.
My Request looks like this:
public string SendRequest(string requestUrl, StringContent content, HttpMethod httpMethod)
{
var client = new HttpClient { BaseAddress = new Uri(ServerUrl) };
var uri = new Uri(ServerUrl + requestUrl); // http://localhost/api/test
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
response = client.PostAsync(uri, content).Result;
if (!response.IsSuccessStatusCode)
{
throw new ApplicationException(response.ToString());
}
string stringResult = response.Content.ReadAsStringAsync().Result;
return stringResult;
}
I call this method like this
var content = new StringContent(JsonConvert.SerializeObject(testObj), Encoding.UTF8, "application/json");
string result = Request.SendRequest("/api/test", content, HttpMethod.Post);
Now currently my Web API controller method reads the send data like this:
[HttpPost]
public string PostContract()
{
string httpContent = Request.Content.ReadAsStringAsync().Result;
return httpContent;
}
This works fine. The stringResult property contains the string returned by the controller method. But I'd like to have my controller method like this:
[HttpPost]
public string PostContract([FromBody] string httpContent)
{
return httpContent;
}
The request seems to be working, getting a 200 - OK, but the stringResult from the SendRequest method is always null.
Why isn't the method where I'm using the RequestBody as parameter not working?
Since you're posting as application/json, the framework is attempting to deserialize it rather than providing the raw string. Whatever the type of testObj is in your sample, use that type for your controller action parameter and return type instead of string:
[HttpPost]
public MyTestType PostContract([FromBody] MyTestType testObj)
{
return testObj;
}
Related
Why is the IDictionary<string, string> data in API Controller coming in as null?
I am using HttpClient GetAsync to make a call to the APi Controller.I am sending a one parameter as a querystring https://localhost:44384/my/types?token=NbLeZVEEksQ0GTIY2clmM50uRfZ9%252bWY895mfS25R1zI%253d
On the API Controller side, I am using [FromQuery] to receive the token as a keyvale pair. But its coming in as null. I tried the same on postman
If I use [FromQuery] string token instead of [FromQuery] IDictionary<string,string> it works just fine. But I would like the arguments in keyvalue pair format.
I need that [FromQuery] to work if I wanted to pass multiple arguments. Currently I am passing only the token but I also need the ID. Hoping to have this keyvalue pair working.
https://localhost:44384/note/types?token=73UtMF24W1%252fpUbO5TlF%252bOJ0uRfZ9%252bWY895mfS25R1zI%253d&custid=1}
using (var client = new HttpClient())
{
string targetUrl = string.Format("{0}{1}", "http://localhost:4451", "my/types");
var builder = new UriBuilder(targetUrl);
var query = HttpUtility.ParseQueryString(builder.Query);
query["token"] = encodedSSo;
query["custid"] = "1";
builder.Query = query.ToString();
;
using (var response = await client.GetAsync(builder.ToString()))
{
if (response.StatusCode == HttpStatusCode.OK)
{
responseBody = await response.Content.ReadAsStringAsync();
}
}
using (var response = await client.GetAsync(builder.ToString()))
{
if (response.StatusCode == HttpStatusCode.OK)
{
responseBody = await response.Content.ReadAsStringAsync();
}
}
return Ok(repo.result);
}
API controller
[Route("my/types")]
[HttpGet]
public IHttpActionResult GetTypesAsync([FromQuery] IDictionary<string, string> data) ///why is the data here null. Should it not be a keyvalue pair because HttpClient GetAsync is sending a keyvalue pair? [FromQuery] string data.. works just fine.
{
try
{
//do something
return Ok(report);
}
catch (Exception ex)
{
IHttpActionResult response;
}
}
When you send the KeyValuePairs you are assigning the parameter values. Like you saw parameter:"token" value: "your token value".
So you don't receive it like IDictionary you should receive it as string parameter.
Try to change this line to
public IHttpActionResult GetTypesAsync([FromQuery] string token)
If you what to get list of keyvaluepair from GET request it is the only way you can get it. If you don't like it then use POST
[HttpGet("~/my/types")]
public IHttpActionResult GetTypesAsync(string token)
{
var data = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("token", token)
};
....
result
[{"Key":"token","Value":"NbLeZVEEksQ0GTIY2clmM50uRfZ9%252bWY895mfS25R1zI%253d"}]
and code
var url=#"https://localhost:44384/my/types?token=NbLeZVEEksQ0GTIY2clmM50uRfZ9%252bWY895mfS25R1zI%253";
using (var response = await client.GetAsync(url))
{
...
UPDATE
if you want to use more params , your action should be
[HttpGet("~/my/types")]
public IHttpActionResult GetTypesAsync(Dictionary<string, string> data)
but the url still should be like this
var url=#"https://localhost:44384/my/types?token=NbLeZVEEks&Id=Id";
I have trouble trying to create an integration test for my post call that accepts a viewmodel that has amongst other values, an IFormFile, which makes this call from an application/json to a multipart/form-data
My IntegrationSetup class
protected static IFormFile GetFormFile()
{
byte[] bytes = Encoding.UTF8.GetBytes("test;test;");
var file = new FormFile(
baseStream: new MemoryStream(bytes),
baseStreamOffset: 0,
length: bytes.Length,
name: "Data",
fileName: "dummy.csv"
)
{
Headers = new HeaderDictionary(),
ContentType = "text/csv"
};
return file;
}
My Test Method
public async Task CreateAsync_ShouldReturnId()
{
//Arrange
using var content = new MultipartFormDataContent();
var stringContent = new StringContent(
JsonConvert.SerializeObject(new CreateArticleViewmodel
{
Title = "viewModel.Title",
SmallParagraph = "viewModel.SmallParagraph",
Url = "viewModel.Url",
Image = GetFormFile()
}),
Encoding.UTF8,
"application/json");
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
//Act
var response = await httpClient.PostAsync($"{Url}", content);
//Assert
response.StatusCode.ShouldBe(HttpStatusCode.OK);
int id = int.Parse(await response.Content.ReadAsStringAsync());
id.ShouldBeGreaterThan(0);
}
My Controller Method
[HttpPost]
public async Task<IActionResult> CreateArticleAsync([FromForm] CreateArticleViewmodel viewModel)
{
var id = await _service.CreateAsync(viewModel).ConfigureAwait(false);
if (id > 0)
return Ok(id);
return BadRequest();
}
It throws a BadRequest without getting inside the method.
The way you are posting the request contents to the API, in your code, is not correct.
When the API expects a FileInfo in the request payload, posting JSON content never works. You need to send the payload as MultipartFormData and not as JSON.
Consider following example.
This is a an API endpoint which expects and model with FileInfo in it as payload.
[HttpPost]
public IActionResult Upload([FromForm] MyData myData)
{
if (myData.File != null)
{
return Ok("File received");
}
else
{
return BadRequest("File no provided");
}
}
public class MyData
{
public int Id { get; set; }
public string Title { get; set; }
// Below property is used for getting file from client to the server.
public IFormFile File { get; set; }
}
This is pretty much the same API as yours.
Following is the client code which calls the above API with file and other model properties.
var apiURL = "http://localhost:50492/home/upload";
const string filename = "D:\\samplefile.docx";
HttpClient _client = new HttpClient();
// Instead of JSON body, multipart form data will be sent as request body.
var httpContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(File.ReadAllBytes(filename));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// Add File property with file content
httpContent.Add(fileContent, "file", filename);
// Add id property with its value
httpContent.Add(new StringContent("789"), "id");
// Add title property with its value.
httpContent.Add(new StringContent("Some title value"), "title");
// send POST request.
var response = await _client.PostAsync(apiURL, httpContent);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
// output the response content to the console.
Console.WriteLine(responseContent);
The client code is running from a Console application. So when I run this, the expectation is to get File received message in the console and I am getting that message.
Following is the screen capture of the model content at the API end while debugging it.
And if I am calling this API from postman, it would look like following.
I hope this will help you solve your issue.
I am trying to create a basic test web api, and use a standard controller to test call it.
When I run it, by putting
http://localhost:55144/home/testapi
it'll run the catcher function and completely ignore the parameter.
Then, the catcher will happily return a value, which can be seen in the calling code.
I have tried various combinations of putting [FromBody], changing the type of the parameter in TestApiMethod, and seeing if making a list or array makes any difference.
I've noticed a couple of weird things:
- I'm not using the parameter in the code of TestApiMethod, but Visual Studio is not giving me an unused variable warning.
- If I make the type of the parameter testString a string or even an int, the code below will route to the catcher. If I make it some variation of a model or a Jobject, it will not. It gets as far as running
HttpResponseMessage response = await client.PostAsJsonAsync("api/activity", sendData);
then just returns to the web page.
Here's the code:
Models
public class testStringModel
{
public string testString { get; set; }
}
public class apiResponse
{
public string response { get; set; }
}
Home controller calling Api:
public void TestApi()
{
Task myTask = testApiCall();
}
private async Task<string> testApiCall()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:55144");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
testStringModel data = new testStringModel { testString = "cheese" };
string jsonData = JsonConvert.SerializeObject(data);
var sendData = new StringContent(jsonData, Encoding.UTF8, "application/json");
//var sendData = new Dictionary<string, string>
//{
// {"testString", "cheese"}
//};
HttpResponseMessage response = await client.PostAsJsonAsync("api/activity", sendData);
string responseBodyAsText = await response.Content.ReadAsStringAsync();
dynamic stuff = JObject.Parse(responseBodyAsText);
string finalResponse = stuff.response;
return finalResponse;
}
}
The api:
namespace ApplicationActivity
{
public class ActivityController : ApiController
{
[System.Web.Http.HttpPost]
public HttpResponseMessage Catcher()
{
apiResponse apiResponseObject = new apiResponse();
apiResponseObject.response = "You have somehow wound up in the catcher";
string json = JsonConvert.SerializeObject(apiResponseObject);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent(json, Encoding.Unicode, "application/json");
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(20)
};
return response;
}
[System.Web.Http.HttpPost]
public HttpResponseMessage TestApiMethod(string testString)
{
apiResponse apiResponseObject = new apiResponse();
apiResponseObject.response = "OK from test";
string json = JsonConvert.SerializeObject(apiResponseObject);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");
response.Content = new StringContent(json, Encoding.Unicode, "application/json");
response.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromMinutes(20)
};
return response;
}
}
}
Please will you tell me what I'm doing wrong with my code, how to fix it and what is happening when the code doesn't get to the catcher?
Thanks.
It turns out that I was using an older version of visual studio and as a result the whole thing got really confused with whether is was running .net core or not.
Upgrading to the latest and making sure the latest .net core is installed solved most of my troubles
I am doing an MVC 5 Application, and I am calling a API controller method that is in another Solution.
I am using HttpClient(). and I am calling PostAsJsonAsync with some parameters, an instance of a class.
It looks like this.
string apiUrl = "localhost:8080/api/";
ContactWF contactWF = new contactWF();
contactWF.contact_id=0;
contactWF.UserOrigin_id=20006
contactWF.ProcessState_id=2;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync(apiUrl + "Contact/Method", contactWF);
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<int>().Result;
}
}
My API controller method is like this.
[ActionName("Method")]
[HttpGet]
public int Method([FromBody] ContactWF userwf)
{
return 10;
}
It Works fine...
My problem is when I try Serialized the parameter class instance
I replace line
HttpResponseMessage response = await client.PostAsJsonAsync(apiUrl + "Contact/Method", contactWF);
with this one
string jsonData = JsonConvert.SerializeObject(contactWF);
HttpResponseMessage response = client.PostAsJsonAsync("api/Contact/Method", jsonData).Result;
I've got an Error:405...
It looks like the Json string it is not recognize as a Parameter.
My Json string looks like this.
"{\"Contact_id\":0,\"Description\":null,\"ProcessState_id\":2,\"Type_id\":0,\"Object_id\":0,\"Parent_id\":null}"
that is ContactWD class converter to json.
What´s wrong?
Method PostAsJsonAsync serialize parameter object himself, so it serialized your json string again.
If you need serialize object himself for some reason, then use method HttpClient.PostAsync
string jsonData = JsonConvert.SerializeObject(contactWF);
var stringContent = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync("api/Filler/CountMensajeByUser", stringContent);
Change verb to HttpPost in your api controller
[ActionName("Method")]
[HttpPost]
public int Method([FromBody] ContactWF userwf)
{
return 10;
}
Update
You don't need to serialize object in PostAsJsonAsync
HttpResponseMessage response = client.PostAsJsonAsync("api/Contact/Method", contactWF).Result;
Take a look at sample code from microsoft
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing
internal class NewIdeaDto
{
public NewIdeaDto(string name, string description, int sessionId)
{
Name = name;
Description = description;
SessionId = sessionId;
}
public string Name { get; set; }
public string Description { get; set; }
public int SessionId { get; set; }
}
//Arrange
var newIdea = new NewIdeaDto("Name", "", 1);
// Act
var response = await _client.PostAsJsonAsync("/api/ideas/create", newIdea);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
I'm writing an API controller intended to receive and parse the contents of a JSON asynchronous post, and am unable to read the contents of the StringContent object in that post.
Here is the section from my API controller where I expect to see the value. The value arriving in the ApiController method is null. And the jsonContent value is an empty string. What I'm expecting to see is the contents of a JSON object.
public class ValuesController : ApiController
{
// POST api/values
public void Post([FromBody]string value)
{
HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
// Also tried this per mybirthname's suggestion.
// But content ends up equaling 0 after this runs.
var content = Request.Content.ReadAsStreamAsync().Result.Seek(0, System.IO.SeekOrigin.Begin);
}
}
here is my controller to show how it's being called.
[HttpPost]
public ActionResult ClientJsonPoster(MyComplexObject myObject)
{
this.ResponseInfo = new ResponseInfoModel();
PostToAPI(myObject, "http://localhost:60146", "api/values").Wait();
return View(this.ResponseInfo);
}
And this is the posting method.
private async Task PostToAPI(object myObject, string endpointUri, string endpointDirectory)
{
string myObjectAsJSON = System.Web.Helpers.Json.Encode(myObject);
StringContent stringContent = new StringContent(myObjectAsJSON, Encoding.UTF8, "application/json");
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(endpointUri);
using (HttpResponseMessage responseMessage = await httpClient.PostAsJsonAsync(endpointDirectory, stringContent).ConfigureAwait(false))
{
// Do something
}
}
}
I suspect something is wrong with the signature of the Post method inside the ApiController. But don't know how that should be changed. Thanks for your help.
You are mixing async and sync calls which will lead to deadlocks.
Update controller to
[HttpPost]
public async Task<ActionResult> ClientJsonPoster(MyComplexObject myObject) {
this.ResponseInfo = new ResponseInfoModel();
await PostToAPI(myObject, "http://localhost:60146", "api/values");
return View(this.ResponseInfo);
}
Also [FromBody] is used to force Web API to read a simple type from the request body.
Update Api
public class ValuesController : ApiController {
// POST api/values
[HttpPost]
public async Task Post() {
var requestContent = Request.Content;
var jsonContent = await requestContent.ReadAsStringAsync();
}
}