I'm working with a REST API, that returns 2 different kinds of XML responses for the same request.
For example if I ask for a ticket using some ticket number, say 12345 to this API, it either returns:
The ticket:
Or says that it doesn't have the ticket:
(I couldn't format my XML for some reason so just pasted the screenshot.)
Note that the status code comes to be Ok in both the cases. I'm aware that it's a bad api design but we can't change anything about it.
With some help from this JSON2Csharp website, I came up with these classes to represent the response:
The Ticket class:
[XmlRoot(ElementName = "Tickets")]
public class TicketsResponse
{
public List<Ticket> Tickets { get; set; } = new List<Ticket>();
public bool HasTickets() => Tickets.Any();
}
[XmlRoot(ElementName = "Ticket")]
public class Ticket
{
[XmlElement(ElementName = "Field1", IsNullable = true)]
public string Field1 { get; set; }
public bool ShouldSerializeField1() { return Field1 != null; }
[XmlElement(ElementName = "TicketNumber")]
public int TicketNumber { get; set; }
[XmlElement(ElementName = "SomeOtherDetails")]
public SomeOtherDetails SomeOtherDetails { get; set; }
[XmlElement(ElementName = "Accessorials")]
public object Accessorials { get; set; }
}
[XmlRoot(ElementName = "SomeOtherDetails")]
public class SomeOtherDetails
{
[XmlElement(ElementName = "SomeOtherField1", IsNullable = true)]
public string SomeOtherField1 { get; set; }
public bool ShouldSerializeSomeOtherField1() { return SomeOtherField1 != null; }
}
The Error class:
[XmlRoot(ElementName = "response")]
public class ErrorResponse
{
public byte requestId { get; set; }
public byte errorCode { get; set; }
public string errorDesc { get; set; }
public ErrorResponseBody body { get; set; }
public bool HasErrors()
{
var hasTopLevelError = errorCode != 0;
var hasErrorBody = body?.errors?.Any() ?? false;
if (hasTopLevelError || hasErrorBody)
{
return true;
}
return false;
}
public string ErrorMessage()
{
var hasTopLevelError = errorCode != 0;
var hasErrorBody = body?.errors?.Any() ?? false;
if (hasTopLevelError)
{
return errorDesc;
}
else if (hasErrorBody)
{
return string.Join(", ", body.errors.Select(e => e.errorDescription));
}
return null;
}
}
[XmlRoot(ElementName = "body")]
public class ErrorResponseBody
{
[XmlElement("errors")]
public List<Error> errors { get; set; }
}
[XmlRoot(ElementName = "Error")]
public class Error
{
public byte errorId { get; set; }
public string errorDescription { get; set; }
public string errorObjectId { get; set; }
}
I then call the API using a TicketNumber that exists.
I'm using RestSharp for calling the api:
public async void SendRequestAndReceiveResponse()
{
var restClient = new RestClient("https://someapiaddress.net");
var requestXMLBody = "<request><request_id>1</request_id><operation>retrieve</operation><method /><entity>ticket</entity><user>someuser</user><password>somepassword</password><body><ticket><TicketNumber>12345</TicketNumber></ticket></body></request>";
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", requestXMLBody, "text/xml", ParameterType.QueryString);
var response = await restClient.ExecuteAsync<TicketsResponse>(request);
// Do other stuffs with this response...
}
Now this works very well. Because I know my response will have the ticket and that will correctly deserialize to TicketsResponse object.
But if I call the API using a TicketNumber that doesn't exist, I simply get TicketsResponse object that has an empty list of Tickets because this time I'm getting error response. The status code comes to be OK in this case too.
What I want to do here is that I want to capture the error message from the error response. (Response of either Ticket or Error applies to bunch of other processes as well, so it's important to grab this information in a single call.)
And if I knew this ticket doesn't exist, I could simply call the API this way and capture the errors. But that's not ideal nor even a good idea:
var response = await restClient.ExecuteAsync<ErrorResponse>(request);
So I thought of combining TicketsResponse and ErrorResponse, like this:
[XmlRoot]
public class CombinedResponse
{
[XmlElement(ElementName = "Tickets")]
public TicketsResponse Data { get; set; }
[XmlElement(ElementName = "response")]
public ErrorResponse NonData { get; set; }
}
And get the response using that class:
var response = await restClient.ExecuteAsync<CombinedResponse>(request);
The Status code comes OK (when it returns either data or error message) and I get my correct response in response.Content, but the deserialization doesn't work, so my response.Data will show 2 fields Data and NonData both as null. Ideally I should have gotten either my Ticket data or Error data in response.Data.
So my question is:
Is it possible to make this work using a single class for deserialization?
I have spent too much time on this so any help is appreciated.
Also please look at my model classes and suggest if there's better way of doing things.
This is how I solved this issue.
I'm posting here so others may find it helpful.
If there's a better way of doing this, please advise.
I created a method to call the API and deserialize the response to multiple types:
public async Task<(T1, T2)> SendRequestAndReceiveResponse<T1, T2>(RestRequest request)
{
// This can be done in the constructor so we don't instantiate new client for every request.
var restClient = new RestClient("https://someapiaddress.net");
// Get response:
var response = await restClient.ExecuteAsync(request);
// Log request and response here if you want.
if (response.ErrorException != null)
{
var message = $"An error occured during this request. Check request response entries for more details.";
var newException = new Exception(message, response.ErrorException);
throw newException;
}
else
{
var xmlDeserilizer = new RestSharp.Deserializers.XmlDeserializer();
var data = xmlDeserilizer.Deserialize<T1>(response);
var nonData = xmlDeserilizer.Deserialize<T2>(response);
return (data, nonData);
}
}
And used it, by sending the types I need:
public async Task<IEnumerable<Ticket>> FetchTickets()
{
var xmlRequestBody = "<request><request_id>1</request_id><operation>retrieve</operation><method /><entity>ticket</entity><user>someuser</user><password>somepassword</password><body><ticket><TicketNumber>12345</TicketNumber></ticket></body></request>";
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", xmlRequestBody, "text/xml", ParameterType.QueryString);
var apiCallResult = await SendRequestAndReceiveResponse<TicketsResponse, ErrorResponse>(request);
if (apiCallResult.Item1 != null && apiCallResult.Item1.HasTickets())
{
// Do something with the tickets...
}
else if (apiCallResult.Item2 != null && apiCallResult.Item2.HasErrors())
{
// Do something with the errors...
}
// And so on...
}
My complete solution. If you just need the answer, please take a look at the accepted answer.
This is more like a documentation of the whole process for anyone who deals with RestSharp and XML.
Request:
To form a request body that looks like this, we need few classes like below:
[XmlRoot(ElementName = "request")]
[XmlInclude(typeof(RequestBodyWithTicketNumberOnly))] // To make sure 'body' can be serialized to RequestBodyWithTicketNumberOnly
public class TicketRequestBase
{
public byte request_id { get; set; }
public string operation { get; set; }
public string method { get; set; }
public string entity { get; set; }
public string user { get; set; }
public string password { get; set; }
// body can have different shapes, so not giving it any specific class name.
public object body { get; set; }
}
[XmlRoot(ElementName = "body")]
public class RequestBodyWithTicketNumberOnly
{
public TicketWithTicketNumberOnly ticket { get; set; }
}
[XmlRoot(ElementName = "ticket")]
public class TicketWithTicketNumberOnly
{
public string TicketNumber { get; set; }
}
A method to convert C# objects into XML strings, like so:
public static string ToXml<T>(T obj)
{
var settings = new XmlWriterSettings
{
Indent = false,
OmitXmlDeclaration = true,
NewLineHandling = NewLineHandling.None,
NewLineOnAttributes = false
};
var objType = obj.GetType();
var serializer = new XmlSerializer(objType);
var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, obj, emptyNamespaces);
return stream.ToString();
}
}
A method to return request body as XML string:
public static string GetTicketFetchRequestBody(string ticketNumber)
{
if (ticketNumber== null) throw new ArgumentNullException(nameof(ticketNumber));
var singleTicketRequest = new TicketRequestBase()
{
request_id = 1,
operation = "retrieve",
method = string.Empty,
entity = "ticket",
user = "sauser",
password = "sapassword",
body = new RequestBodyWithTicketNumberOnly() { ticket = new TicketWithTicketNumberOnly { TicketNumber = ticketNumber} }
};
return ToXml(singleTicketRequest);
}
Response:
All the classes for the response are already documented in this question. Please take a look at them.
The top level method that gets the Ticket:
public static async Task<IEnumerable<Ticket>> FetchTickets()
{
var xmlRequestBody = GetTicketFetchRequestBody("12345");
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", xmlRequestBody, "text/xml", ParameterType.QueryString);
var apiCallResult = await SendRequestAndReceiveResponse<TicketsResponse, ErrorResponse>(request);
if (apiCallResult.Item1 != null && apiCallResult.Item1.HasTickets())
{
// Do something with the tickets...
}
else if (apiCallResult.Item2 != null && apiCallResult.Item2.HasErrors())
{
// Do something with the errors...
}
// And so on...
}
The method that actually calls the API uses Proxy, logs requests and responses, and executes request using Polly retry policy:
public async Task<(T1, T2)> SendRequestAndReceiveResponse<T1, T2>(RestRequest request, bool useProxy = true)
{
// This can be done in the constructor so we don't instantiate new client for every request.
var restClient = new RestClient("https://someapiaddress.net");
if (useProxy) // This variable can even be initialized in the constructor of this RestClient
{
var proxy = GetWebProxy();
restClient.Proxy = proxy;
}
// Request Logging Part:
var requestAsJSONString = GetRequestForLogging(request, restClient);
// Log it using your logging provider.
// Response Part:
var response = await ExecuteAsyncWithPolicy(request, restClient);
// Response Logging Part:
var responseAsString = response.Content;
// Log it using your logging provider.
if (response.ErrorException != null)
{
var message = $"An error occured during this request. Check request response entries for more details.";
var newException = new Exception(message, response.ErrorException);
throw newException;
}
else
{
var xmlDeserilizer = new RestSharp.Deserializers.XmlDeserializer();
var data = xmlDeserilizer.Deserialize<T1>(response);
var nonData = xmlDeserilizer.Deserialize<T2>(response);
return (data, nonData);
}
}
Create Web proxy like so:
private static WebProxy GetWebProxy()
{
var proxyUrl = "http://proxy.companyname.com:9090/";
return new WebProxy()
{
Address = new Uri(proxyUrl),
BypassProxyOnLocal = false,
//UseDefaultCredentials = true, // This uses: Credentials = CredentialCache.DefaultCredentials
//*** These creds are given to the proxy server, not the web server ***
Credentials = CredentialCache.DefaultNetworkCredentials
//Credentials = new NetworkCredential("proxyUserName", "proxyPassword")
};
}
Create the request string with all the parameters like so:
private string GetRequestForLogging(IRestRequest request, IRestClient client)
{
var serializer = new JsonSerializer();
var requestToLog = new
{
// This will generate the actual Uri used in the request
RequestUri = client.BuildUri(request),
// Parameters are custom anonymous objects in order to have the parameter type as a nice string
// otherwise it will just show the enum value
parameters = request.Parameters.Select(parameter => new
{
name = parameter.Name,
value = parameter.Value,
type = parameter.Type.ToString()
}),
// ToString() here to have the method as a nice string otherwise it will just show the enum value
method = request.Method.ToString()
};
return serializer.Serialize(requestToLog);
}
Polly retry policy:
private AsyncPolicy<IRestResponse> GetRetryPolicy()
{
var policy = Polly.Policy.HandleResult<IRestResponse>((response) =>
{
return response.ResponseStatus != ResponseStatus.Completed;
})
//.Or<SomeKindOfCustomException>()
.RetryAsync();
return policy;
}
Call the API using the retry policy:
private async Task<IRestResponse> ExecuteAsyncWithPolicy(IRestRequest request, IRestClient restClient)
{
var policy = GetRetryPolicy();
var policyResult = await policy.ExecuteAndCaptureAsync(async () => await restClient.ExecuteAsync(request));
return (policyResult.Outcome == OutcomeType.Successful) ? policyResult.Result : new RestResponse
{
Request = request,
ErrorException = policyResult.FinalException
};
}
Hope this was helpful.
Related
I have problem debug the below (in the simplest way possible...). I have a set of properties for a JSON, everything works up to the point that I try to serialize. I would appreciate the simplest way possible to correct, I have to use Newtonsoft.
Below the full C# code. The error area is being marked in comments.
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace MY_TEST
{
public partial class headers
{
[JsonProperty("RequestID")]
public string myRequest { get; set; } = "someIDhere";
[JsonProperty("CorrelationID")]
public string CorrelationID { get; set; } = "1234567890";
[JsonProperty("Token")]
public string Token { get; set; } = "areallylongstringgoeshereastoken";
[JsonProperty("ContentType")]
public string Content_Type { get; set; } = "application/x-www-form-urlencoded";
}
public partial class access
{
[JsonProperty("allPs")]
public string allPs { get; set; } = "all";
[JsonProperty("availableAccounts")]
public string availableAccounts { get; set; } = "all";
}
public partial class body
{
[JsonProperty("combinedServiceIndicator")]
public bool combinedServiceIndicator { get; set; } = false;
[JsonProperty("frequencyPerDay")]
public int frequencyPerDay { get; set; } = 4;
[JsonProperty("recurringIndicator")]
public bool recurringIndicator { get; set; } = false;
[JsonProperty("validUntil")]
public string validUntil { get; set; } = "2020-12-31";
}
public class Consent //RootObject
{
[JsonProperty("headers")]
public headers headers { get; set; }
[JsonProperty("body")]
public body body { get; set; }
}
class Program
{
static HttpClient client = new HttpClient();
static void ShowConsent(Consent cust_some)
{
Console.WriteLine(cust_some.ToString());
}
static async Task<Uri> CreateConsentAsync(Consent cust_some)
{
HttpResponseMessage response = await client.PostAsJsonAsync("http://myurladdr:8001/me/and/you/api/", cust_some);
ShowConsent(cust_some);
response.EnsureSuccessStatusCode();
return response.Headers.Location;
}
static async Task<Consent> GetConsentAsync(string path)
{
Consent cust_some = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
cust_some = await response.Content.ReadAsAsync<Consent>();
}
return cust_some;
}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("http://myurladdr:8001/me/and/you/api/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// >---------- ERROR: Cannot initialize type 'Consent' with a collection initializer because it does not implement 'System.Collection.IEnumerable' ----------<
Consent cust_some = new Consent
{
// Headers
cust_some.headers.myRequest = "someIDhere",
cust_some.headers.CorrelationID = "1234567890",
cust_some.headers.Token = "areallylongstringgoeshereastoken"
cust_some.headers.Content_Type = "application/x-www-form-urlencoded",
// Body
cust_some.body.access.allPs = "all",
cust_some.body.access.availableAccounts = "all",
cust_some.body.combinedServiceIndicator = false,
cust_some.body.frequencyPerDay = 4,
cust_some.body.recurringIndicator = false,
cust_some.body.validUntil = "2020-12-31"
};
// >---------- ERROR ----------<
string json = JsonConvert.SerializeObject(cust_some, Formatting.Indented);
Console.WriteLine(json.ToString());
Console.WriteLine("----------------------------------------------------------");
Console.WriteLine(json);
var url = await CreateConsentAsync(cust_some);
Console.WriteLine($"Created at {url}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
Your'e using the identifier names inside their own initializer. For example, your'e using cust_some inside Consent initializer. You need to remove them, like so:
Consent cust_some = new Consent
{
// Headers
headers = new headers
{
myRequest = "someIDhere",
CorrelationID = "1234567890",
Token = "areallylongstringgoeshereastoken"
Content_Type = "application/x-www-form-urlencoded"
}
// Body
body = new body
{
access = new access
{
allPs = "all",
availableAccounts = "all"
}
combinedServiceIndicator = false,
frequencyPerDay = 4,
recurringIndicator = false,
validUntil = "2020-12-31"
};
}
Also, please note that per Microsoft naming conventions, all identifiers except parameter names should be capitalized, so e.g. headers, body, access and so on, both class and property names, should become Headers, Body, Access. You can read about it here.
In partial class body, add first before all the properties that are needed, the below:
[JsonProperty("access")]
public access access { get; internal set; }
Now, [kudos to user ***HeyJude**** - thank you!] create
Consent cust_some = new Consent
{
headers = new headers
{
myRequest = "someIDhere",
Correlation_ID = "1234567890",
Token = "areallylongstringgoeshereastoken",
Content_Type = "application/json" //"application/json; charset=utf-8"
},
body = new body
{
access = new access //this is how to include access in body
{
allPs = "allAccounts",
availableAccounts = "allAccounts"
},
combinedServiceIndicator = false,
frequencyPerDay = 4,
recurringIndicator = false,
validUntil = "2020-12-31"
}
};
I'm trying to call an external RESTful API in my application and I've been using restsharp for it which has been great so far so but I think I've ran into a problem.
I'm calling an API which normally returns XML documents like this:
<res>
<resultCode>100</resultCode>
<resultText>OK</resultText>
<messageId>52788198</messageId>
<sessionId>TEST</sessionId>
<operatorCode>22802</operatorCode>
</res>
This is my code:
internal class SubmitMessageResponse
{
public ResultCode resultCode { get; set; }
public string resultText { get; set; }
public string messageId; { get; set; }
public string sessionId; { get; set; }
public string operatorCode; { get; set; }
}
public SubmitMessageResponse SendWelcomeSMS(string subscriptionId, string msisdn)
{
var request = new RestRequest(Method.GET);
request.AddQueryParameter("command", "submitMessage")
.AddQueryParameter("username", _Config.User)
.AddQueryParameter("password", _Config.Password)
.AddQueryParameter("msisdn", "00" + _Club.CountryCode + msisdn)
.AddQueryParameter("businessNumber", _Club.SubscribeShortCode)
.AddQueryParameter("content", _Club.WelcomeMessage)
.AddQueryParameter("price", "0")
.AddQueryParameter("sessionId", subscriptionId)
.AddQueryParameter("label", "W")
.AddQueryParameter("keyword", _Club.SubscribeKeyword)
.AddQueryParameter("operatorCode", "test");
return SendGetRequest<SubmitMessageResponse>(request);
}
private T SendGetRequest<T>(RestRequest request) where T : new()
{
try
{
var client = new RestClient(this._BaseUrl);
client.Timeout = 10000;
_Logger.DebugFormat("{0} Request: {1}", "submitMessage", client.BuildUri(request));
var data = client.Execute<T>(request);
_Logger.DebugFormat("{0} Response: {1}", "submitMessage", Newtonsoft.Json.JsonConvert.SerializeObject(data.Content));
return data.Data;
} catch(Exception e)
{
_Logger.Error(e);
}
return default(T);
}
Where ResultCode is an enumeration including all known values that this API returns:
internal enum ResultCode
{
Ok = 100,
AuthorizationInProgress = 150,
PaymentAuthInProcess = 150,
InvalidOperatorCode = 201,
...
}
The problem is that normally this would work correctly but I'm unable to deserialize the enum from the XML:
var response = apiHelper.SendWelcomeSMS(client.ExternalData, client.Number);
Logger.Debug(Newtonsoft.Json.JsonConvert.SerializeObject(response));
My log:
{"messageId":"52788292","sessionId":"TEST","operatorCode":"22802","**resultCode**":0,"resultText":"OK"}
But I also log the response and it works fine:
submitMessage Response: "<res>\r\n\t<**resultCode**>**100**</**resultCode**>\r\n\t<resultText>OK</resultText>\r\n\t<messageId>52788292</messageId>\r\n\t<sessionId>TEST</sessionId>\r\n\t<operatorCode>22802</operatorCode>\r\n</res>\r\n"
Any advice is appreciated.
What I want to do is extremely simple in php. I just want to read the contents of the post. It also extremely simple on sailsjs / node ... I just return the result from within the async function.
In c# asp the answer is eluding me. I want the function to read the contents of the post before it attempts to process the post.
Sometimes the following code works. Sometimes the reading of the json from the post happens too slowly and jsonText is read as "" so nothing is processed.
In all of the test runs the json is being sent in the body of the post.
What is the best way to return a httpResponse after making sure the contents of the post is read first?
public HttpResponseMessage Post()
{
string content;
try
{
string result = String.Empty;
Newtonsoft.Json.Linq.JObject jObject = null;
string jsonText = String.Empty;
var syncTask = new Task<string>( () => {
return Request.Content.ReadAsStringAsync().Result;
});
/* I'm expecting that this will finish */
syncTask.RunSynchronously();
jsonText = syncTask.Result;
/* before this line of code executes */
System.Net.Http.HttpResponseMessage response = new HttpResponseMessage();
if (jsonText == "")
{
result = "{\"error\":\"body is empty\"}";
response.StatusCode = System.Net.HttpStatusCode.InternalServerError;
}
else
{
jObject = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JRaw.Parse(jsonText);
string ipAddress = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
jObject["ipAddress"] = ipAddress;
Models.JsonXML jsonXml = new JsonXML(jObject.ToString(Newtonsoft.Json.Formatting.None));
System.Xml.XmlDocument document = new System.Xml.XmlDocument();
document.LoadXml(jsonXml.xml);
result = ReferralsManager.ProcessReferral(document);
if (result == "")
{
result = "{}";
}
response.StatusCode = System.Net.HttpStatusCode.OK;
}
response.Content = new StringContent(result);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return response;
}
catch (Exception ex)
{
content = ErrorMessage.ServerException(Converter, ex);
return Request.ToResponseMessage(content);
}
finally
{
LogManager.GetCurrentClassLogger().Info(InfoMessage.FUNC_ENDS, "Process Referral");
}
}
The working modified code after the answer from #Mekap is
public class ProcessReferralAddressModel {
public ProcessReferralAddressModel() { }
public string address { get; set; }
public string name { get; set; }
}
public class ProcessReferralModel
{
public ProcessReferralModel()
{
}
public string uuid { get; set; }
public DateTime date { get; set; }
public ProcessReferralAddressModel from { get; set; }
public ProcessReferralAddressModel[] to { get; set; }
public string subject { get; set; }
public string text { get; set; }
public string html { get; set; }
}
/// <summary>
/// Process a referral.
/// </summary>
/// <param name="userid">The userid.</param>
/// <returns></returns>
public HttpResponseMessage Post([FromBody] ProcessReferralModel processReferralModel)
{
string content;
string jsonText = Newtonsoft.Json.JsonConvert.SerializeObject(processReferralModel) ;
try
{
string result = String.Empty;
Newtonsoft.Json.Linq.JObject jObject = null;
System.Net.Http.HttpResponseMessage response = new HttpResponseMessage();
if (jsonText == "" || jsonText == null )
{
result = "{\"error\":\"body is empty\"}";
response.StatusCode = System.Net.HttpStatusCode.InternalServerError;
}
else
{
jObject = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.Linq.JRaw.Parse(jsonText);
string ipAddress = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
jObject["ipAddress"] = ipAddress;
Models.JsonXML jsonXml = new JsonXML(jObject.ToString(Newtonsoft.Json.Formatting.None));
System.Xml.XmlDocument document = new System.Xml.XmlDocument();
document.LoadXml(jsonXml.xml);
result = ReferralsManager.ProcessReferral(document);
if (result == "")
{
result = "{}";
}
response.StatusCode = System.Net.HttpStatusCode.OK;
}
response.Content = new StringContent(result);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return response;
}
catch (Exception ex)
{
content = ErrorMessage.ServerException(Converter, ex);
return Request.ToResponseMessage(content);
}
finally
{
LogManager.GetCurrentClassLogger().Info(InfoMessage.FUNC_ENDS, "Process Referral");
}
}
The json you're fetching, for our example will look something like
{ "ID" : 3,
"StringCmd" : "ls -l"
}
For starters, we are going to write a small class who's representing our data in your web api
public class StringCmdModel
{
public StringCmdModel()
{
}
public int ID { get; set; }
public string StringCmd { get; set; }
}
Now, we just have to write our Entry point in our WebAPI :
[HttpPost]
public HttpResponseMessage PostFonction([FromBody] StringCmdModel NewEntry)
You don't have to check for the existence of the data inside the function. But you should still do proper checks on theirs values, in case you get bad formated json or malicious calls.
But, if you get a call with json that is not matching the StringCmdModel you gave in parameter from the body, this function will not be executed, and the server will throw on its own a 500 error.
I am calling an external service using GetAsync() and passing parameters in query string. When i check the content in the response, i don't see anything returned, however it returns 200 OK and in fiddler it returns me the XML response correctly. I need the XML response to get de-serialize to an C# object and then further save it to DB.
Things tried:
1) Tried this by adding this setting in global- app_start(), It didn't help
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
2) Created an object and tried to sent it via GetAysnc, that didn't help either.
public class Request
{
[XmlElement]
public string XML { get; set; }
[XmlElement]
public List<string> ProNumber { get; set; }
}
2) Should i try passing parameters in query string and expect json result? if i add mediatyperformatter to application/json?
Here is my code:
public async Task<HttpResponseMessage> GetData()
{
string requestString = "&xml=Y&PRONumber=82040X,03117X";
string result = "";
string url = #"http://my.yrc.com/dynamic/national/servlet?CONTROLLER=com.rdwy.ec.rextracking.http.controller.PublicTrailerHistoryAPIController";
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
HttpResponseMessage response = await client.GetAsync(url+requestString);
if (response.IsSuccessStatusCode)
{
return response;
}
}
}
catch (Exception ex)
{
result = ex.Message;
}
return null;
}
EDIT:
Shipments scp = null;
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "Shipment";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(Shipment), xRoot);
using (Stream stream = response.Content.ReadAsStreamAsync().Result)
{
scp = (Shipments)serializer.Deserialize(stream);
}
Model:
public class Shipments
{
[XmlArrayItem(Type = typeof(Shipment))]
public Shipment[] Shipment;
}
public class Shipment
{
[XmlAttribute()]
public int returnCode { get; set; }
.................
..............
Getting error:<SHIPMENTS xmlns=''> was not expected.
Any help on this is much appreciated.
Thanks,
WH
This worked for me -
var client = new HttpClient();
var data = client.GetStringAsync("http://my.yrc.com/dynamic/national/servlet?CONTROLLER=com.rdwy.ec.rextracking.http.controller.PublicTrailerHistoryAPIController&xml=Y&PRONumber=82040X,03117X").Result;
var ser = new XmlSerializer(typeof(Shipments));
var t = (Shipments)ser.Deserialize(new StringReader(data));
public class Shipment
{
public string returnCode { get; set; }
public string returnMessage { get; set; }
public string freightBillNumber { get; set; }
//props
}
[XmlRoot(ElementName = "SHIPMENTS")]
public class Shipments
{
[XmlElement(ElementName = "SHIPMENT")]
public List<Shipment> SHIPMENT { get; set; }
}
EDIT
this works as well -
var data = client.GetStreamAsync("http://my.yrc.com/dynamic/national/servlet?CONTROLLER=com.rdwy.ec.rextracking.http.controller.PublicTrailerHistoryAPIController&xml=Y&PRONumber=82040X,03117X").Result;
EDIT
works as well -
var client = new HttpClient();
var data = client.GetAsync("http://my.yrc.com/dynamic/national/servlet?CONTROLLER=com.rdwy.ec.rextracking.http.controller.PublicTrailerHistoryAPIController&xml=Y&PRONumber=82040X,03117X").Result;
var ser = new XmlSerializer(typeof(Shipments));
var t = (Shipments)ser.Deserialize(data.Content.ReadAsStreamAsync().Result);
I have the following method in my web api controller
public HttpResponseMessage PostGrantAccess(DeviceAccessRequest deviceAccessRequest)
{
var deviceId = deviceAccessRequest.DeviceId;
var deviceAccessResponse = new DeviceAccessResponse(deviceAccessRequest.RequestId)
{
Status = "OK"
};
var response = Request.CreateResponse<DeviceAccessResponse>(HttpStatusCode.OK, deviceAccessResponse);
return response;
}
This is the calling client code:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:55208/");
var request = new DeviceAccessRequest
{
RequestId = Guid.NewGuid().ToString(),
DeviceId = "bla",
LoginId = "tester",
Password = "haha" ,
};
var response = client.PostAsJsonAsync("api/accesspanel", request).Result;
if (response.IsSuccessStatusCode)
{
var deviceAccessResponse = response.Content.ReadAsAsync<DeviceAccessResponse>().Result;
}
}
Object classes:
public class DeviceAccessResponse : ResponseBase
{
public DeviceAccessResponse()
{
}
public DeviceAccessResponse(string correlationId)
: base(correlationId)
{
}
public string Status { get; set; }
}
public class ResponseBase
{
private string correlationId;
public ResponseBase()
{
}
public ResponseBase(string correlationId)
{
this.correlationId = correlationId;
}
}
I am able to receive DeviceAccessRequest in my controller just fine, I am able to get the guid string.
However, after returning the response back to the client, I am only able to get back Status = "OK", the correlationId is null instead of containing the guid string which I have assigned in the client code with this line
var deviceAccessResponse = new DeviceAccessResponse(deviceAccessRequest.RequestId)
What did I miss?
is the response.Content.ReadAsAsync<DeviceAccessResponse>().Result; the correct code to use to reconstruct my whole object?
Your correlationId is a private field. If you want it to serialize over the wire, you probably need to make a public property to expose it.
You should make the correlationId a public property if you want it to be exposed and travel to the client:
public class ResponseBase
{
public ResponseBase()
{
}
public string CorrelationId { get; set; }
public ResponseBase(string correlationId)
{
this.CorrelationId = correlationId;
}
}