How to pass Xml file using RestClient in Post - C#? - c#

I'm trying to pass xml file to api using RestSharp, but I'm receiving the file at the Post method as null.
Here is my code:
public void SendXmlToApi()
{
var client = new RestClient(_uri);
var request = new RestRequest(Method.POST);
request.AddFile("Xml",XmlPath);
request.RequestFormat = DataFormat.Xml;
request.AddHeader("content-type", "application/xml");
var response = client.Execute(request);
bool res = (response.StatusCode == HttpStatusCode.OK);
}
And my Post Func:
[HttpPost]
[Route("Test")]
public void UpdateResult(XDocument a)
{
}
Any idea whats the problem?

I don't use XML, so this deviates a little from your example, but it is a viable option for posting XML into a [HttpPost] API endpoint. I used your SendXmlToApi() example untouched (just supplied my own _uri and XmpPath variables) and was successful (Core 3.1).
I modified your receiving code to be:
[HttpPost]
[Route("test")]
public async Task UpdateResult()
{
string body = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
XDocument xdoc = XDocument.Parse(body);
}
Of course, you'll want to put guard rails on this and have proper error handling and validation, but it should get you over the hump.

Related

learning .net core 3.1 - very first attempt at consuming API webservice

I'm brand new to .net core 3.1 - not a lot of oop experience period - I have 20 years software development experience using procedural languages on IBMi. Been working through a tutorial to consume an API that I have created on my IBMi... I'm stuck...
Below is my HomeController... What I'm able to do by running this code thus far...
1.) connects to my IBMi endpoint
2.) it does invoke the correct backend API pgm
3.) backend API pgm does indeed take json string from URI and process it like it should, and produces a json string response.
issues..
1.) ultimately i'd like to pass this json request string in the body and not the URI
2.) while everything is working with passing the json string in the URI, when the json string response is sent back, there is an issue processing that response. My guess is that it is expecting a json object back and not a string.
The error is...
JsonException: The JSON value could not be converted to
System.Collections.Generic.IEnumerable`1[coreiWS.Models.ProductKey].
Path: $ | LineNumber: 0 | BytePositionInLine: 1.
the generated json string response is... (or could also contain a trial key value - nonetheless, both are valid json in string format)...
{
"success": 0,
"resultMessage": "Error RST00001R - Invalid - Product Code Already Granted 2 Trial Keys. Please Email Us At xxxxxxxxxx#gmail.com To
Discuss Your Future Business Requirements Utilizing CoreiRST." }
and the code causing it is...
var responseStream = await response.Content.ReadAsStreamAsync();
productKeys = await JsonSerializer.DeserializeAsync
<IEnumerable<ProductKey>>(responseStream);
I would be extremely grateful for anyone that could help guide me in the right direction with getting this working.
the complete HomeController Code is...
public class HomeController : Controller
{
// jhv - add this...
private readonly IHttpClientFactory _clientFactory;
public IEnumerable<ProductKey> productKeys { get; set; }
public HomeController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
// jhv - add this...
public async Task<IActionResult> Index()
{
//var user = Authenticate().Result;
//if (user == null)
//return View();
var request = new HttpRequestMessage(HttpMethod.Get,
"http://xxxxxxxxxxxxxxxxxxxxxxxxx/{\"env\":\"xxx\",\"command\":\"getTrialKey\",\"payload\":{\"productCode\":\"MFT 102A08R EPCA 00007\"}}");
//request.Headers.Add("Authorization", "Bearer " + user.Token);
request.Headers.Add("Authorization", "Basic xxxxxxxxxxxxxxxxxx");
//request.Headers.Add("text/plain", "{\"env\":\"xxx\",\"command\":\"getTrialKey\",\"payload\":{\"productCode\":\"MFT 102A08R EPCA 00007\"}} \r\n");
var client = _clientFactory.CreateClient();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("text/plain"));
var response = await client.SendAsync(request);
if(response.IsSuccessStatusCode)
{
var responseStream = await response.Content.ReadAsStreamAsync();
productKeys = await JsonSerializer.DeserializeAsync
<IEnumerable<ProductKey>>(responseStream);
}
else
{
productKeys = new List<ProductKey>();
}
return View(productKeys);
It looks like your JSON is returning a singular object, but you are trying to map it to an IEnumerable<ProductKey>.
That might get you through the first error message.

ASP.NET Web API does not receive the data posted from Angular

I'm currently trying to post an angular model to a web API inside .NET Core MVC. The model on the Angular side is populated correctly before the point at which is Posts to the web API. When it reaches the API, however, the model looks like {"urlEndpoint":null,"token":null}.
I have tried changing the header Content-Type, I have tried adding [FromBody], I have also tried changing the controller from HttpResponseMessage to IHttpActionResult - pretty much every solution on stack overflow to similar problems actually. However, the interesting thing is, that this same code works in an older project on standard .NET.
Any help with this would be massively appreciated... it's driving me nuts!
Angular component:
getPdfData() {
let token = this.httpService.getToken("token");
this.urlAndTokenModel = new UrlAndTokenModel();
this.urlAndTokenModel.Token = token;
this.urlAndTokenModel.UrlEndpoint = this.apiEndpoint;
this.httpService.postPdfBytes('/blahblah/api/pleaseworkthistime', this.urlAndTokenModel, this.successCallback.bind(this),
this.failureCallback.bind(this));
}
Angular http.service
postPdfBytes(url: string, data: UrlAndTokenModel, successCallback, errorCallback) {
return this.http.post(url, data,
{
headers: new HttpHeaders().set('Content-Type', 'application/json'),
responseType: 'blob'
}
).subscribe(
resp => successCallback(resp),
error => errorCallback(error)
);
}
Web API:
public class TestController : BaseController
{
public TestController(ICacheHelper cacheHelper) :
base(cacheHelper)
{
}
[Route("api/pleaseworkthistime")]
[HttpPost]
public HttpResponseMessage GetDocument(UrlAndTokenModel data)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", data.Token);
var responseTask = client.GetAsync(data.UrlEndpoint);
responseTask.Wait();
var result = responseTask.Result;
byte[] finalResult = null;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsByteArrayAsync();
readTask.Wait();
finalResult = readTask.Result;
}
var httpRequestMessage = new HttpRequestMessage();
var httpResponseMessage = httpRequestMessage.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new ByteArrayContent(finalResult);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "mytestpdf.pdf";
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
return httpResponseMessage;
}
}
Then obviously there is the angular URLAndTokenModel with a URLEndpoint and a Token - the same is mirrored in the C# model.
Finally solved it! I hope this helps anyone else going through the same issue.
When it posts to the .NET API, it firstly needs to be JSON.Stringify
this.httpService.postPdfBytes('/blahblah/api/pleaseworkthistime', JSON.stringify(this.urlAndTokenModel), this.successCallback.bind(this),
this.failureCallback.bind(this));
The angular http.service also needs to be updated therefore to a string instead of a model.
postPdfBytes(url: string, data: string, successCallback, errorCallback)
at this point it still wasn't having it, and then a simple add of [FromBody] to the Controller and walahh!
public HttpResponseMessage GetDocument([FromBody] UrlAndTokenModel data)

Receiving XML in HttpPost Using C#

I thought this would be pretty easy, but it's just not - at least for me. I am trying to send an XML string to a REST endpoint. At this time, the only thing the endpoint has to do is log the XML to a file or database. The XML itself is unknown, it could be literally any length and have any number of nodes. So it really needs to be treated as a string.
My problem is that I cannot determine how to receive the XML/string in the Post method. I am using the RestSharp library.
Here is the Post method I am using; very simple. I removed logging code and try/catch code to keep it simple.
[HttpPost]
public IHttpActionResult Post([FromBody] string status)
{
// Log the post into the DB
LogPost(status);
}
The code to perform the post:
public void TestPost()
{
IRestResponse response;
try
{
// Get the base url for
var url = #"http://blahblah/status";
// Create the XML content
object xmlContent = "<XML><test><name>Mark</name></test></XML>";
var client = new RestClient(url);
var request = new RestRequest(Method.POST);
// Add required headers
request.AddHeader("Content-Type", "text/plain");
request.AddHeader("cache-control", "no-cache");
request.AddJsonBody(xmlContent);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
response = client.Execute(request);
}
catch (Exception ex)
{
...
}
}
The problem: the status parameter received by the post is, simply, "Mark". The full XML is missing! I need the entire XML string.
I have tried a few different variations of this. Changing the content-type to "application/xml", "application/json", etc. Nothing is working.
I have tried using request.AddXmlBody(statusObject), and request.AddBody(statusObject). Both were unsuccessful.
I have even tried sending the XML using request.AddHeader() with no luck. What am I missing. There must be something obvious that I'm not getting.
a) you must configure Web API to use XmlSerializer in your WebApiConfig.Register. Otherwise Web API uses the DataContractSerializer by default.
File: App_Start\WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Formatters.XmlFormatter.UseXmlSerializer = true; //HERE!
...
}
b) you need to define a class for your xml
public class test { public string name { get; set; } } //BASED ON YOUR XML NODE
[HttpPost]
public IHttpActionResult Post([FromBody] string status)
{
}
c) if you need to work with a simple string, change POST method
public void Post(HttpRequestMessage request)
{
string body = request.Content.ReadAsStringAsync().Result;
}
d) invoke from restsharp client
string xmlContent = "<test><name>Mark</name></test>";
var client = new RestClient(url);
var request = new RestRequest(Method.POST);
request.AddParameter("application/xml", xmlContent, ParameterType.RequestBody);
var response = client.Execute(request);
For "some" reason request.AddParameter takes the first param as ContentType(not the Name)
https://github.com/restsharp/RestSharp/issues/901
Did you tried to send the request with
Content-Type: application/xml; charset="utf-8"
instead of text\plain?

C# .NET web api return from post request

I am making a POST request to a route which is returning JSON data.
[HttpPost("api/v1/testGetAll")]
public object Test([FromBody]object filteringOptions)
{
return myService.GetLogs(filteringOptions).ToArray();
}
Route works fine, filtering works fine, and when I test the route in Postman I get the right response. However this is only a back-end, and I would like to invoke this route from my custom API gateway.
The issue I'm facing is getting that exact response back. Instead I am getting success status, headers, version, request message etc.
public object TestGetAll(string ApiRoute, T json)
{
Task<HttpResponseMessage> response;
var url = ApiHome + ApiRoute;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
try
{
response = client.PostAsync(url, new StringContent(json.ToString(), Encoding.UTF8, "application/json"));
return response.Result;
}
catch (Exception e)
{
...
}
}
}
How can I get exact content back?
You need to read the content from response.
var contentString = response.Result.Content.ReadAsStringAsync().Result;
If you wish, you can then deserialize the string response into the object you want returning.
public async Task<TResult> TestGetAll<TResult>(string apiRoute, string json)
{
// For simplicity I've left out the using, but assume it in your code.
var response = await client.PostAsJsonAsync(url, json);
var resultString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<TResult>(resultString);
return result;
}
You have to return the response as an HttpResponseMessage.
Try changing your return statement to
[HttpPost("api/v1/testGetAll")]
public IHttpActionResult Test([FromBody]object filteringOptions)
{
return Ok(myService.GetLogs(filteringOptions).ToArray());
}
Please note: This will return the response with status code 200. In case you want to handle the response based on different response code. You can create the HttpResponseMessage like this-
Request.CreateResponse<T>(HttpStatusCode.OK, someObject); //success, code- 200
Request.CreateResponse<T>(HttpStatusCode.NotFound, someObject); //error, code- 404
T is your object type.
And so on...

C# how to not serialize an already serialized string with Content

So I have built an REST API that in itself also consumes another API. Now I could just call the other api, create objects from that call and then make a new request and send it on it's way but that would use up a bit of performance.
I tried just sending the second request again but the problem is that Content serializes it again so I get alot of backslashes. This is my code that does this:
[Route("")]
public IHttpActionResult GetAllDevices()
{
var request = new RestRequest();
request = new RestRequest("devices", Method.GET);
IRestResponse response = client.Execute(request);
return Content(HttpStatusCode.OK, response.Content);//response.Content get's serialized again.
}
As I said, I could deserialized the first call and then just put that in Content, but it feels unnecessary.
Here is one way of doing it, remember to set the content-type explicitly to application/json if needed:
[HttpGet]
[Route("test")]
public HttpResponseMessage Test()
{
const string json = "{ \"test\": 123 }"; // from RestClient
var res = Request.CreateResponse();
res.Content = new StringContent(json);
res.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
return res;
}

Categories