Consuming web api with RestSharp basic authentication return null - http

I am trying to consume an endpoint with RestSharp with Basic authentication.
I followed the instructions on the documentation https://restsharp.dev/getting-started/getting-started.html
The request was successful but I think the request body was malformed.
How can I get this to work
internal BalanceInquiryResponse BalanceInquiryRest(BalanceInquiryRequest BalanceInquiryRequest, Settings Settings)
{
// BalanceInquiryResponse BalanceInquiryResponse = new BalanceInquiryResponse();
var client = new RestClient(Settings.BaseUrl + "All/Inquiry");
client.Authenticator = new HttpBasicAuthenticator(Settings.Username, Settings.Password);
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddJsonBody(new
{
Acc = BalanceInquiryRequest.Acc
});
IRestResponse response = client.Execute(request);
IRestResponse<BalanceInquiryResponse> res = client.Execute<BalanceInquiryResponse>(request);
if (response.IsSuccessful)
{
BalanceInquiryResponse = new BalanceInquiryResponse
{
responseInquiry = res.Data.responseInquiry,
ResponseDescription = res.Data.ResponseDescription,
ResponseMessage = res.Data.ResponseMessage
};
return BalanceInquiryResponse;
}
else
{
BalanceInquiryResponse = new BalanceInquiryResponse
{
ResponseDescription = responseses.ErrorMessage,
};
return BalanceInquiryResponse;
}
}
This is my response body
{
"responseMessage": "Successful",
"responseDescription": "Request Successful",
"responseInquiry": null
}
When I tried with postman I got
{
"ResponseMessage": "Successful",
"ResponseDescription": "Request Successful",
"response": {
"AvalBal": 586324.42,
"ReverAmt": 0,
"AccCurrency": "US "
}
}

IRestResponse<BalanceInquiryResponse> res = client.Execute<BalanceInquiryResponse>(request);
So there is a specific reason...you are putting BalanceInquiryResponse in the generic IRestResponse above.
With the above call, this should automatically hydrate the BalanceInquiryResponse object, and you shouldn't need to hand map.
Aka, you should ~not~ need this below code:
BalanceInquiryResponse = new BalanceInquiryResponse
{
responseInquiry = res.Data.responseInquiry,
ResponseDescription = res.Data.ResponseDescription,
ResponseMessage = res.Data.ResponseMessage
};
I think your issue is that your POCO object (BalanceInquiryResponse) should perfectly match the "structure" of the JSON.
Change your BalanceInquiryResponse to PERFECTLY match the json "properties".
and recognize you have a nested object.
I think it it would be:
public class ResponsePoco {
public double AvalBal { get; set; }
public int ReverAmt { get; set; }
public string AccCurrency { get; set; }
}
public class BalanceInquiryResponse{
public string ResponseMessage { get; set; }
public string ResponseDescription { get; set; }
public ResponsePoco response { get; set; }
}
Pay attention the to "ResponsePoco response"..note the variable name is LOWERCASE .. because...the json has a lowercase "response" in it.
I have called the (child) object "ResponsePoco" to highlight the difference between the object name and the variable name.
If you cannot "perfectly" match the Poco properties. you can use attributes to "massage" the discrepencies. As seen here:
https://www.newtonsoft.com/json/help/html/JsonPropertyName.htm
public class Videogame
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("release_date")]
public DateTime ReleaseDate { get; set; }
}

Related

how to generate a Http Request for AAD?

so, I get the Access Token and I want to create a request...
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", accesToken);
HttpResponseMessage response = await httpClient.GetAsync("https://graph.microsoft.com/v1.0/users");
Also, the Problem may be on setting the scopes for the authentication...i set it to:
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
But I don't know if it gets all application permissions that I have (it is a deamon).
You can use this sample which will help you to create events with same client credential flow which you are using but you need to change some things here.
You need to first give the Calendar.ReadWrite permission in the Azure portal for your app.
You need to add the below code in the Program.cs
if (result != null)
{
var httpClient = new HttpClient();
var apiCaller = new ProtectedApiCallHelper(httpClient);
await apiCaller.CallWebAPIToPostEvent($"{config.ApiUrl}v1.0/users/{user obj id}/calendars/{calendar id}/events", result.AccessToken, Display);
}
Then you need to add the below classes in the protectedApiCallHelper.cs
public class Event
{
[JsonProperty("subject")]
public string Subject { get; set; }
[JsonProperty("body")]
public Body Body;
[JsonProperty("start")]
public TimeAndDate Start;
[JsonProperty("end")]
public TimeAndDate End;
[JsonProperty("location")]
public Location Location;
[JsonProperty("attendees")]
public List<Attendees> Attendees;
}
public class Body
{
[JsonProperty("contentType")]
public string ContentType { get; set; }
[JsonProperty("content")]
public string Content { get; set; }
}
public class TimeAndDate
{
[JsonProperty("dateTime")]
public string DateTime { get; set; }
[JsonProperty("timeZone")]
public string TimeZone { get; set; }
}
public class Location
{
[JsonProperty("displayName")]
public string DisplayName { get; set; }
}
public class Attendees
{
[JsonProperty("emailAddress")]
public EmailAddress EmailAddress;
[JsonProperty("type")]
public string Type;
}
public class EmailAddress
{
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
In this same ProtectedApiCallHelper class you can create a post request and get the details by adding the below code
public async Task CallWebAPIToPostEvent(string webApiUrl, string accessToken, Action<JObject> processResult)
{
var defaultRequetHeaders = HttpClient.DefaultRequestHeaders;
if (defaultRequetHeaders.Accept == null || !defaultRequetHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequetHeaders.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
var payload = new Event
{
Subject = "Let's go for lunch",
Body = new Body
{
ContentType = "HTML",
Content = "Does mid month work for you?"
},
Start = new TimeAndDate
{
DateTime = "2019-03-15T12:00:00",
TimeZone = "Pacific Standard Time"
},
End = new TimeAndDate
{
DateTime = "2019-03-15T14:00:00",
TimeZone = "Pacific Standard Time"
},
Location = new Location
{
DisplayName = "Harry's Bar"
},
Attendees = new List<Attendees>
{
new Attendees
{
EmailAddress = new EmailAddress
{
Address = "Shiva#nishantsingh.live",
Name = "Shiva"
},
Type = "required"
}
}
};
// Serialize our concrete class into a JSON String
var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(payload));
// Wrap our JSON inside a StringContent which then can be used by the HttpClient class
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
HttpResponseMessage response = await HttpClient.PostAsync(webApiUrl, httpContent);
if (response.Content != null)
{
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
}
This will help you create the event with these details.

Error retrieving data from Api Controller

I'm working on an ASP.NET Core Api and Xamarin forms client using Visual Studio 2017.
I'm getting an error
System.Runtime.Serialization.SerializationException: Invalid JSON string
because response.Content is null, when retrieving data from API but when paste this Url in browser "https://localhost:44305/api/Agreement/GetAgreementText/1" it shows data in the browser. When I run using client it's not hit to api method debug point .
Here is my APi method
[HttpGet]
[Route("GetAgreementText/{id}")]
public DefaultApiResult GetAgreementText(long Id)
{
Company com = _companyRepository.Get(Id);
string st = com.AgreementText;
DefaultApiResult result = new DefaultApiResult
{
Data = st
};
return result;
}
Here is my client application Api invoking method
public string GetAgreementTextLoading(long idCompany)
{
string agreementText = "";
// var token = _tokenService.GetLastActivateToken().Hash;
var clientURL = "https://localhost:44305/";
var client = new RestClient(clientURL);
var request = new RestRequest("api/Agreement/GetAgreementText/{Id}", Method.GET);
request.AddUrlSegment("Id", idCompany.ToString());
IRestResponse response = client.Execute(request);
AppRestResponse apiResponse = SimpleJson.DeserializeObject<AppRestResponse>(response.Content);
var statusMessage = "";
if (apiResponse.Success)
{
statusMessage = "Success.";
if (!string.IsNullOrEmpty(response.Content))
{
agreementText = apiResponse.Data.ToString();
}
else
{
throw new Exception("Invalid response");
}
}
else
{
agreementText = "Error retrieving agreement text";
}
return agreementText;
}
public class AppRestResponse
{
public bool Success { get; set; }
public object Data { get; set; }
public IEnumerable<AppRestReponseError> ErrorMessages { get; set; }
}
public class DefaultApiResult
{
public bool Success
{
get
{
return ErrorMessages.Count == 0;
}
private set { }
}
public List<ErrorMessage> ErrorMessages { get; set; }
public object Data { get; set; }
public DefaultApiResult()
{
ErrorMessages = new List<ErrorMessage>();
}
public DefaultApiResult(string errorMessage)
:this()
{
ErrorMessages.Add(new ErrorMessage()
{
Message = errorMessage
});
}
public DefaultApiResult(string[] errorMessages)
:this()
{
foreach (var errorMessage in errorMessages)
{
ErrorMessages.Add(new ErrorMessage()
{
Message = errorMessage
});
}
}
}
I'm not sure about the SimpleJson and the rest client you are using .
However , assuming you're using the RestSharp , it seems that there's no need to use the SimpleJson to deserialize response here .
I just remove the following codes :
IRestResponse response = client.Execute(request);
AppRestResponse apiResponse = SimpleJson.DeserializeObject<AppRestResponse>(response.Content);
and add the following two lines:
IRestResponse<AppRestResponse> response = client.Execute<AppRestResponse>(request);
var apiResponse= response.Data;
It works as expected .

MultipartFormDataStreamProvider for ASP.NET Core 2

I am in the process of migrating a project from ASP.NET MVC 5 to ASP.NET Core 2 and have run into some issues regarding the MultipartFormDataStreamProvider
As far as I can tell it's not yet a part of .NET Core and therefore cannot be used. The issue I'm trying to solve is a part of the code where Sendgrid is beeing used, parsing of e-mails.
The .NET MVC 5 code looks as follows
[HttpPost]
public async Task<HttpResponseMessage> Post()
{
var root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
var email = new Email
{
Dkim = provider.FormData.GetValues("dkim").FirstOrDefault(),
To = provider.FormData.GetValues("to").FirstOrDefault(),
Html = provider.FormData.GetValues("html").FirstOrDefault()
}
}
This code is a snippet taken from the Sendgrid API Documentation: https://sendgrid.com/docs/Integrate/Code_Examples/Webhook_Examples/csharp.html
So I have been fiddling with this for a while, trying to come up with a solution but I'm utterly stuck. The closest to a solution I've come is to use Request.Form e.g
To = form["to"].SingleOrDefault(),
From = form["from"].SingleOrDefault()
However this only works when sending in data through the ARC REST Client plugin for Chrome (or any other REST-API tester). Also this solution won't be able to handle attachments such as images and the like.
So I'm turning to the community of StackOverflow hoping that someone has some pointers or a solution for how to migrate this to .NET Core 2.
Thanks in advance!
Here is my solution so far. It is still a work in progress, for example, in terms of handling attachments but it is successfully parsing the email. It borrows heavily from Wade's blog on uploading files in ASP.NET Core at https://dotnetcoretutorials.com/2017/03/12/uploading-files-asp-net-core/
[HttpPost]
[DisableFormValueModelBinding]
[Route("v4/ProcessEmail")]
public async Task<IActionResult> ParseSendGridInboundWebHook()
{
FormValueProvider formModel;
using (var stream = System.IO.File.Create("c:\\temp\\myfile.temp"))
{
formModel = await _context.HttpContext.Request.StreamFile(stream);
}
var viewModel = new SendGridEmailDTO();
var bindingSuccessful = await TryUpdateModelAsync(viewModel, prefix: "",
valueProvider: formModel);
if (!bindingSuccessful)
{
if (!ModelState.IsValid)
{
return new BadRequestResult();
}
}
<your code here>
return new OkResult();
}
public static class MultipartRequestHelper
{
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
// The spec says 70 characters is a reasonable limit.
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary);
if (string.IsNullOrWhiteSpace(boundary.Value))
{
throw new InvalidDataException("Missing content-type boundary.");
}
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException(
$"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary.Value;
}
public static bool IsMultipartContentType(string contentType)
{
return !string.IsNullOrEmpty(contentType)
&& contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
}
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="key";
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& string.IsNullOrEmpty(contentDisposition.FileName.Value)
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
}
public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
|| !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
}
}
public static class FileStreamingHelper
{
private static readonly FormOptions _defaultFormOptions = new FormOptions();
public static async Task<FormValueProvider> StreamFile(this HttpRequest request, Stream targetStream)
{
if (!MultipartRequestHelper.IsMultipartContentType(request.ContentType))
{
throw new Exception($"Expected a multipart request, but got {request.ContentType}");
}
// Used to accumulate all the form url encoded key value pairs in the
// request.
var formAccumulator = new KeyValueAccumulator();
string targetFilePath = null;
var boundary = MultipartRequestHelper.GetBoundary(
MediaTypeHeaderValue.Parse(request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
await section.Body.CopyToAsync(targetStream);
}
else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
{
// Content-Disposition: form-data; name="key"
//
// value
// Do not limit the key name length here because the
// multipart headers length limit is already in effect.
var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
var encoding = GetEncoding(section);
using (var streamReader = new StreamReader(
section.Body,
encoding,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1024,
leaveOpen: true))
{
// The value length limit is enforced by MultipartBodyLengthLimit
var value = await streamReader.ReadToEndAsync();
if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))
{
value = String.Empty;
}
formAccumulator.Append(key.Value, value);
if (formAccumulator.ValueCount > _defaultFormOptions.ValueCountLimit)
{
throw new InvalidDataException($"Form key count limit {_defaultFormOptions.ValueCountLimit} exceeded.");
}
}
}
}
// Drains any remaining section body that has not been consumed and
// reads the headers for the next section.
section = await reader.ReadNextSectionAsync();
}
// Bind form data to a model
var formValueProvider = new FormValueProvider(
BindingSource.Form,
new FormCollection(formAccumulator.GetResults()),
CultureInfo.CurrentCulture);
return formValueProvider;
}
private static Encoding GetEncoding(MultipartSection section)
{
MediaTypeHeaderValue mediaType;
var hasMediaTypeHeader = MediaTypeHeaderValue.TryParse(section.ContentType, out mediaType);
// UTF-7 is insecure and should not be honored. UTF-8 will succeed in
// most cases.
if (!hasMediaTypeHeader || Encoding.UTF7.Equals(mediaType.Encoding))
{
return Encoding.UTF8;
}
return mediaType.Encoding;
}
}
public class SendGridEmailDTO
{
public string Dkim { get; set; }
public string To { get; set; }
public string Html { get; set; }
public string From { get; set; }
public string Text { get; set; }
public string SenderIp { get; set; }
public string Envelope { get; set; }
public int Attachments { get; set; }
public string Subject { get; set; }
public string Charsets { get; set; }
public string Spf { get; set; }
}

get user email after Azure AD authentication in xamarin app

I need to get the email adress after the user authentication.
I tried to fid this information in the authenticationResult but I just found the user name .. but not the email.
How can I get this information?
Thanks
Do you want the email on the client-side or the server-side? If it's the server-side, try checking the x-ms-client-principal-name HTTP header value. If it's the client-side, try making an authenticated request to /.auth/me and you should see all the claims, including the user's email in the JSON response.
Since you mentioned in another answer that this is client side, use the InvokeApi<>() method. This is discussed in detail in the book here: https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/authorization/#obtaining-user-claims
Short version is this code:
List<AppServiceIdentity> identities = null;
public async Task<AppServiceIdentity> GetIdentityAsync()
{
if (client.CurrentUser == null || client.CurrentUser?.MobileServiceAuthenticationToken == null)
{
throw new InvalidOperationException("Not Authenticated");
}
if (identities == null)
{
identities = await client.InvokeApiAsync<List<AppServiceIdentity>>("/.auth/me");
}
if (identities.Count > 0)
return identities[0];
return null;
}
Where AppServiceIdentity is defined like this:
public class AppServiceIdentity
{
[JsonProperty(PropertyName = "id_token")]
public string IdToken { get; set; }
[JsonProperty(PropertyName = "provider_name")]
public string ProviderName { get; set; }
[JsonProperty(PropertyName = "user_id")]
public string UserId { get; set; }
[JsonProperty(PropertyName = "user_claims")]
public List<UserClaim> UserClaims { get; set; }
}
public class UserClaim
{
[JsonProperty(PropertyName = "typ")]
public string Type { get; set; }
[JsonProperty(PropertyName = "val")]
public string Value { get; set; }
}
I don't find the InvokeApiAsync to call it.
are there a token or something like that to find the email ?
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, CloudConstants.ApIbaseUrl + /.auth/me");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authenticationResult.Token);
try
{
var response = client.SendAsync(request);
if (response.Result.IsSuccessStatusCode)
{
var responseString = response.Result.Content.ReadAsStringAsync();
var profile = JArray.Parse(responseString.Result);
}
}
catch (Exception ee)
{
_dialogService.DisplayAlertAsync("An error has occurred", "Exception message: " + ee.Message, "Dismiss");
}

JsonResult equivalent to [DataMember(Name="Test")]

I have a method doing this:
public JsonResult Layar(string countryCode, string timestamp, string userId,
string developerId, string layarName, double radius,
double lat, double lon, double accuracy)
{
LayarModel model = new LayarModel(lat, lon, radius);
return Json(model, JsonRequestBehavior.AllowGet);
}
It returns this object:
public class LayarModel
{
private List<HotSpot> _hotSpots = new List<HotSpot>();
public List<HotSpot> HotSpots { get { return _hotSpots; } set { _hotSpots = value; } }
public string Name { get; set; }
public int ErrorCode { get; set; }
public string ErrorString { get; set; }
}
I want the JSON to be
{"hotspots": [{
"distance": 100,
"attribution": "The Location of the Layar Office",
"title": "The Layar Office",
"lon": 4884339,
"imageURL": http:\/\/custom.layar.nl\/layarimage.jpeg,
"line4": "1019DW Amsterdam",
"line3": "distance:%distance%",
"line2": "Rietlandpark 301",
"actions": [],
"lat": 52374544,
"type": 0,
"id": "test_1"}],
"layer": "snowy4",
"errorString": "ok",
"morePages": false,
"errorCode": 0,
"nextPageKey": null
}
Everything comes out capitalised as in the class being returned (HotSpots instead of hotspots).
I have tried DataContract and DataMembers(Name="Test") but that doesn't work. Any suggestions?
JsonResult() uses JavaScriptSerializer internally for serialization, and it seems it doesn't support defining the serialized property names using attributes.
DataContractJsonSerializer supports this, so that may be a way to go.
Some links that may be useful:
JavaScriptSerializer.Deserialize - how to change field names : JavaScriptSerializer.Deserialize - how to change field names
DataContractJsonSerializer Versus JavaScriptSerializer : Changing Field Names: http://publicityson.blogspot.com/2010/06/datacontractjsonserializer-versus.html
I would also recommend installing json.NET but the rest is a lot easier. Below is an extension method I am using in my current application to provide better reuse, feel free to adapt it to your needs, but it should do what you need right out of the box.
public class JsonNetResult : ActionResult
{
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
public JsonNetResult()
{
SerializerSettings = new JsonSerializerSettings
{
//http://odetocode.com/blogs/scott/archive/2013/03/25/asp-net-webapi-tip-3-camelcasing-json.aspx
#if DEBUG
Formatting = Formatting.Indented, //Makes the outputted Json easier reading by a human, only needed in debug
#endif
ContractResolver = new CamelCasePropertyNamesContractResolver() //Makes the default for properties outputted by Json to use camelCaps
};
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
JsonTextWriter writer = new JsonTextWriter(response.Output) {Formatting = Formatting};
JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
serializer.Serialize(writer, Data);
writer.Flush();
}
}
}
public static class JsonNetExtenionMethods
{
public static ActionResult JsonNet(this Controller controller, object data)
{
return new JsonNetResult() {Data = data};
}
public static ActionResult JsonNet(this Controller controller, object data, string contentType)
{
return new JsonNetResult() { Data = data, ContentType = contentType };
}
public static ActionResult JsonNet(this Controller controller, object data, Formatting formatting)
{
return new JsonNetResult() {Data = data, Formatting = formatting};
}
}
Here's an example of using it.
public JsonNetResult Layar(string countryCode, string timestamp, string userId,
string developerId, string layarName, double radius,
double lat, double lon, double accuracy)
{
LayarModel model = new LayarModel(lat, lon, radius);
return this.JsonNet(model);
}
The part to note that solves your problem specifically is when the ContractResolver on the JsonSerializerSettings is set to use new CamelCasePropertyNamesContractResolver()
This way you never have to set custom naming again.

Resources