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();
}
}
Related
I have a .net api and I want to test the api from a console app.
The method I am trying to test is a POST Method.I serialize data from my console app into a json string and I want to post it to the API, but the API does not get hit and I dont get any errors from my console app.
My GET calls work though. It is just the post I cant get to work.
My API Controller->
using _ErrorLogger.Shared;
using _ErrorLogger.Server.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Runtime.CompilerServices;
namespace _ErrorLogger.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ExceptionDetailsController : ControllerBase
{
private readonly IExceptionDetailsService _exceptionDetailsService;
public ExceptionDetailsController(IExceptionDetailsService exceptionDetailsService)
{
_exceptionDetailsService = exceptionDetailsService;
}
[HttpGet]
[Route("GetExceptions")]
public async Task<List<ExceptionDetails>> GetAll()
{
return await _exceptionDetailsService.GetAllExceptionDetails();
}
[HttpGet]
[Route("GetExceptionByID/{id}")]
public async Task<ExceptionDetails> GetByID(int id)
{
return await _exceptionDetailsService.GetExceptionDetails(id);
}
[HttpPost]
[Route("CreateException")]
public async Task<IActionResult> CreateException([FromBody]string obj)
{
//await _exceptionDetailsService.AddExceptionDetails(exceptionDetails);
return Ok();
}
[HttpPost]
[Route("Test")]
public async Task<IActionResult> Test([FromBody] string obj)
{
return Ok();
}
}
}
My Call from the console app ->
public async void ExceptionsAnalyzer(Exception exception)
{
HttpClient _httpClient = new HttpClient();
StackTrace stack = new StackTrace(exception, true);
StackFrame frame = stack.GetFrame(stack.FrameCount - 1);
ExceptionDetails exceptionDetails = new ExceptionDetails
{
ExceptionMessage = exception.Message,
InnerException = exception.InnerException?.ToString(),
ExceptionType = exception.GetType().ToString(),
ExceptionSourceFile = frame.GetFileName(),
ExceptionSourceLine = frame.GetFileLineNumber().ToString(),
ExceptionCaller = frame.GetMethod().ToString(),
ExceptionStackTrace = exception.StackTrace,
DateLogged = DateTime.Now
};
string json = JsonSerializer.Serialize(exceptionDetails);
//var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.PostAsJsonAsync("http://localhost:5296/api/ExceptionDetails/CreateException", json);
if (response.IsSuccessStatusCode)
{
}
}
I am Expecting the api endpoint to be hit.
I am Expecting the api endpoint to be hit.
Well, Firstly, your method in console app which is ExceptionsAnalyzer structure is wrong. It should be type of static because, main method within console app itself is type of static.
Another mistake is async should be type of Task and while calling the ExceptionsAnalyzer method it should be wait() for response but your console app is static so how it would handle await call? So see the solution below:
Solution:
using System.Net.Http.Json;
using System.Text.Json;
// Calling method
ExceptionsAnalyzer().Wait();
//Defining Method in dotnet 6 console app
static async Task ExceptionsAnalyzer()
{
HttpClient _httpClient = new HttpClient();
var obj = "Test data";
string json = JsonSerializer.Serialize(obj);
HttpResponseMessage response = await _httpClient.PostAsJsonAsync("http://localhost:5094/api/ExceptionDetails/CreateException", json);
if (response.IsSuccessStatusCode)
{
}
}
Note: I haven't consider your parameter Exception exception which you can modify yourself. I am mostly considering why you cannot get to hit API Endpoint. Hope you now got the mistake.
Output:
Unless ExceptionDetails is part of your basepath and as such is included for all API calls, I think you need to remove that.
You defined the route to the call as CreateException, so the url should be <base url>/CreateException
If that doesn't help, please post the code of your entire controller (with endpoint method).
I want to send an image with C# HttpClient, receive it in ASP.NET Core controller and save it to disk. I tried various methods but all i'm getting in controller is null reference.
My http client:
public class HttpClientAdapter
{
private readonly HttpClient _client;
public HttpClientAdapter()
{
_client = new HttpClient();
}
public async Task<HttpResponse> PostFileAsync(string url, string filePath)
{
var requestContent = ConstructRequestContent(filePath);
var response = await _client.PostAsync(url, requestContent);
var responseBody = await response.Content.ReadAsStringAsync();
return new HttpResponse
{
StatusCode = response.StatusCode,
Body = JsonConvert.DeserializeObject<JObject>(responseBody)
};
}
private MultipartFormDataContent ConstructRequestContent(string filePath)
{
var content = new MultipartFormDataContent();
var fileStream = File.OpenRead(filePath);
var streamContent = new StreamContent(fileStream);
var imageContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
content.Add(imageContent, "image", Path.GetFileName(filePath));
return content;
}
}
and controller:
[Route("api/files")]
public class FilesController: Controller
{
private readonly ILogger<FilesController> _logger;
public FilesController(ILogger<FilesController> logger)
{
_logger = logger;
}
[HttpPost]
public IActionResult Post(IFormFile file)
{
_logger.LogInformation(file.ToString());
return Ok();
}
}
As i mentioned above, the IFormFile object i'm getting in the controller is null reference. I tried adding [FromBody], [FromForm], tried creating class with two properties: one of type string and one with type IFormFile, but nothing works. Also instead of sending file with C# HttpClient i used Postman - same thing happens.
Does anyone know solution for this problem? Thanks in advance.
The name of the form field must match the property name:
content.Add(imageContent, "file", Path.GetFileName(filePath));
file instead of image, since you use file in
public IActionResult Post(IFormFile file)
{
}
Hi i am developing API's using Web API 2. I know how to add header when using HttpResponseMessage. Now I am using IHttpActionResult.
Below is my sample current code.
return Content(HttpStatusCode.OK, LoginResponse);
How can I add a header when I am returning content?
Whenever I use HttpResponseMessage I will be having request object and I can add header.
Below code I tried in HttpResponseMessage.
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
return response;
In this case where can I add header values?
You can continue to use the HttpResponseMessage as you are accustom to and update the header. After which you can use the IHttpActionResult ResponseMessage(HttpResponseMessage) method to convert to IHttpActionResult
Simple example
public class MyApiController : ApiController {
public IHttpActionResult MyExampleAction() {
var LoginResponse = new object();//Replace with your model
var cookie = new CookieHeaderValue("name", "value");//Replace with your cookie
//Create response as usual
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK, LoginResponse);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
//Use ResponseMessage to convert it to IHttpActionResult
return ResponseMessage(response);
}
}
You can create a custom IHttpActionResult which decorates a real one but exposes a way to manipulate the response:
public class CustomResult : IHttpActionResult
{
private readonly IHttpActionResult _decorated;
private readonly Action<HttpResponseMessage> _response;
public CustomResult(IHttpActionResult decorated, Action<HttpResponseMessage> response)
{
_decorated = decorated;
_response = response;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await _decorated.ExecuteAsync(cancellationToken);
_response(response);
return response;
}
}
Then use this in your action:
return new CustomResult(Content(HttpStatusCode.OK, loginResponse), res => res.Headers.AddCookies(new []{ cookie}));
You can add header by using this code:
HttpContext.Current.Response.AppendHeader("Some-Header", value);
or this
response.Headers.Add("Some-Header", value);
I'm trying to perform a POST action to MVC controller like so:
string payload = "hello";
using (var httpClient = new HttpClient())
{
var str = new StringContent(new JavaScriptSerializer().Serialize(payload), Encoding.UTF8,
"application/json");
httpClient.BaseAddress = new Uri("http://localhost:52653/");
var response = httpClient.PostAsync("Home/TestPost", str).Result;
}
MVC Controller:
[HttpPost]
public ActionResult TestPost(string value)
{
var result = value;
return Content("hello");
}
When I debug I see the breakpoint hit in the controller but "value" param is null.
Is this even possible? can objects be sent this way as well? ie (Person)?
Try the FromBody attribute, that should work.
As #maccettura pointed out, you must include the proper namespace for this to work
using System.Web.Http;
[HttpPost]
public ActionResult TestPost([FromBody]string value)
{
var result = value;
return Content("hello");
}
Another method is this:
[HttpPost]
public async Task<string> TestPost(HttpRequestMessage request)
{
var requestString = await request.Content.ReadAsStringAsync();
return requestString;
}
You can use JsonConvert.SerializeObject() method.
var str = new StringContent(JsonConvert.SerializeObject(payload).ToString(), Encoding.UTF8,"application/json");
The serialized JSON string would look like:
{"payload":"hello"}
Also change
public ActionResult TestPost(string value)
TO
public ActionResult TestPost(string payload)
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;
}