REST file upload with HttpRequestMessage or Stream? - c#

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.

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

How to consume Json string from external API? Json returns with escaped or extra characters.

I spent some time trying to figure this one out so I decided to post it here - hopefully it saves some time to someone else.
I'm building an ASP.Net Core Web API MVC application that accepts a Get request and makes a call to an external API (in this case is the Bing Image Search). When returning a result, it would give me a escaped Json string. Example:
"{\"_type\": \"Images\", \"instrumentation\": {\"pageLoadPingUrl\": \"https:...}
Instead of:
{
"_type": "Images",
"instrumentation": {
"pageLoadPingUrl": "https:....
}
Then, I wanted to pass it back to my web client with all sort of non-successes.
I will post shortly how I solved it.
Cheers!
So the issue was that I was trying to process the reponse content the wrong way. All I had to do is user the JsonConvert library.
My full API method looks like this:
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "Enter your key here");
var uri = new Uri("uri to external API here + any parameters");
var response = await client.GetStringAsync(uri);
var jsonResponse = JsonConvert.DeserializeObject(response);
return Ok(jsonResponse);
}
}
Cheers! :)

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

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.

Return to MVC controller after posting to Web Api

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.

Content negotiation to return HTML

After reading this blog post on how to return HTML from Web API 2 using IHttpActionResult, I wanted to somehow "wire-up" this IHttpActionResult to my ApiController based on the Accept header that is sent with request.
Given controller actions that have signature similar to this:
public MyObject Get(int id)
{
return new MyObject();
}
If the request specifies the Accept: text/html, this IHttpActionResult should be used to return HTML. Is that possible? In addition, some insight on how this content negotiation pipeline works for json or xml (that have built-in support) would be greatly appreciated.
If we keep the discussion of IHttpActionResult aside for a momment, Content-negotiation process in Web API is driven through formatters. So you would need to create a new formatter for handling the media type text/html.
Web API exposes the default algorithm it uses for content-negotiation called DefaultContentNegotiator which is an implementation of the service IContentNegotiator.
Now this negotiation algorithm can be run either by Web API automatically for you like in the following cases:
Usage # 1:
public MyObject Get(int id)
{
return new MyObject();
}
OR
you can manually run the negotiation yourself like in the following:
Usage #2 :
public HttpResponseMessage Get()
{
HttpResponseMessage response = new HttpResponseMessage();
IContentNegotiator defaultNegotiator = this.Configuration.Services.GetContentNegotiator();
ContentNegotiationResult negotationResult = defaultNegotiator.Negotiate(typeof(string), this.Request, this.Configuration.Formatters);
response.Content = new ObjectContent<string>("Hello", negotationResult.Formatter, negotationResult.MediaType);
return response;
}
Regarding IHttpActionResults:
In the following scenario, Ok<> is a shortcut method for generating an instance of type
OkNegotiatedContentResult<>.
public IHttpActionResult Get()
{
return Ok<string>("Hello");
}
The thing is this OkNegotiatedContentResult<> type does similar thing as in Usage # 2 scenario above. i.e they run the negotiator internally.
So to conclude, if you plan to support text/html media type then you need to write a custom formatter and add it to Web API's formatter collection and then when you use Ok<string>("Hello") with an Accept header of text/html, you should see the response in text/html. Hope this helps.

Categories

Resources