403 when calling AirBnb api from C# - c#

I've not used HttpClient before so apologies if it's an obvious one.
I'm poking about with the airbnb api http://airbnbapi.org/#view-listing-info
My understanding of the endpoint is that I don't need an auth token, as this is a public endpoint I'm trying to use. Unfortunately I'm getting a 403 no matter what I try to do and I'm not entirely sure why.
I've got the following code:
client.DefaultRequestHeaders.Add("client_id", "<My client Id>");
client.DefaultRequestHeaders.Add("locale", "en-gb");
client.DefaultRequestHeaders.Add("currency", "gbp");
var request = new HttpRequestMessage()
{
RequestUri = new Uri($"https://api.airbnb.com/v2/listings/{id}"),
Method = HttpMethod.Get,
};
var task = client.SendAsync(request)
.ContinueWith((taskwithmsg) =>
{
var response = taskwithmsg.Result;
//var jsonTask = response.Content.ReadAsAsync<JsonResult>();
//jsonTask.Wait();
//var jsonObject = jsonTask.Result;
return response.Content;
});
task.Wait();
return task.Result;
And I'm getting the following response:
- response {StatusCode: 403, ReasonPhrase: 'Forbidden', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Date: Tue, 04 Apr 2017 19:44:40 GMT
Server: AkamaiGHost
Mime-Version: 1.0
Content-Length: 291
Content-Type: text/html
Expires: Tue, 04 Apr 2017 19:44:40 GMT
}} System.Net.Http.HttpResponseMessage
Any advice?
EDIT:
Macceturra wisely suggested I try to make the call with postman.
I've now established that I can make a call in postman and get a correct response back.

The request you're sending has the client_id as a HTTP header, when Airbnb is expecting it as a URL parameter.
Additionally, Airbnb requires the client to send an Accept (or User-Agent) header, or else it will still return "403 Forbidden" (probably should be "400 Bad Request").
Putting that together (and deleting the unnecessary headers):
var id = ...;
var clientId = ...;
var uri = new Uri($"https://api.airbnb.com/v2/listings/{id}?client_id={Uri.EscapeDataString(clientId)}&locale=en-gb&currency=GBP");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
await client.GetAsync(uri);

I've not been able to mark a single answer as the solution as it came through in the comments.
Solution
Start by making calls in postman. This helped me realise that I should have been passing the client id as a url parameter and not as a header. Bradley Grainger went on to answer this too.
Try and make that exact call with the HttpClient. This still returned the 403, but now we have confidence in the url we're sending.
Change the content type to application/json. This beats the 403 and gets us a 200 with the requested data.
Big thanks to all who helped piece these steps together.

You may need to add a cookie to the request called _aat which contains the airbnb access token. This is generated during login. You'll be able to see it by looking at the headers in Fiddler or the Net section of the browser dev tools when browsing one of your own listings when logged in. Just copy and paste it from there into your code to test if you can get the request working properly and then later you can automate the process of getting the _aat cookie within your code. Make sure the http headers in your code also match what is being sent via the browser.

Related

Related to Get service call

I just created a HTTP get request to get the content(All the Badges) from stack overflow for my console application as shown below :
public void getStackBadges()
{
var client = new HttpClient();
client.BaseAddress = new Uri("https://api.stackexchange.com/docs//badges?order=desc&sort=rank&site=stackoverflow");
var res = client.GetAsync(client.BaseAddress).Result;
Console.WriteLine(res);
}
Can anybody please tell if i want to get all the badges from stack overflow using this API what i need to do. I don't realy understand the format of result that i am getting on my Cmd prmt !
Output on Console:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Credentials: false
X-Content-Type-Options: nosniff
Cache-Control: private
Date: Thu, 28 Dec 2017 09:49:33 GMT
Content-Length: 880
Content-Encoding: gzip
Content-Type: application/json; charset=utf-8
}
You're querying the documentation of the API and you're most likely getting the page markup.
You should be querying: https://api.stackexchange.com/2.2/badges?order=desc&sort=rank&site=stackoverflow
The format of the output in above API method is JSON.
Few things to note in the output:
quota_max - Tells you how many requests can be made by you in a day.
quota_remaining - How many requests more you can make today.
has_more - If there is another page with badges.
To change the page you append a &page= parameter to the url.
So your query for page 2 would look like this:
https://api.stackexchange.com/2.2/badges?page=2&order=desc&sort=rank&site=stackoverflow
Edit:
As I said, the API I have linked is correct, your problem is the way you try to display the content of the response from the API.
.Result is not what you think it is. .Result returns Task<TResult> which is not the response from the API but the result of the request. That's why you have status code of the request, response type, etc etc.
Here's how to retrieve the response text. This is a sample, you'll need to do your own processing if you want to access different properties of the response objects. This is a separate question though and it's outside the scope of this one.
var response = client.GetAsync("https://api.stackexchange.com/2.2/badges?order=desc&sort=rank&site=stackoverflow").Result;
string res = "";
using (HttpContent content = response.Content)
{
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
}

Twilio Error - 12300 - Invalid Content Type only sometimes

I have a C# / .NET WebApi endpoint tied to a number. When that number receives a text, it's forwarded to my API via webhook.
Sometimes (not all the time), I get an error in my debugger with the following:
Error - 12300
Invalid Content-Type
Twilio is unable to process the Content-Type of the provided URL.
Please see the Twilio Markup XML Documentation for more information on
valid Content-Types. You must return a Content-Type for all requests.
Requests without a Content-Type will appear in the App Monitor as a
502 Bad Gateway error.
In the response that triggered this, I see the following:
With the following headers:
Content-Type application/json; charset=utf-8
Pragma no-cache
Date Sat, 14 Jan 2017 02:57:45 GMT
X-AspNet-Version 4.0.30319
X-Powered-By ASP.NET
What might be causing this, and how do I address it?
After some research from TWIML MESSAGE: YOUR RESPONSE
this code seems to work
[HttpGet]
public HttpResponseMessage SmsAnswerCallBack(string id)
{
_smsAnswerCallBackCallIndex++;
var r = new SmsApiResult();
r.SetStatus(true, _smsSendCallIndex, _smsAnswerCallBackCallIndex);
r.DataList = _answers;
var res = Request.CreateResponse(HttpStatusCode.OK);
res.Content = new StringContent("<Response/>", Encoding.UTF8, "text/xml");
return res;
}
I too was sending a json response and getting this error. Using the answer by Frederic Torres got me on the right track. It looks like Twilio is looking for XML in TwiML format. But if you basically just return an empty "Response" element in text/xml format, that satisfies Twilio. So here is a simplified answer for anybody else that runs into this:
public ContentResult IncomingSMS(string To, string From, string Body)
{
//do stuff
//...
return Content("<Response/>", "text/xml");
}

How to get RestSharp to properly deflate compressed HTTP response?

I'm using RestSharp to make a call to REST service. My call looks something like this:
var request = new RestRequest("/foo", Method.POST);
request.JsonSerializer.ContentType = "application/json; charset=utf-8";
request.AddJsonBody(new string[] { "param1", "param2" });
var response = this._client.Execute<Foo>(request);
For most other calls this works fine. I'm running into issues when the response is compressed. The headers in the response look (mostly) like this:
HTTP/1.1 200 OK
Uncompressed-Size: 35000
Content-Length: 3019
Content-Encoding: deflate
Content-Type: application/json
The issue is when I call this method with RestSharp I keep getting the error:
Error: Block length does not match with its complement.
I've tried setting the Accept-Encoding header in the request but it still produces the error. I also tried using a custom deserializer but the error is occurring before deserialization. From what I can tell, RestSharp should automatically handle deflation if the Content-Encoding header says deflate (which it does).
How can I get RestSharp to handle the deflation properly?
UPDATE
In the end I was able to have the service changed to look for an Accept-Encoding header in the request with a value of identity. If found, the service was changed to return the data uncompressed.
This is unfortunately not really a solution to the original issue but it does resolve the problem for me. If a better solution is posted I will try it.
According to this post, you should be able to handle it if you won't pass charset=utf-8 in content type.
Please refer to this:
RestSharp compress request while making rest call to server

HttpClient is adding it's own cookie header

I have rather strange problem. With all that questions over internet how to add and get cookies, I want the opposite ;)
When I try to send request via HttpHandler it is adding it's own Cookie header. I have to get rid of it. Without digging into details - when it is added, server I am trying to request is giving me wrong answer. It works without this cookie (tried in fiddler).
But back to the problem, code:
string domain = "someMysteriousDomain";
var handler = new HttpClientHandler();
handler.UseDefaultCredentials = false;
handler.AllowAutoRedirect = true;
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.UseCookies = false;
var httpClient = new HttpClient(handler);
var request = new HttpRequestMessage(HttpMethod.Get, domain);
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("Mozilla", "5.0"));
request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
var response = await httpClient.SendAsync(request);
Raw request seen in fiddler:
GET https://domain HTTP/1.1
Accept-Encoding: gzip
User-Agent: Mozilla/5.0
Host: domain
Connection: Keep-Alive
Cookie: cadata477E7C1824F44800AF0077724F65345="51595d316-0286-44bb-bc6f-ffb1fd311a92SqJA36rA69YW7aBg+iHXYi9LAcBLN6DBWE8a3MLejd2VCluO/UQ5eF6F6T4NWh4NhdRcv4rea15Hs0e2q6GatMac59UVbljhREYdH6PRbzZC/2qn8QHtpc6go5B56R"; mobile=0
I don't want to add that cookie! How to delete/clear/whatever it?
I am using Visual Studio Community 2015, with Windows Universal Project.
What is interesting, after rebooting my pc after few hours, I was able to make 2 or 3 requests without this cookie (using THE SAME code) and then mysterios cookie returned.
What it is about? How to get rid of it?
Thank you for reporting this issue - this is a known issue with the System.Net.Http.HttpClientHandler API implementation on Windows 10 and we are working on fixing it in an upcoming release.
In the meanwhile, a possible workaround is to use the Windows.Web.Http.HttpClient API with the underlying HttpBaseProtocolFilter class. This class has a property called CookieManager that stores all the cookies for each URI. You can write a method to delete the cookies from the CookieManager for the destination URI before sending a request. This will ensure that no cookies get sent. You can see this sample for how to delete cookies from the CookieManager: https://github.com/Microsoft/Windows-universal-samples/tree/master/httpclient
Thanks
Sidharth [MSFT]

Obtaining a token for authentication

I asked how to set up a call to a service and got a great info on HttpClient. However, while that question's technically answered, I still get stuck.
In the console, I can see what request my browser send to the service to obtain the token for authorization. However, when I try to mimic the call building the request in my service layer, I get the following error message. The probability of me being at fault here is pretty steep. Not sure what to google for, really...
"StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:\u000d\u000a{\u000d\u000a Transfer-Encoding: chunked\u000d\u000a Connection: keep-alive\u000d\u000a Date: Wed, 12 Nov 2014 21:00:34 GMT\u000d\u000a Set-Cookie: lang=\"en\";Max-Age=31622400;expires=Fri, 13-Nov-2015 21:00:33 GMT;Path=/;Version=\"1\"\u000d\u000a Server: nginx/1.4.1\u000d\u000a Server: (Ubuntu)\u000d\u000a Content-Type: text/plain; charset=UTF-8\u000d\u000a}"
The call itself looks like this.
using (HttpClient client = new HttpClient())
{
Task<HttpResponseMessage> message
= client.PostAsync(urlToken, new StringContent(credentials));
message.Wait();
result = message.Result.ToString();
}
As was posted in the answer that you accepted in your linked post, you need to read the content of the response. Calling ToString() directly on the response is not showing you the actual error.
Change your code to something like:
using (HttpClient client = new HttpClient())
{
var response = await client.PostAsync(url, new StringContent(credentials));
result = await response.Content.ReadAsStringAsync();
}
Once you can see the actual response message from the server, you should be able to figure out what to do next.

Categories

Resources