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?
Related
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.
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;
}
I’m having some problems using the Web API with the MVC, not sure what is causing it but it doesn’t throw any exceptions or errors in debug mode, please could someone help to resolve this issue.
Code is as follows:
MVC Controller calls:
PortalLogonCheckParams credParams = new PortalLogonCheckParams() {SecurityLogonLogonId = model.UserName, SecurityLogonPassword = model.Password};
SecurityLogon secureLogon = new SecurityLogon();
var result = secureLogon.checkCredentials(credParams);
Data Access Object method:
public async Task <IEnumerable<PortalLogon>> checkCredentials(PortalLogonCheckParams credParams)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:50793/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Check Credentials
//Following call fails
HttpResponseMessage response = await client.PostAsJsonAsync("api/chkPortalLogin", credParams);
if (response.IsSuccessStatusCode)
{
IEnumerable<PortalLogon> logonList = await response.Content.ReadAsAsync<IEnumerable<PortalLogon>>();
return logonList;
}
else return null;
}
}
Web API:
[HttpPost]
public IHttpActionResult chkPortalLogin([FromBody] PortalLogonCheckParams LogonParams)
{
List<Mod_chkPortalSecurityLogon> securityLogon = null;
String strDBName = "";
//Set the database identifier
strDBName = "Mod";
//Retrieve the Logon object
using (DataConnection connection = new DataConnection(strDBName))
{
//Retrieve the list object
securityLogon = new Persistant_Mod_chkPortalSecurityLogon().findBy_Search(connection.Connection, LogonParams.SecurityLogonLogonId, LogonParams.SecurityLogonPassword);
}
AutoMapper.Mapper.CreateMap<Mod_chkPortalSecurityLogon, PortalLogon>();
IEnumerable<PortalLogon> securityLogonNew = AutoMapper.Mapper.Map<IEnumerable<Mod_chkPortalSecurityLogon>, IEnumerable<PortalLogon>>(securityLogon);
return Ok(securityLogonNew);
}
You need to remove the [FromBody] attribute from the parameter
Using
[FromBody]
To force Web API to read a simple type from the request body, add the
[FromBody] attribute to the parameter:
public HttpResponseMessage Post([FromBody] string name) { ... }
In this example, Web API will use a media-type formatter to read the
value of name from the request body. Here is an example client
request.
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"
When a parameter has [FromBody], Web API uses the Content-Type header
to select a formatter. In this example, the content type is
"application/json" and the request body is a raw JSON string (not a
JSON object).
At most one parameter is allowed to read from the message body.
I want to POST data to WebAPI. Ideally I would just do:
http:www.myawesomesite.com?foo=bar
As a POST. But to my specific question, I am trying to use:
using(var webClient = new WebClient())
{
client.uploadString("http:www.myawesomesite.com", "POST", "foo=bar");
}
But that converts "foo=bar" to a bye array. Okay fine, I'm just trying to get it to work at this point.
My Web API controller looks like this:
[HttpPost]
public void MashPotatos(string foo)
{
potatoMasher.Mash(foo);
}
But I get The remote server returned an error: (404) Not Found. First off, I thought WebAPI would automatically grock that data for me even if it was in the body of the request. But more importantly, I just want to be able to get it to work.
Ideally, I'd like to leave the WebAPI method in a form such that you can still invoke it by using a query string with a POST verb.
you need to configure your web API route to accept a foo parameter.May be this will solve your issue
config.Routes.MapHttpRoute(name: "newont",
routeTemplate: "api/{controller}/{foo}",
defaults: new { controller = "Controllername", foo= "foo"}
);
Well here is a post that might be helpful to some. For me I just had to do:
using(var webClient = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
client.uploadString("http:www.myawesomesite.com", "POST", "foo=bar");
}
and
[HttpPost]
public void MashPotatos([FromBody]string foo)
{
potatoMasher.Mash(foo);
}
I decided against doing a POST with a query string as it seems to go against convention but there is also a [FromUri] attribute
public string GetData(){
string jsonResponse = string.Empty;using (WebClient client = new WebClient()){client.Headers[HttpRequestHeader.ContentType] = "application/json";
jsonResponse = client.UploadString(baseuri, "POST", #"{personId:""1"", startDate:""2018-05-21T14:32:00"",endDate:""2018-05-25T18:32:00""}");
return JsonConvert.DeserializeObject<Model>(jsonResponse);}}
#"{personId:""1"",
startDate:""2018-05-21T14:32:00"",endDate:""2018-05-25T18:32:00""}
It's a JSON custom string, or you can serialize the object here on the API side
HttpPost
public string Metod(Something data)
{
DateTimeOffset sDate = DateTimeOffset.Parse(data.date1);
DateTimeOffset eDate = DateTimeOffset.Parse(data.date2);
return _someService.GetService(data.Id, sDate, eDate);
}
Then we go to the service and get data from DB
In the latest Web API 2, how do I configure it so that it will only return a reply if the Accept header is application/json? This API will only support json, if any other accept header is sent then an error must be thrown. There will be no xml and even no html interface.
If the client asks for xml or html or anything, we need to throw an error to let them know they used the wrong accept type. We must not mask this problem by replying with the correct json when they have requested a type that is not actually supported.
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
var response = request.GetResponse();
And the json result is returned successfully. But if there is any other Accept then an error is returned
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/xml"; // or text/html or text/plain or anything
var response = request.GetResponse();
Returns HTTP 501 Not Implemented or similar http error code.
This question is not a duplicate of How do I get ASP.NET Web API to return JSON instead of XML using Chrome? - that question asks how to also return json. My question is how to only return json, and only if the client asks for json. If the client asks for any other type like xml or html, then an error is returned.
This page shows how to access content negotiation directly. You could conceivably instead pass some filtered subset of this.Configuration.Formatters containing only the desired formatters to IContentNegotiator.negotiate, like so:
ContentNegotiationResult result = negotiator.Negotiate(
typeof(Product), this.Request, this.Configuration.Formatters.Where(/* some expression that excludes all but the json formatter */);
This looks quite clumsy and would be a lot of dull boilerplate, so Javad_Amiry's answer is probably better, but this is another option that might be useful in specific cases.
You can clear all formatters except JSON:
configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());
Or you can change the default Web API’s content negotiation mechanism:
public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
return result;
}
}
// in app_start:
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
See the article.
UPDATE:
Well, if you want to return a HTTP error on non-json request, you can do it by implementing a custom IHttpModule for checking header. But, for self-host apps it won't work. So, it's better to use extend a custom DelegatingHandler. For example, you can use this one:
public class FilterJsonHeaderHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken){
if (request.Headers.Accept.All(a => a.MediaType == "application/json")){
// if we have only application/json, so the pipeline continues
return base.SendAsync(request, cancellationToken);
}
// otherwise, do whatever you want:
var response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
var completionSource = new TaskCompletionSource<HttpResponseMessage>();
completionSource.SetResult(response);
return completionSource.Task;
}
}
and register it in app_start:
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new FilterJsonHeaderHandler());
// your other settings...
}
}
NOTE: the code is not tested. Please let me know if there is any error.