C# POSTing [FromBody] data from one controller to another - c#

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.

Related

HttpClient POST with data

I got a form where user will insert data and save it to the database. After save, it will do a post with data to the URL(RequestURL) given. This RequestURL will analyze the data and pass back the result to the different URL(ResponseURL). Below is the simple code for it.
public class RequestSender
{
private static HttpClient _client;
public RequestSender()
{
if (_client== null)
{
_client= new HttpClient();
_client.BaseAddress = new Uri("https://example.com/api/");
}
}
[HttpPost]
public async Task<string> SaveData()
{
// get data from the form
// save the data
// Save data is easy, already done it
// Here is the problem
// after save the data, it will send a post to the default URL
var response = await _client.PostAsync(path);
return "OK";
}
}
I'm using httpclient, but the problem is I only get the response of the post. What I want is, it will post data and redirect to the URL like an action attribute in the form below. So, the URL will handle the data and pass back to the ResponseURL. Appreciate your help.
<form action="https://example.com/api/" method="post">
Actually I try to integrate the payment gateway. Here is the flow:
User submit form, save to the database.
After form save success, will have another post from backend to the URL (Payment site).
Payment site will handling the post data and send back to the ResponseURL.
Here I will read the response and handling it based on the response.
Actually, this can be done by submitting another form from client side, but instead of doing that, i try to post within the first form (save data).
You can return RedirectResult from action
[HttpPost]
public async Task<string> SaveData()
{
// some code
var response = await _client.PostAsync(path);
return Redirect("https://example.com/api/");
}
If you want HttpClient.PostAsync() with the data , you could try the below code :
[HttpPost]
public async Task<string> SaveData()
{
//Since the API will need the new data in JSON format
//therefore I am serializing the data into JSON and then converting it to a StringContent object
StringContent content=new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json" );
//The StringContent object is then added to the API request by adding it to the 2nd parameter of the PostAsync() method.
var response = await _client.PostAsync(path ,content);
var content= response.Content.ReadAsStringAsync().Result;
return "OK";
}
Reference : https://www.yogihosting.com/aspnet-core-consume-api/
This part you have it already
1- User submit a form, save to the database.
2/3- After form save success, will have another post from the backend to the URL (Payment site). Payment site will be handling the post data and send back to the ResponseURL.
This can be done in 2 different ways, you do that using an HttpClient as #Alexander suggested
var response = await _client.PostAsync(paymentUrl);
var responseModel = JsonConvert.DeserializeObject<ResponseModelDto>(json)
var returnUrl = responseModel.ReturnUrl
Or you need to do it from your front end, doing an async post to the PaymentPage and process the response (ResponseUrl) via javascript.
4 - Here I will read the response and handling it, based on the response.
With the response, you can redirect you can do anything you need
var returnUrl = responseModel.ReturnUrl;
return Redirect(returnUrl);
But there are some integrations with payment websites, that you normally redirect the user passing parameters via a post or a get. The website handles the request as part of the querystring or as part of the body from your post, you should send as well a returnUrl, where they will send any information(Dto) back to be processed it from your side. But that will depend on your specific scenario.
Hope this clarifies your doubts

c# webapi post() and post([FromBody])

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!

asp.net webapi 2.0 handle wrong route

I have developed an API for an application and i have all routes set up, but when the users request the wrong action, the server returns 404 by default and i tried to find some way of changing that but i didnt find anything...
So lets say i have an api with the following route:
/api/school/1/classes
if the client requests
/api/school/1/classez
i want to return response code 501 or 405 instead of the default 404.
how can this be done?
Use Route attributes.
[HttpGet]
[Route("api/{id}/foo")]
public HttpResponseMessage foo(int id)
{
var response = new ApiResponse();
response.StatusCode = HttpStatusCode.OK;
return response;
}
[HttpGet]
[Route("api/{id}/{route}")]
public HttpResponseMessage bar(int id)
{
var response = new ApiResponse();
response.StatusCode = HttpStatusCode.BadRequest;
return response;
}
If they type it correctly then it will enter foo. Otherwise they will enter bar and you can handle that how ever you like.

Making an async HttpClient post request with data from FormCollection

I am doing an Asp.Net MVC 4 project and am looking to an internal request (like a proxy) to our api service.
This is what the index method looks like in my controller. I'm stuck at the PostAsync part.
[HttpPost]
public async Task<ActionResult> Index(FormCollection body){
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://myapi.com");
// posts to http://myapi.com/users
var response = await httpClient.PostAsync("users", body);
if(response.isSuccessStatusCode) return Json(new {
status = true,
url = response.Content.Url
});
}
I want to pass my "application/x-form-urlencoded" "body" content to the PostAsync POST method. However, I get an error reading "body is not of type HttpContent".
I can't cast or convert. What now?
Let me know what I'm doing incorrectly here.
Erik
I'm not entirely sure what you're trying to do, but possibly converting the FormCollection to a dictionary and using the FormUrlEncodedContent class is what you're looking for.
e.g.:
var response = await httpClient.PostAsync("users",
new FormUrlEncodedContent(
body.
AllKeys.ToDictionary(
k => k, v => body[v])));

REST file upload with HttpRequestMessage or Stream?

What is the better way to upload a file for a REST client?
From the WCF Web API Documentation
[WebInvoke(UriTemplate = "thumbnail", Method = "POST")]
public HttpResponseMessage UploadFile(HttpRequestMessage request)
{
From multiple forum posts:
WCF REST File upload with additional parameters
[WebGet(UriTemplate="", Method ="POST"]
public string UploadFile(Stream fileContents)
I understand, that the first method allows to directly post a file from a normal HTML form. The 2nd approach seems more common on all forum posts I find.
What would you recommend and why? The REST api should be accessible from all kind of languages and platforms.
For the HttpRequestMessage approach, how would I do an upload a file preferable with the WCF HttpClient? With the FormUrlEncodedMediaTypeFormatter)
In order to test the HttpRequestMessage approach I have done the following using MVC:
public class TestingController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Upload()
{
var file = Request.Files[0];
var filename = Request.Form["filename"];
var uri = string.Format("http://yoururl/serviceRoute/{0}", filename);
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("image/pjpeg"));
var content = new StreamContent(file.InputStream);
var response = client.PostAsync(uri, content);
ViewBag.ServerUri = uri;
ViewBag.StatusCode = response.Result.StatusCode.ToString();
return View();
}
}
The Index view should have a form in it that posts back to the Upload method. Then you are able to use the HttpClient to make a connection to your REST service.
The first method is "closer to the metal" and would be more flexible since you would be processing the http requests and building the responses yourself. If all you need to do is accept a stream from a client, the second option is much simpler from the implementation standpoint (under the hood, it does the same work that the first method is doing)
I don't have an answer for your last question.

Categories

Resources