I'm trying to use JSON over HTTP to communicate with the Twilio API (I'm running on asp.net vNext - and their C# library doesn't yet support coreclr)
I've got the following code, but every time I call the API I get the same 400 - Bad request response:
{"code": 21603, "message": "A 'From' phone number is required.", "more_info": "https://www.twilio.com/docs/errors/21603", "status": 400}
I'm using the following code to setup the request and make the call - the auth succeeds and I get the above error:
var byteArray = Encoding.ASCII.GetBytes("{AccountSid}:{AuthToken}");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
var jsonStr = JsonConvert.SerializeObject(new {
From = WebUtility.UrlEncode("+15005550003"), // A test number twilio supply
To = WebUtility.UrlEncode("{my phone number}"),
Body = WebUtility.UrlEncode("Hi there from a pure json rest client...")
});
var response = await client.PostAsync("https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/SMS/Messages.json", new StringContent(jsonStr));
After contacting support and dealing with a very helpful guy called Joshua, we have figured it out.
In Twilio docs you can either use a url with "messages.json" or "messages.xml" - I took that to mean: whichever url you use, the message (request and response) will have to follow that format.
Unfortunately that is not true - all requests to Twilio, must be of Content-Type: application/x-www-form-urlencoded
e.g. To=0123456789&From=9876543210&Body=Hi
in C# (HttpClient) that looks like:
_client.PostAsync(_url, new FormUrlEncodedContent(new Dictionary<string, string>(){
{ "To", "0123456789" },
{ "From", "9876543210" },
{ "Body", "Hi" } }));
I'm Megan from the Developer Community team at Twilio.
It looks like the number you are using, "+15005550003" is one of Twilio's magic input numbers that actually represents the "To" field, thus the error you are receiving. Depending on what you want to test, you might try passing in "+15005550006" instead.
You can see more about what each magic input represents in the docs here.
Hope this helps!
If using an SMS / Messaging Service which allows you to send from multiple phone numbers and you get this error,
then replace
{ "From","<number>" }
with
{ "MessagingServiceSid","<sid>" }
the SID can be found on the messaging services page
https://www.twilio.com/console/sms/services
Related
I am living in Denmark where we have the ability to pull all data regarding our power usage from the website Eloverblik.dk.
They have even provided a nice API, so you can make your own programs that pulls the data from their website:
https://api.eloverblik.dk/CustomerApi/index.html
However:
Assume you have the following C# prof of concept code:
using RestSharp;
using RestSharp.Authenticators;
var token = "eyJ...";
var client = new RestClient("https://api.eloverblik.dk")
{
Authenticator = new JwtAuthenticator(token)
};
/* Get power usage from June 1st 2022 until September 1st 2022 */
var request = new RestRequest("api/meterdata/getmeterreadings/2022-06-01/2022-09-01");
var response = await client.GetAsync(request);
Console.WriteLine(response.Content);
When I run the code I get an error code "BadRequest" when I try to call await client.GetAsync.
What am I doing wrong?
They write in the API that the token is formattet as Bearer {token}, but I thought that JwtAuthenticator did that for me?
It seems that you call API incorrectly.
Check again documentation on API at https://api.eloverblik.dk/CustomerApi/index.html.
getMeterReadings is a POST method, not a GET (so, you need to call client.PostAsync and not client.GetAsync)
getMeterReadings expects also JSON-body to be sent (with list of metering points).
Also, I would recommend you to test your requests using some HTTP-request tool (i.e. Postman) and when you're pretty sure that request works move it into C#.
I am getting to the stage of hair pulling with this one, I'm hoping someone can see if I'm doing anything wrong.
I'm trying to POST some form data to website using Azure data factory web activity however whilst I get a response (I get the page and some headers) it is different to the response I get if I make the exact same request using C# and HttpClient code. I've used fiddler to view the request being post'd using my C# script and according to the request information given in data factory they are exactly the same - so same headers, same content format etc...
This POST request is to login to a website which has a custom login mechanism, so no OAuth or anything like that unfortunately. It is supposed to return a cookie, which it does if I use my C# script, but if I make the same POST request using data factory web activity then I get different html sent back (it just returns the same login screen) and also different set of response headers in the "ADFWebActivityResponseHeaders" part of the activity output!?! See below for what is returned in the web activity output response headers:-
"ADFWebActivityResponseHeaders": {
"Pragma": "no-cache",
"Vary": "Accept-Encoding",
"X-Frame-Options": "DENY",
"Cache-Control": "no-store, must-revalidate, no-cache, post-check=0, pre-check=0",
"Date": "Wed, 09 Sep 2020 08:09:30 GMT",
"Server": "Microsoft-IIS/8.5"
}
If I do this via C# I also get a 'Set-Cookie' as well (strangely if I make a 'GET' request for the homepage of this site I do get a 'Set-Cookie' in the response!!!), but never when doing this via data factory. I'm struggling to see how this is possible unless data factory is modifying my request in some fashion? Below is my C# code, pretty simple/standard:-
var handler = new HttpClientHandler();
handler.CookieContainer = new CookieContainer();
handler.UseCookies = true;
handler.UseDefaultCredentials = false;
// Create our http client which will perform our web requests
var HttpClient = new HttpClient(handler);
HttpClient.BaseAddress = new Uri("**REMOVED**");
// Some of the extracts take a LONG time, so set the timeout for default of 30mins
HttpClient.Timeout = TimeSpan.FromMinutes(30);
// Set the 'form' parameters we're going to POST to the server in the request
var parameters = new Dictionary<string, string>
{
{ "username", "**REMOVED**" },
{ "password", "**REMOVED**" }
};
// URL encode the parameters
var content = new FormUrlEncodedContent(parameters);
// Submit our POST with the parameters
var response = await HttpClient.PostAsync("**REMOVED**", content);
Running this code and using fiddler I see the following request with headers, these are the only headers:-
Content-Length: 80
Content-Type: application/x-www-form-urlencoded
username=REMOVED&password=REMOVED
and in the 'input' side of the web activity is the details of the request, I've added the headers in the web activity and these are correct:-
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": 80
},
"body": "username=REMOVED&password=REMOVED"
Note that in the data factory I'm using a self hosted integration runtime as this website blocks addresses that do not come from the specific IP addresses used externally by our on-prem network/firewall. I know that is not the problem as I'm getting a response with the normal login page from the site (if I use the Azure integration runtime I get a denied response).
Here is a screen shot of the web activity in data factory:-
Really hope someone out there can see what I'm missing or whatever...
Turns out this does work and will list the cookies in the JSON output from the activity as shown below (note this is to be found in the output of the ADF activity, so you would pick up the cookie from the output a bit like... #activity('Login and get cookie').output.ADFWebActivityResponseHeaders["Set-Cookie"] )
However, in my case the url I was POSTing to was responding with a 302 (moved temporarily) but the 'Location' header which should be there is not in the ADFWebActivityResponseHeaders - which is why I missed it. I tried using Chrome with the developer tools and looked at the response directly which is where I found the 302 response code. After that, I just used the new URL given in the response headers (i.e. the url in the 'Location') that I found when using the browser dev tools.
Unfortunately at the time of writing, the Azure data factory HTTP activity does not follow redirects (and doesn't list all the response headers either!) so if anyone encounters the same problem they will need to manually find out and get the url's for any redirects. In other words, try using a tool like browser/postman and look at the response if it doesn't work in ADF... you might find there is a redirect going on :-)
There is a feature request logged for this here, be sure to add your vote :)
edited to update the Azure feedback change of URL after MS decided to change things on the feedback site!?!
I'm making an api call to UPS using their Address Validation call(https://onlinetools.ups.com/rest/AV). I have had success calling different Gets from UPS before(example: UPS Address Streen Level Validation).
Here is the json that is being sent:
{
"AccessRequest": {
"AccessLicenseNumber": "removed for security reasons",
"UserId": "removed for security reasons",
"Password": "removed for security reasons"
},
"AddressValidationRequest": {
"Request": {
"TransactionReference": {
"CustomerContext": ""
},
"RequestAction": "AV"
},
"Address": {
"City": "",
"StateProvinceCode": "",
"PostalCode": "98272"
}
}
}
In PostMan, there are only the default Headers being used:
This returns successfully with the data I am looking for with code: 200.
In my .Net Standard 2.0 Library, I am using HTTPClient(Microsoft.AspNet.WebApi.Client 5.2.7) to make a call to the api. Here is how I initiate the HTTP Client(variable called "ApiClient"):
string api = _config["UPS_API_LINK"];
_apiClient = new HttpClient();
_apiClient.BaseAddress = new Uri(api);
_apiClient.DefaultRequestHeaders.Accept.Clear();
_apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Now I have created the models to line up so it sends the exact same Json as Postman does. You will see with the code below that I double check that by serializing it myself(I take that string, copy it into postman, and works like a charm). So I send my request. I shortened the code a little bit make my point:
string json = JsonConvert.SerializeObject(request);
Debug.Print(json);
using (HttpResponseMessage response = await ApiClient.PostAsJsonAsync("/rest/AV", request))
{
var content = await response.Content.ReadAsStringAsync();
}
Here is what the content returns:
<HTML><HEAD><TITLE>Weblogic Bridge Message</TITLE></HEAD> <BODY><H2>Failure of Web Server bridge:</H2><P><hr>Your chunked POST data is too large to upload.<hr> </BODY></HTML>
The return code is 503. So can someone lead me into the right direction? I just don't know what HTTP Client is sending that is making it so large that I get this response. Whats surprising to me is that I have another call to the api that sends more data and it works perfectly. Any suggestions would be appreciated.
Note: I have double checked that I am requesting the call using the right URL
Update: Thanks to Nehorai, I discovered that I can use third party dll called RestSharp for making Api calls. This library worked flawlessly! Though I would love to still know why HTTP Client does not work in this situation. Here is my code below using RestSharp incase anyone wanted to see it:
var client = new RestClient("https://onlinetools.ups.com/rest/AV");
client.Timeout = -1;
var requestRest = new RestRequest(Method.POST);
requestRest.AddHeader("Content-Type", "application/json");
requestRest.AddJsonBody(request);
var result = await client.ExecutePostAsync<ZipInfo_RootResponse>(requestRest);
In postman, you can view a code snippet of the request, click on "Code":
Then you can select the language, select C# and you can see the request in C# code (you will need to add a reference to RestSharp dll)
I have my telegram application with app's api_id and app's api_hash.
I used TLSharp library for implementing my own things. But now I need to use this https://core.telegram.org/method/auth.checkPhone telegram api method, but it's not implemented in TLSharp library!
I don't mind doing it all manually, but I don't know how!
I know how you send post requests in C#, example:
var response = await client.PostAsync("http://www.example.com/index", content);
but in this specific case I don't. Because I don't know:
1) what link should I use for sending post requests? I couldn't find it on the telegram's website.
2) what content should I pass there? Should it be just "(auth.checkPhone "+380666454343")" or maybe the whole "(auth.checkPhone "+380666454343")=(auth.checkedPhonephone_registered:(boolFalse)phone_invited:(boolFalse))" ?
So, How do I sent this post request to the telegram api? (NOT telegram bot api!)
Try to use System.Net.Http like in this example (auth request to the server):
var user = new { login = "your login", password = "your pass" };
string json = JsonConvert.SerializeObject(user);
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri("server route link"); // can be like https://a100.technovik.ru:1000/api/auth/authenticate
request.Method = HttpMethod.Post;
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);
responseText.Text = await response.Content.ReadAsStringAsync();
I think based on a brief look, that it would be more along the lines of your second example, e.g.:
var phonenumber = "0123456789";
var content =
$#"(auth.checkPhone ""{phonenumber}"")"+
"=(auth.checkedPhone phone_registered: (boolFalse) phone_invited:(boolFalse))";
var result = DoHttpPost("http://some.example.com/api/etc", content);
(note: I've not listed the actual mechanics of an HTTP Request here, as that is covered in plenty of detail elsewhere - not least in the other current answer supplied to you; DoHttpPost() is not a real method and exists here only as a placeholder for this process)
And since the payload of this appears to indicate the exact function and parameters required, that you'd just send it to the base api endpoint you use for everything, but I can't say for sure...
I do note they do appear to have links to source code for various apps on the site though, so perhaps you'd be better off looking there?
Awhile ago I implemented some code to consume a REST Api using the HttpClient class.
using (var client = new HttpClient() { BaseAddress = new Uri(#"https://thirdparty.com") })
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...);
var uri = new Uri(#"rest/api/foo", UriKind.Relative);
var content = new StringContent(json.ToString());
using (var response = await client.PostAsync(uri, content))
{
// etc ...
}
}
This code seemed to work perfectly fine against both the test and production environments (each of which access a test/production uri). Recently, we started to get an HttpRequestException in the production environment only: System.Net.Http.HttpRequestException: Error while copying content to a stream.
This seemed a bit strange, so I used Postman to send the same message and it worked just fine. I wasn't sure why our code was failing and Postman was working. I changed a parameter in the json data (the state from "NY" to "NV") and our .NET code worked fine -- of course we can't just send the wrong json data, so this isn't a solution; this was more of an observation that the exact same code worked fine with different content.
What's interesting is that there are two code changes we could make that will resolve this. Firstly, Postman is able to generate working C# code using the RestSharp package. Alternatively, I found an answer from another question pointing to using HttpVersion 1.0:
using (var request = new HttpRequestMessage(HttpMethod.Post, uri))
{
request.Version = HttpVersion.Version10;
request.Content = new StringContent(json.ToString());
using (var response = await client.SendAsync(request))
{
// etc ...
}
}
The confusing part is that Postman uses the HTTP/1.1 version. So, in summary:
If we change the json data (US State from "NY" to "NV"), the code works.
The same exact json and code works against the test uri.
Changing the code to use the RestSharp package works.
Changing the code to use HTTP/1.0 instead of HTTP/1.1 works.
Why on earth is Postman able to work using HTTP/1.1 but HttpClient fails? Is this a problem with our client (the code works for other US States)? Is this a bug in the .NET Framework? Is there something wrong with the implementation/hosting of the REST Api from the third party?
Postman headers:
POST rest/api/foo HTTP/1.1
Host: thirdparty.com
Content-Type: application/json
Authorization: Basic SOME_ENCRYPTED_USER_PASS
Cache-Control: no-cache
Postman-Token: 2fa5b5a0-b5d3-bd4c-40f0-d2b55b60316b
Sample Json:
{
"stateCode": "NY",
"packageID": "58330",
"name": "58330-PRI-1",
"documents": [
{
"type": "SPECIAL",
"name": "Sample Document",
"documentID": "3569976"
}
],
"descriptions": [
{
"city": "New York",
"state": "NY"
}
]
}
Stacktrace:
AggregateException: One or more errors occured.
HttpRequestException: Error while copying content to a stream.
IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
SocketException: An existing connection was forcibly closed by the remote host.
Due to the fact that you can change the data slightly and have it succeed, I would say your issues has nothing to do with any of your code. What if their server has cached a bad value and will continue sending you that value until their cache clears?
Try implicitly telling the server not to use cached values...
using (var client = new HttpClient() { BaseAddress = new Uri(#"https://thirdparty.com") })
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...);
var uri = new Uri(#"rest/api/foo", UriKind.Relative);
var content = new StringContent(json.ToString());
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache");
using (var response = await client.PostAsync(uri, content))
{
// etc ...
}
}
and see if you get a valid response stream.
Have you got Antivirus/Firewall on the machine running the HttpClient? I the past I have had problems with AVG, Mcafee, Norton and others silently blocking requests. Its quite tricky to find out exactly where, but there is may be a tab where ports are monitored, unticking / disabling this might help identify the problem. IF thats the case, the proper solution is to get your "thirdparty.com" on a white list of the appropriate vendor/s.
It may be worth looking at your response headers from your server as well? Could you add them to your question?Only because, in the past I have found Content-Security-Policy headers preventing some of my requests from completing?