I’m new with HttpClient and Web API so I’m sure I’m missing something obvious to others. I’m trying to call a 3rd party Web API with a list of new Employees. The API is supposed to return JSON with information about the employees that were uploaded. This is going to be a console app that will be run once a night. There is no requirement for this to run Async.
I’m trying to follow along with these articles.
http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
and
http://typecastexception.com/post/2013/07/03/Building-Out-a-Clean-REST-ful-WebApi-Service-with-a-Minimal-WebApi-Project.aspx
What’s not working is the following code to try to get the API results into an array or list. When I run it in Debug it gives me an exception “One or more errors occurred.” which doesn’t give me a lot to go on.
if (response.IsSuccessStatusCode)
{
AddEmployeeResult employeeResults = response.Content.ReadAsAsync<AddEmployeeResult>().Result;
}
The vendor gave me this as an example of what should be returned.
[{"EmployeeNumber":"22449","SuccessfullyAdded":true,"Existing":false,"Comments":"App UserId: 6037"},{"EmployeeNumber":"379844","SuccessfullyAdded":true,"Existing":false,"Comments":" App UserId: 6038"},{"EmployeeNumber":"23718","SuccessfullyAdded":true,"Existing":false,"Comments":" App UserId ps: 6039"}]
I created a class in my console app to match what is supposed to be returned from the API.
class AddEmployeeResult
{
public string EmployeeNumber { get; set; }
public bool SuccessfullyAdded { get; set; }
public bool Existing { get; set; }
public string Comments { get; set; }
}
Here is the code I’m trying.
static void Main(string[] args)
{
UploadNewEmployee();
}
public static void UploadNewEmployee()
{
string serviceURI = "https://services.website.com/api/employees/upload";
var credentials = new NetworkCredential("username", "password");
var clientHandler = new HttpClientHandler();
clientHandler.Credentials = credentials;
clientHandler.PreAuthenticate = true;
//*** Get the Employee Data ***
List<Employee> lstEmployee = new List<Employee>();
try
{
lstEmployee = DataManager.GetEmployees();
}
catch (Exception ex)
{
logger.Error("Error while getting data. | {0} | Trace: {1}", ex.Message, ex.StackTrace);
}
//*** Process users if there is data. ***
if (lstEmployee != null && lstEmployee.Count > 0)
{
logger.Info("Employee count: " + lstEmployee.Count);
logger.Debug("Web API Uri: " + serviceURI);
try
{
using (var client = new HttpClient(clientHandler))
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.PostAsJsonAsync(serviceURI, lstEmployee).Result;
if (response.IsSuccessStatusCode)
{
AddEmployeeResult employeeResults = response.Content.ReadAsAsync<AddEmployeeResult>().Result;
}
}
}
catch (Exception ex)
{
logger.Error("Error posting to Web API. | {0} | Trace: {1}", ex.Message, ex.StackTrace);
}
}
else
{
logger.Info("No records found.");
}
}
Related
I am using the nexmo api for .NET to send verification code to given phone which gives me the following error
The underlying connection was closed
My code is as follows
public bool PhoneVerfication(string PhoneNumber, long userId)
{
try
{
long? _cellPhone = Convert.ToInt64(PhoneNumber);
var _util = new utilMessageSender();
_util.SendVerificationCode(PhoneNumber);
return true;
}
catch (Exception ex)
{
return false;
}
}
public utilMessageSender()
{
client = new Client(creds: new Credentials
{
ApiKey = "********",
ApiSecret = "**************",
ApplicationId = "*********-****-****-****-***********",
ApplicationKey = "**************"
});
}
public void SendVerificationCode(string phoneNumber)
{
try
{
var result = client.NumberVerify.Verify(new NumberVerify.VerifyRequest
{
number = phoneNumber,
brand = "Offey-app"
});
RequestId = result.request_id;
}
catch (Exception ex)
{
throw ex;
}
}
I am using this API for the first time it worked very fine and sent verification code to different phone numbers but I don't know what happened as now it's not working.
I'm having a bit of an issue with HTTP calls in production. Here is the scenario:
We have a desktop app that communicates with the web server via HTTP requests. This works fine with the below blurb of code on both the server and client side in QA. Now we move from an internally hosted IIS to AWS and throw it live (same configuration, IIS7/Server 2016, port 443/80 allowed on AWS for all IPs), it now half works. Users are able to log in and authenticate remotely from the client application, but certain calls fail to even reach the method on the controller (returns a 500 error after hanging for a few seconds, whereas the calls that work go through instantly) in production. I'm having trouble understanding it as I would believe it would fail completely in QA (for either a network issue or something else) and not work in one place but not the next.
DB connectivity is verified (able to pull the user account/authenticate). All external DLLs are up to date (old files are burned each time a new build is pushed to avoid stagnation), verified connectivity on 443/80, other methods on the same controller are working, and everything works in QA.
[New Password Class]
[Serializable]
public class NewPassword
{
public int UserID { get; set; }
public string Password { get; set; }
public string Location { get; set; }
public string Username { get; set; }
public string Name { get; set; }
public string UserPassword { get; set; }
public int PasswordID { get; set; }
}
[Controller Code]
(Controller path is /API/)
[HttpPost] // Does not work with or without HttpPost attribute
[ValidateInput(false)] // disabled for testing, did not help
public JsonResult CSCreate(NewPassword Data)
{
}
[Client Code]
internal static Status CreatePassword(UserAccount Account, Password Credential)
{
try
{
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://" + Methods.Session.Connection.ServerName + "/API/CSCreate");
Request.ContentType = "application/json";
Request.Method = "POST";
using (StreamWriter SW = new StreamWriter(Request.GetRequestStream()))
{
NewPassword Data = new NewPassword() { UserID = Account.ID, Password = Credential.Text, Username = Credential.Username, Location = Credential.Location, UserPassword = Account.Password, Name = Credential.Name };
string JSON = new JavaScriptSerializer().Serialize(Data);
SW.Write(JSON);
}
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
using (StreamReader SR = new StreamReader(Response.GetResponseStream()))
{
string Data = SR.ReadToEnd();
Status EndData = (Status)new JavaScriptSerializer().Deserialize(Data, typeof(Status));
if (EndData.ExitCode != 200) return new Status() { ExitCode = 400, ExitMessage = EndData.ExitMessage };
else return new Status() { ExitCode = 200, ExitMessage = EndData.ExitMessage };
}
}
catch (Exception Ex)
{
return new Status() { ExitCode = 500, ExitMessage = "Unable to update your account. An error occurred. Please try again later." };
}
}
Interestingly enough, the (500) response is mentions a GET method even though "POST" is specified in the request information. Attempting a PUT or GET method errors out (GET for obvious reasons) with a not allowed (which makes sense given the attribute). I'm completely stumped on this one.
I'm working on my own ChatBot for YouTube in Winforms and C#. It already works on Twitch and I'm trying to replicate the functionality for Youtube using the C# API. I can download the Chat Messages no problem, but creating a chat message is causing me a headache as I'm getting a 403, Insufficient Permission error. The full error message is
Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [
Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]
]
After some searching I've tried most things that I can find and have still come up empty as to what exactly is causing this. I get it's a permission issue and I obviously need to set something but I can't figure out what. My code is below and definitely works for reading data... but i don't know why it won't work for writing.
public class YouTubeDataWrapper
{
private YouTubeService youTubeService;
private string liveChatId;
private bool updatingChat;
private int prevResultCount;
public List<YouTubeMessage> Messages { get; private set; }
public bool Connected { get; private set; }
public bool ChatUpdated { get; set; }
//public Authorisation Authorisation { get; set; }
//public AccessToken AccessToken { get; set; }
public YouTubeDataWrapper()
{
this.Messages = new List<YouTubeMessage>();
}
public async void Connect()
{
Stream stream = new FileStream("client_secrets.json", FileMode.Open);
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, new[] { YouTubeService.Scope.YoutubeForceSsl }, "user", CancellationToken.None, new FileDataStore(this.GetType().ToString()));
stream.Close();
stream.Dispose();
this.youTubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var res = this.youTubeService.LiveBroadcasts.List("id,snippet,contentDetails,status");
res.BroadcastType = LiveBroadcastsResource.ListRequest.BroadcastTypeEnum.Persistent;
res.Mine = true;
//res.BroadcastStatus = LiveBroadcastsResource.ListRequest.BroadcastStatusEnum.Active;
var resListResponse = await res.ExecuteAsync();
IEnumerator<LiveBroadcast> ie = resListResponse.Items.GetEnumerator();
while (ie.MoveNext() && string.IsNullOrEmpty(this.liveChatId))
{
LiveBroadcast livebroadcast = ie.Current;
string id = livebroadcast.Snippet.LiveChatId;
if (!string.IsNullOrEmpty(id))
{
this.liveChatId = id;
this.Connected = true;
}
bool? def = livebroadcast.Snippet.IsDefaultBroadcast;
string title = livebroadcast.Snippet.Title;
LiveBroadcastStatus status = livebroadcast.Status;
}
}
public async void UpdateChat()
{
if (!this.updatingChat)
{
if (!string.IsNullOrEmpty(this.liveChatId) && this.Connected)
{
this.updatingChat = true;
var livechat = this.youTubeService.LiveChatMessages.List(this.liveChatId, "id,snippet,authorDetails");
var livechatResponse = await livechat.ExecuteAsync();
PageInfo pageInfo = livechatResponse.PageInfo;
this.ChatUpdated = false;
if (pageInfo.TotalResults.HasValue)
{
if (!this.prevResultCount.Equals(pageInfo.TotalResults.Value))
{
this.prevResultCount = pageInfo.TotalResults.Value;
this.ChatUpdated = true;
}
}
if (this.ChatUpdated)
{
this.Messages = new List<YouTubeMessage>();
foreach (var livemessage in livechatResponse.Items)
{
string id = livemessage.Id;
string displayName = livemessage.AuthorDetails.DisplayName;
string message = livemessage.Snippet.DisplayMessage;
YouTubeMessage msg = new YouTubeMessage(id, displayName, message);
string line = string.Format("{0}: {1}", displayName, message);
if (!this.Messages.Contains(msg))
{
this.Messages.Add(msg);
}
}
}
this.updatingChat = false;
}
}
}
public async void SendMessage(string message)
{
LiveChatMessage liveMessage = new LiveChatMessage();
liveMessage.Snippet = new LiveChatMessageSnippet() { LiveChatId = this.liveChatId, Type = "textMessageEvent", TextMessageDetails = new LiveChatTextMessageDetails() { MessageText = message } };
var insert = this.youTubeService.LiveChatMessages.Insert(liveMessage, "snippet");
var response = await insert.ExecuteAsync();
if (response != null)
{
}
}
}
The main code in question is the Send Message method. I've tried changing the Scope of the UserCredentials to everything I can try to no avail. Any ideas?
From the YouTube Data API - Error, your error 403 or insufficientPermissions is error in the OAuth 2.0 token provided for the request specifies scopes that are insufficient for accessing the requested data.
So make sure you use proper Scope in your application. Here is the example of scope that is needed in your application.
https://www.googleapis.com/auth/youtube.force-ssl
https://www.googleapis.com/auth/youtube
For more information about this error 403, you can check this related SO question.
Turns out that revoking the access and then re-doing it fixed the issue. The error message was not very helpful.
I am getting data from a web api by making httpclient calls from various MVC controllers. Because I have to do it many times, I made a generic method that I can reuse by just passing in the api url and the model return type. It works fine, but I am concerned I am loosing the oppurtunity to have different methods, like GetPeople, GetPersonById, etc. Is there a downside to what I am doing?
Utilities.cs:
public static T GetDataFromWebService<T>(T model, string svcEndPoint)
{
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(svcEndPoint).Result;
var result = response.Content.ReadAsAsync<T>().Result;
return result;
}
Controller:
string svc = appSettings.GetPeopleApiUrl;
var model = new List<Person>();
var people = Utilities.GetDataFromWebService <IEnumerable<Person>>(model, svc);
You can still have specialized methods such as GetPeople, GetPersonById by layering them on top:
PeopleModel GetPeople(...) {
return GetDataFromWebService<PeopleModel>(...);
}
No downsides, it is good that you have all boilerplate code in a shared utility method.
Well, there is definitely, better way of doing the overall implementation, but if I have to stick to the question, I would say any attempt of reducing coupling is a good step for future directions. In your situation, since you are abstracting away the responsibility of making service calls to a utility method, it would help you in the long run.
Though I would suggest that instead of having this stuffed together in Utility class you should make the connectivity it's own class, something like this
public delegate T ParseToObject<T>(string response);
public class ServiceConnector : IServiceConnector
{
public string LogoffUrl { get; set; }
public bool SupportRetry { get; set; }
private WebClient _client;
public ServiceConnector()
{
}
public T GetResponse<T>(string requestUrl, ParseToObject<T> parsingMethod)
{
string response = __getResponse(requestUrl);
return parsingMethod(response);
}
private string __getResponse(string requestUrl)
{
string serviceResponse = string.Empty;
try
{
__initializeWebClient();
Logger.Current.LogInfo(string.Format("Sending request with URL {0}", requestUrl));
serviceResponse = _client.DownloadString(requestUrl);
}
catch (Exception ex)
{
if (ex.Message != null)
{
Logger.Current.LogException(string.Format("Exception during OvidWS request {0} ", requestUrl), ex);
_client = null;
}
//Sample implementation only, you could throw the exception up based on your domain needs
}
return serviceResponse;
}
private void __initializeWebClient()
{
if (_client == null)
_client = new WebClient();
}
}
With this in place, tomorrow, let's say you want to add support to log off, support cookies, support credentials, support retries, this is the only place where you can be and comfortably make changes. Similarly if you want to use Webclient over something else, you can also do that better here.
Try with that helper:
public static class WebClientExtension
{
public static T DownloadSerializedJsonData<T>(string url) where T : new()
{
var contentType = ConfigurationManager.AppSettings["ContentType"];//content type in app config or web config
using (var webClient = new WebClient())
{
webClient.Headers.Add("Content-Type", contentType);
var jsonData = string.Empty;
try
{
jsonData = webClient.DownloadString(url);
}
catch (Exception ex)
{
throw ex;
}
return !string.IsNullOrEmpty(jsonData) ? JsonConvert.DeserializeObject<T>(jsonData) : new T();
}
}
public static T AuthorizationContentSerializedJsonData<T>(string url) where T : new()
{
string jsonData = null;
try
{
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
//ClientBase.AuthorizeRequest(httpRequest, Authorization.AccessToken);
var response = httpRequest.GetResponse();
Stream receiveStream = response.GetResponseStream();
var readStream = new StreamReader(receiveStream, Encoding.UTF8);
jsonData = readStream.ReadToEnd();
response.Close();
}
catch (Exception ex)
{
throw ex;
}
return !string.IsNullOrEmpty(jsonData) ? JsonConvert.DeserializeObject<T>(jsonData) : new T();
}
}
App confing / Web config example for content type
<add key="ContentType" value="application/hal+json; charset=UTF-8" />
I have the following Web Api
public class ApiTestController : ApiController
{
// GET api/<controller>
[HttpGet]
public string UploadImage(int id)
{
return "You entered = " + id;
}
}
which when run and I enter /api/ApiTest/3 it hits it and returns You entered 3
Now in my seperate MVC application I'm trying to reference the same api method by doing the following
private const string WebUrl = "http://localhost:1769/api/ApiTest/";
//
// GET: /Home/
public ActionResult Index()
{
try
{
var test = GetInvoiveNo(3);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return View();
}
public string GetInvoiveNo(int id)
{
var uri = WebUrl + id;
using (var httpClient = new HttpClient())
{
Task response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<string>(response.ToString()).Result;
}
}
But I get the error:
one or more errors occurred
so I take a look into the inner exception and this is what it says:
Unexpected character encountered while parsing value: S. Path '', line 0, position 0."}
Now I'm not sure what I've done wrong here so if someone can kindly tell me or give me a simple example I'd appreciate it.
Your problem is that your response is not in proper format.
In below code you try to user response.ToString() which is wrong and you have to user result of response.
public string GetInvoiveNo(int id)
{
var uri = WebUrl + id;
using (var httpClient = new HttpClient())
{
var response = httpClient.GetStringAsync(uri);
return JsonConvert.DeserializeObjectAsync<string>(response.Result).Result;
}
}