Client unable to read respose from API - c#

I created
Web api
Client.
I am trying to make a simple get request from client to Api
When I debug I can see that my client sends request to web api, web api receives it fine and returns a response. But seems like the response never reaches my client and client keeps waiting forever.
I tried using Postman tool to my Web api and received response fine in json form
I also tried using Fiddle to see where things are going wrong. It showed a downward arrow indicating that the Response was being read from the server. It did not show any error code.
Can someone please suggest what I could be doing wrong.
API:
// GET: api/Account
public IEnumerable<Account> Get()
{
//return new string[] { "value1", "value2" };
Account a = new Account(100, "RES", new Customer(1, "K", "M"), new Premise(1, "2225", "City A", "Ohio"));
Account a1 = new Account(101, "RES", new Customer(1, "R", "M"), new Premise(1, "Prior Road", "Texas", "US"));
Account a2 = new Account(102, "RES", new Customer(1, "A", "M"), new Premise(1, "estern Road", "NY", "US"));
List<Account> list = new List<Account>();
list.Add(a);
list.Add(a1);
list.Add(a2);
return (list);
}
CLIENT :
public async Task<List<Account>> SelectAllAccounts()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:62617/api/");
//HTTP GET
var response = await client.GetAsync("Account");
var myInstance = JsonConvert.DeserializeObject<Account>(await response.Content.ReadAsStringAsync());
List<Account> a= null;
a.Add(myInstance);
return a;
}
}

Some typos exist in your simple example, which should be modified:
// GET: api/Account
public List<Account> Get()
{
Account a = new Account(100, "RES", new Customer(1, "K", "M"), new Premise(1, "2225", "City A", "Ohio"));
// ...
// ... Your Code here
// ...
list.Add(a2);
return list;
}
And, you also need to modify your Client-Side:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:62617/");
HttpResponseMessage response = client.GetAsync("api/Account").Result;
string _content = await response.Content.ReadAsStringAsync();
List<Account> myInstance = JsonConvert.DeserializeObject<List<Account>>(_content);
return myInstance;
}
You can return myInstance or return View(myInstance), or return it as JsonResult, whichever suits your need. For instance:
return Json(new { Status = 200, LIST = myInstance}, JsonRequestBehavior.AllowGet);
However, sometimes it may be better to return _content as string and let De-serialization be done for the last moment.
For more info, following are simple Nice examples on how to create and consume Rest APIs in ASP.Net MVC framework:
https://www.c-sharpcorner.com/article/create-simple-web-api-in-asp-net-mvc/
https://www.c-sharpcorner.com/article/consuming-asp-net-web-api-rest-service-in-asp-net-mvc-using-http-client/
https://www.tutorialsteacher.com/webapi/consume-web-api-get-method-in-aspnet-mvc

Instead of returning IEnumerable I suggest return HttpResponseMessage and also set ContentType to application/json.
So your api will be like this,
HttpResponseMessage httpResponseMessage = this.Request.CreateResponse<List<Account>>(HttpStatusCode.OK, list);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return httpResponseMessage;
In Client code you have to deserialize to List as you are returning collection instead of just Account. So changes in Client code will be
var myInstance = JsonConvert.DeserializeObject<List<Account>>(await response.Content.ReadAsStringAsync());
It's tried and tested.
:-)

Related

Unit Test in Azure Function C#

I want to unit test my azure function API by sending mock request and response data. But my test is getting failed even if i pass same Json data on both request and response.
TestCode
[TestMethod]
public async Task ClinicReadTestMethod()
{
//Arrange
//var clinicRequest = new
//{
// Id = "1",
// OpenIdProvider = "Google",
// Subject = "Test",
// Name = "Test",
// Address = "Test",
// Email = "Test",
// Phone = "Test",
// Notes = "Test"
//};
var query = new Dictionary<string, StringValues>();
query.Add("openIdProvider", "Google");
query.Add("subject", "Test");
//var body = JsonSerializer.Serialize(clinicRequest);
var logger = Mock.Of<ILogger>();
var client = Mock.Of<CosmosClient>();
ContentResultFactory contentResultFactory = new ContentResultFactory();
//Act
var testFunction = new ClinicReadFunction(contentResultFactory);
var result = await testFunction.Run(TestFactory.HttpRequestSetup(query), client, logger); //fixme
var resultObject = JsonSerializer.Serialize(result as ContentResult);
//Assert
var clinicResponse = new
{
Id = "1",
openIdProvider = "Google",
subject = "Test",
Name = "Test",
Address = "Test",
Email = "Test",
Phone = "Test",
Notes = "Test"
};
var resultBody = JsonSerializer.Serialize(clinicResponse);
//var res = contentResultFactory.CreateContentResult(HttpStatusCode.OK);
Assert.AreEqual(resultBody, resultObject);
}
}
This is how my azure function looks like. It is taking two parameters and returning the response. I have tried to mock the data for unit test still no success. If anyone have idea how to unit test this azure function please let me know.
//AzureFunction
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "")] HttpRequest req,
[CosmosDB(
databaseName: "",
containerName: "",
Connection = ""
)] CosmosClient client,
ILogger log)
{
string subject = req.Query["sub"];
if (!Enum.TryParse(req.Query["idp"], out OpenIdProvider openIdProvider) || string.IsNullOrEmpty(subject))
{
var message = "";
log.LogWarning();
return _contentResultFactory.CreateContentResult(message, HttpStatusCode.BadRequest);
}
var query = client.GetContainer("", "").GetItemLinqQueryable<Clinic>()
.Where(x => x.OpenIdProvider == openIdProvider && x.Subject == subject);
Clinic clinic;
using (var iterator = query.ToFeedIterator())
clinic = (await iterator.ReadNextAsync()).FirstOrDefault();
if (clinic == null)
{
log.LogWarning();
return _contentResultFactory.CreateContentResult();
}
var response = new ClinicReadResponse(clinic);
return _contentResultFactory.CreateContentResult(response, HttpStatusCode.OK);
}
//TestFactory
public static HttpRequest HttpRequestSetup(Dictionary<string, StringValues> query)
{
var context = new DefaultHttpContext();
var request = context.Request;
request.Query = new QueryCollection(query);
request.Method = "GET";
return request;
}
In both your Clinic objects, your are generating a new GUID for the ID by calling System.Guid.NewGuid. Assuming the JSON generated from each object is the same shape (they will need to be if you want them to match), the values of each ID property will be different. Since the IDs are different, your JSON strings are not equal, therefore causing the failure.
Here is a post that will show you how to manually create a Guid. You can use this to ensure your IDs are of the same value when testing.
Assigning a GUID in C#
I don't know what your Azure Function code looks like, but your test's setup to make an HTTP request tells me you're calling the method tied to the Http Trigger. Consider the scope of what your method is doing; if it is large (or is calling other methods), this will increase the chances of your test breaking as you change the Azure Function over time. To help future-proof your test make sure the method it's calling has a single responsibility. This will make debugging your code easier to do if a change does make your test fail, and will lessen the likelihood of needing to edit your test to accommodate for code changes.

Calling Microsoft Graph API to create an event, What am I doing wrong?

I've been trying to call Microsoft Graph API for creating events, however I've not been able to do it.
Context: I have a Web MVC application (C#) already in production, with the "common" authentication method, reading a database of users. Recently the customer asked me the possibility to create Microsoft Teams Meetings from the application and also those created meetings have to be scheduled in the Microsoft Teams Calendar with the "Join" button to enter the meeting.
I already configured the API permissions, client secret and used the other properties like tenant, user id, etc from the Azure Portal, I'm sharing a screenshot of my configuration. I'm doing the "Get access on behalf of a user" process.
API Permissions:
Permissions image
Taking the example of the authorize endpoint from the docs, of course I'm replacing the values with my own info
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=11111111-1111-1111-1111-111111111111
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=offline_access%20user.read%20mail.read
&state=12345
Here is my code to Receive the code once the user authorizes the permissions, I'm just storing the value in a static class for testing
public ActionResult ReceiveCode(string code)
{
AuthenticationConfig.Code = code;
//this.Code = code;
return RedirectToAction("Index");
}
Once I got the Auth code, I'm using it to create the event with the generated token, also I already verified that the token contains the permissions given in the Azure Portal.
This is the input for the /events endpoint
var json = JsonConvert.SerializeObject(new
{
subject = "Let's go for lunch",
body = new
{
contentType = "HTML",
content = "Does noon work for you?"
},
start = new
{
dateTime = "2017-04-15T12:00:00",
timeZone = "Pacific Standard Time",
},
end = new
{
dateTime = "2017-04-15T14:00:00",
timeZone = "Pacific Standard Time"
},
location = new
{
displayName = "Harry's Bar",
},
attendees = new List<Attendee>()
{
new Attendee
{
EmailAddress = new EmailAddress
{
Address = "mymail#whatever.com",
Name = "Foo Bar"
},
Type = AttendeeType.Required
}
},
allowNewTimeProposals = true,
isOnlineMeeting = true,
onlineMeetingProvider = "teamsForBusiness",
});
This is the complete method, for the json value, please see the json above. I also tried with the "me" url but it does not work either.
public async Task<ActionResult> OnlineMeeting()
{
try
{
var httpClient = new HttpClient();
var paramsDictionary = new Dictionary<string, string>();
paramsDictionary.Add("client_id",AuthenticationConfig.ClientId);
paramsDictionary.Add("scope", "Calendars.ReadWrite");
paramsDictionary.Add("code", AuthenticationConfig.Code);
paramsDictionary.Add("redirect_uri", "https://localhost:44379/Meeting/Reunion/ReceiveCode");
paramsDictionary.Add("grant_type", "authorization_code");
paramsDictionary.Add("client_secret", AuthenticationConfig.ClientSecret);
var url = string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", "tenant");
var response = await httpClient.PostAsync(url, new FormUrlEncodedContent(paramsDictionary));
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
var jsonResult = JsonConvert.DeserializeObject(jsonResponse) as JObject;
var accessToken = jsonResult.GetValue("access_token").ToString();
httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(new { });
var defaultRequestHeaders = httpClient.DefaultRequestHeaders;
if (defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var data = new StringContent(json);
response = await httpClient.PostAsync("https://graph.microsoft.com/v1.0/users/{user id}/events", data);
if (response.IsSuccessStatusCode)
{
// Nice
}
else
{
Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
}
else
{
Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
}
return View();
}
I'm able to the get the token, but when trying to create the event returns the next response.
{
"error": {
"code": "ResourceNotFound",
"message": "Resource could not be discovered.",
"innerError": {
"date": "2021-08-31T22:58:18",
"request-id": "c5c07afa-fa89-4948-a9f8-f80ca4cbafc3",
"client-request-id": "c5c07afa-fa89-4948-a9f8-f80ca4cbafc3"
}
}
}
Am I missing something? Maybe the wrong endpoint?
Please, help.
Thanks in advance.

Adding users to Slack channel

In the code I have, I am doing a POST request to a Slack API to create a channel for me. According to Slack's documentation, I could add users into "user_ids." Debugging wise I called another API called channels.invite and adding users is successful, so I don't know what's wrong with adding users through conversations.create
class CreateChannels
{
private static readonly HttpClient client = new HttpClient();
static async Task CreateChannel()
{
var values = new Dictionary<string, string>
{
{ "token", "SLACK TOKEN" },
{ "name", "test" },
{ "is_private", "true" },
{ "user_ids", "USER-ID"}
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("https://slack.com/api/conversations.create", content);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
}
}
[1]: https://api.slack.com/methods/conversations.create
This does not work, because the parameter user_ids only works for so called workspace apps, but not for normal Slack like yours. You need to use conversations.invite.
From the documentation (emphasis mine):
Required for workspace apps. A list of between 1 and 30 human users
that will be added to the newly-created conversation. This argument
has no effect when used by classic Slack apps.

IdentityServer4.Models.Client as httppost parameter always null in aspnet core 2.x

I am sending an httpPost parameter "client" of type IdentityServer4.Models.Client via a C# console application to a C# web api and client is always null.
If I send a different object using the same code, the parameter is received just fine. This seems to be a problem specific to Identity Server 4 and I don't know what's going on.
Server code:
[HttpPost]
public JsonResult Post(IdentityServer4.Models.Client client){
return Json(new { result = true });
}
Client code:
private static async Task Register(string clientName){
var controllerName = "BasicClient";
var basicClientApi = string.Format("http://localhost:5100/api/{0}", controllerName);
using (var httpClient = new HttpClient()){
var clientData = new IdentityServer4.Models.Client();
var client = new { client = clientData };
client.client.ClientName = clientName;
var json = JsonConvert.SerializeObject(client);
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync(basicClientApi, content);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var rawResponse = await response.Content.ReadAsStringAsync();
JObject o = JObject.Parse(rawResponse);
Console.WriteLine(o.ToString());
}
}
}
EDIT
After applying [FromBody] and unwrapping the object, I am still getting null for client in the receiving Web API. One thing caught my eye on the console application debug screen though.. see the image below:
The actual client variable is null in the console application, yet JsonConvert was able to serialize it into something. What's that about?
You are wrapping your model inside an anonymous object, which will turn its JSON representation into something which has nothing in common with your original Client class.
This:
var clientData = new IdentityServer4.Models.Client();
var client = new { client = clientData };
client.client.ClientName = clientName;
var json = JsonConvert.SerializeObject(client);
Will result in a JSON similar to the following:
{
"client": {
"clientName": "foo",
"anotherProperty": "bar",
// other properties omitted for brevity
}
}
But what you really want is just the Client object:
{
"clientName": "foo",
"anotherProperty": "bar",
// other properties omitted for brevity
}
Do not wrap your clientData, just serialize it directly to follow the model inside your MVC Action Method:
var clientData = new IdentityServer4.Models.Client();
clientData.ClientName = clientName;
var json = JsonConvert.SerializeObject(clientData);
For everything to be working, you have to tell the model binder explicitly where to expect the data.
Use [FromBody] attribute on the model.
[FromBody]: Use the configured formatters to bind data from the request body. The formatter is selected based on content type of the request.
[HttpPost]
public IActionResult Post([FromBody]IdentityServer4.Models.Client client) {
return Json(new { result = true });
}
Reference Model Binding in ASP.NET Core
You're not going to believe this, but ultimately the reason why IdentityServer.Models.Client was null in the Web Api post parameter was because the class is decorated with [DebuggerDisplay("{ClientId}")] and I did not provide a ClientId in my test application, so it was always showing up as null when in fact it actually WAS THERE THE WHOLE TIME. I am glad this issue is behind me, but I am very angry about this "feature".

Using twilio c# client to pass twilio response back

I am using the twilio c# wrapper and am able to receive communication from twilio. I am having trouble responding back to twilio with a response and having twilio recognize it. Currently I am trying to respond with a TwilioResponse. Anyone have a good example? This is a WebApi self hosted windows service.
ex. TwilioResponse().Message("ok");
I have used Same web API you can use the below menioned code but First you have to configure your APP in Twilio UserAccount.
public class WelcomeController : ApiController
{
public HttpResponseMessage Post(VoiceRequest request)
{
var response = new TwilioResponse();
response.Say("Welcome to Dhaval demo app. Please enter your 5 digit ID.");
response.Gather(new { numDigits = 5, action = string.Format("/api/Authenticate") });
return this.Request.CreateResponse(HttpStatusCode.OK, response.Element, new XmlMediaTypeFormatter());
}
}
When I press the 5 digit no then it will lend to Authetication Controller it' look like
public HttpResponseMessage Post(VoiceRequest request)
{
var response = new TwilioResponse();
var validIds = new List<string> { "12345", "23456", "34567" };
var userId = request.Digits;
var authenticated = validIds.Contains(userId);
if (!authenticated)
{
response.Say("You entered an invalid ID.");
response.Hangup();
}
else
{
response.Say("ID is valid.");
response.Redirect(string.Format("/api/Name?userId={0}", userId));
}
return this.Request.CreateResponse(HttpStatusCode.OK, response.Element, new XmlMediaTypeFormatter());
}
and it's work fine in my side, once again check it properly you have given the correct AuthToken
Cheers
After tinkering for a while, this seems to do the trick.
var twiml = new TwilioResponse().Message("test");
return TwilioResponse(twiml);
private HttpResponseMessage TwilioResponse(TwilioResponse twilioResponse)
{
return new HttpResponseMessage()
{
Content = new StringContent(twilioResponse.ToString(), Encoding.UTF8, "text/xml"),
StatusCode = System.Net.HttpStatusCode.OK
};
}

Categories

Resources