I'm building an webapi in c# to be called by an outside server.
let's say my API address is www.server.com/webapi/service1
when I set the address above in the app that will use it, it sends a simple POST with an empty body to service1 and waits for a specific KEY as response (in body), like an authentication. ok.
the same service1 can be called, using POST too, passing a raw JSON in the body, and I'm using the [FromBody] attribute to get the body and process.
I tried this to manage the empty POST call and the call with body data:
[HttpPost]
[Route("webapi/service1")]
public HttpResponseMessage Post()
{
var resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StringContent(TokenKey.ToString(), System.Text.Encoding.UTF8, "text/html");
return resp;
}
[HttpPost]
[Route("webapi/service1")]
public async Task<HttpResponseMessage> Post([FromBody] RetornoChat retornoChat)
{
await closedChat(retornoChat); //process body
return resp;
}
but it was not working.I manage a workaround like the code below, I check if the class in [FromBody] is empty, if this is the case return the special string to validate and finish, if there is a body then get the data validate and process. I'm wondering if there is a better solution.
I really thought that the solution was to double the post method and when there was a body it would call the post with the [frombody] and when there is no body it would go to the empty post.
[HttpPost]
[Route("webapi/service1")]
public async Task<HttpResponseMessage> Post([FromBody] RetornoChat retornoChat)
{
var resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StringContent(TokenKey.ToString(), System.Text.Encoding.UTF8, "text/html");
if (retornoChat == null)
{
}
else
{
//get the body data and process
}
return resp;
}
Thanks in advance for your time!
Related
With an endpoint like this:
[HttpPost("[action]")]
public async Task<ActionResult> Import([FromBody]string request)
{
var list = JsonConvert.DeserializeObject<List<RequestQuote>>(request);
return NoContent();
}
request always seems to be null. It worked for a little while earlier today but not sure what changed.
Here is the client side where I do the call.
var json = JsonConvert.SerializeObject(payload);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = client.PostAsJsonAsync($"{endpoint}/api/v1.0/BatchImport/Import", data);
Payload is a List
Even when using
Import([FromBody] List<RequestQuote> request)
I get the same issue.
There is a bug in the code, the data is serialized twice, the second time when you use PostAsJsonAsync. Try this
var json = JsonConvert.SerializeObject(payload);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{endpoint}/api/v1.0/BatchImport/Import", data);
if (response.IsSuccessStatusCode)
{
var stringData = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<object>(stringData);
}
and action should be
public async Task<ActionResult> Import([FromBody] List<RequestQuote> request)
If your controller has the [ApiController] attribute, you can put the data type you want to parse as the parameter of the method. I believe what is happening is that your program is trying to parse the JSON to a string type, which isn't what you want. You can try the code below, which should achieve what you're looking for.
[HttpPost("[action]")]
public async Task<ActionResult> Import([FromBody]List<RequestQuote> list)
{
// The parsed object should now be available here as "list"
return NoContent();
}
Alternatively, this post suggests pulling the body of the request directly. Either solution should be valid.
I am posting to an API using HttpClient and getting back the HttpResponseMessage.
I am reading the status code from the reply but I it's always 200
Posting:
var json = JsonConvert.SerializeObject(loginDto);
var stringContent = new StringContent(json, Encoding.UTF8, "application/json");
var client = new HttpClient();
var response = await client.PostAsync("http://localhost:57770/api/Account/Login", stringContent);
I am replying from API the HttpResponseMessage:
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
But when I read the response, it's always 200
How can I achieve this?
Asp.Net Core no longer recognizes HttpResponseMessage as part of the pipeline. This means it will be treated like any other returned model and serialized as content. Hence the 200 OK status.
The API controller action should return IActionResult derived result.
[HttpPost]
public IActionResult SomeAction(...) {
//...
return StatusCode((int)HttpStatusCode.Unauthorized); //401
//...
}
Or just use
return Unauthorized();
which is derived from StatusCodeResult and is used a short hand to replace the code shown above.
Reference ControllerBase.Unauthorized.
Is it possible to send [FromBody] POST data to a controller using client.GetAsync() (or PostAsync/SendAsync?
I had to set up a base controller that all api calls will go through.
My ajax calls all go to this SecureApi controller, and they send the original path as a parameter to that they can be re-routed to the correct controller. Something like:
$.ajax({
url: "./api/SecureApi/?path=/api/OtherApi/SomeRoute",
data: {
param1: 1,
param2: 2
}
});
So my base controller looks something like:
public class SecurityApiController : ApiController
{
[HttpPost]
public HttpResponseMessage SecureApi([FromBody]object data, string path)
{
// do some stuff
// get uri
var applicationPath = Request.RequestUri.Scheme + "://" + Request.GetRequestContext().VirtualPathRoot.Replace("/", String.Empty);
Uri routeUri = new Uri(applicationPath + path);
// then redirect to correct controller
var config = new HttpConfiguration();
var server = new HttpServer(config);
var client = new HttpClient(server);
// how can I send [FromBody]object data here?
var response = client.GetAsync(routeUri).Result; // or PostAsync/SendAsync?
return response;
}
}
The other controller looks like:
public class OtherApiController : ApiController
{
[HttpPost]
public HttpResponseMessage OtherApi([FromBody]OtherData data)
{
// do stuff
}
}
Unfortunately I can't change OtherApi, so I HAVE to send the [FromBody] POST data in the same way (in the POST body).
Is that possible?
EDIT:
Per #Philippe's response below, I'm using PostAsJsonAsync and it seems to want to work, but I'm getting a 401 Unauthorized result. More info:
I went with the correct(?) ASYNC/AWAIT route...
public async Task<HttpResponseMessage> SecureApi([FromBody]Dictionary<string, dynamic> data, string path)
{
...
var client = new HttpClient();
var response = await client.PostAsJsonAsync(routePath, data);
return response;
}
And the Other controller has:
[Authorize(Roles = "Admin")] // I do have the "Admin" role
[Route("Save")]
[HttpPost]
public SaveResultBase Save([FromBody]Dictionary<string, dynamic> data)
{
...
}
But this controller is never hit (no breakpoints are hit there) and it returns a 401 Unauthorized response.
I guess that I have to add my user credentials to the client headers before calling PostAsJsonAsync. Can't find any way to do that though.
The method GetAsync of HttpClient will send a HTTP GET request so it would only be possible to have [FromUri] arguments. Because [FromBody] argument are by definition POST data, you will want to use PostAsJsonAsync/ PostAsXmlAsync/PostAsync. The difference between all of them is how the data is serialized.
var response = client.PostAsJsonAsync(routeUri, data).Result;
That being said, if you have security in mind, it would be rather easy for anyone to call the "right api" directly. Moreover you will increase latency by generating two HTTP requests.
You should take a look at this guide on MSDN. I believe that an authentication filter is probably what you are looking for.
I'm using restsharp to perform a POST request to my endpoint.
When I add the body, I do this:
request.AddParameter("text/json", message, ParameterType.RequestBody);
The string message is done in this way: VALUE1.VALUE2
It is really simple.
But my endpoint receives only VALUE1
The endpoint signature is:
[HttpPost]
public HttpResponseMessage DoJob([FromBody] string id)
Do you know why? Do I have to encode somehow the message I'm sending?
Doing the same with postman for test purpose I'm not experiencing this behavior.
Thanks!
Here is my working example for RestSharp version 105.1.0.0:
var message = "VALUE1.VALUE2"
var client = new RestClient("http://localhost:64648"); //replace with your domain name
var request = new RestRequest("/Home/DoJob", Method.POST); //replace 'Home' with your controller name
request.RequestFormat = DataFormat.Json;
request.AddBody(new { id = message });
client.Execute(request);
And my endpoint definition
[HttpPost]
public HttpResponseMessage DoJob([System.Web.Http.FromBody] string id) {
//some code
}
Everything is working as expected.
BTW, if you want post array you need change only two places:
request.AddBody(new { ids = message.Split('.') });
And definition
[HttpPost]
public HttpResponseMessage DoJob([System.Web.Http.FromBody] string[] ids) {
//some code
}
I solved this problem passing the body in another way.
Instead of:
request.AddParameter("text/json", message, ParameterType.RequestBody);
I put:
request.RequestFormat = DataFormat.Json;
request.AddBody(message);
Now whatever character is inside the message, the message content (as long as it is json) is properly passed to my endpoint
I'm posting some data to an web api controller method from an MVC controller with this method..
private static async Task<HttpResponseMessage> SendDataToApi (List<TogglRow> input)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:****/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync("api/service", input);
//if (response.StatusCode == HttpStatusCode.OK)
//{
// var resultUri = response.Headers.Location;
//}
return response;
}
}
This is the Web Api method i'm posting to..
public HttpResponseMessage Post(HttpRequestMessage request, List<Dagsrapport> value)
{
if (value != null)
{
var rapporter = value.ToList();
//send rapporter to DB
var response = new HttpResponseMessage(HttpStatusCode.OK);
return response;
}
return request.CreateResponse(HttpStatusCode.BadRequest);
}
Now, the post works fine and i'm returning HttpStatusCode.OK. But i'm not beeing redirected back to the method i'm performing the post from (SendDataToApi()). I'm beeing returned back to the page from wich the post was triggered. I can see the page is working (waiting for localhost..) but nothing happens.
I should probably mention that this is two separate projects (MVC & WebApi), but in the same solution.
What am i missing?
EDIT - Solved
The problem I had was due to the method that ran the task "SendDataToApi" was not set to async. Therefore, it did not wait for an results from the post, but instead ran synchronously and the control never returned to the method that ran SendDataToApi, instead it returned to the original caller - the UI.
Here is the method that is runnig the SendDataToApi task..
[HttpPost]
public async Task<ActionResult> Edit(IEnumerable<TogglRow> tr)
{
var listToExport = tr.Where(x => x.Export.Equals(true));
var result = listToExport.ToList();
var response = await SendDataToApi(result);
return RedirectToAction("Index", "Home",
response.StatusCode == HttpStatusCode.OK ? new { message = "Record(s) were successfully stored." } : new { message = "No records selected." });
}
It seems you have some fundamental misunderstandings about how all this works. MVC actions and WebAPI actions work very differently, which is why they're actually in entirely different namespaces, even though they both implement similar components.
If you need to connect to a Web API from an MVC action, you shouldn't be receiving the response as an HttpResponseMessage. That's a return value for a WebAPI action, similar to how a ViewResult is a return value for an MVC action. It has no meaning to anything in MVC. Rather, your actual response from HttpClient, for example, will be a string (technically a byte array) with a content type indicating that it should be interpreted as plain text, JSON, XML, etc.
Based on the content type, you'll process this response accordingly. If it's JSON, for example, then you can use something like JObject from Newtonsoft.Json (default JSON interpreter in MVC). Then, you could use this data object to construct your response for your MVC action. If you have something indicating that a redirect should be made, then the MVC action can return on of the Redirect* family of results. Importantly, you can't just make the redirect the response of the Web API action, because that merely affects the HttpClient response object.