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.
Related
I am responding to a GET request from a field device with the following:
var reply = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent("SUCCESS")
};
The word appears in the message of the body. But the field device is saying that it is not successful.
From this, I can gather that I shouldn't be using HttpResponseMessage but some other means.
The suggested reply needs to look like this:
HTTP/1.1 200 OK<CR><LF>
Date: Mon, 15 Feb 2016 11:34:50 GMT<CR><LF>
Server: Apache/2.2.31 (Win32) mod_ssl/2.2.31 OpenSSL/1.0.2f PHP/5.4.45<CR><LF>
X-Powered-By: PHP/5.4.45<CR><LF>
Content-Length: 7<CR><LF>
Keep-Alive: timeout=5, max=100<CR><LF>
Connection: Keep-Alive<CR><LF>
Content-Type: text/plain<CR><LF>
<CR><LF>
SUCCESS<CR><LF>
BTW I am using Microsoft for this not PHP, this is from the manual.
Should I be using HttpRequestMessage ?
The part I am concerned with is <CR><LF>SUCCESS<CR><LF>
So the issue was that the device required a time sync with my server before it could send data later on. So there never was an issue with the response.
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;
}
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¤cy=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.
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");
}
I'm working on a small C#/WPF application that interfaces with a web service implemented in Ruby on Rails, using handcrafted HttpWebRequest calls and JSON serialization. Without caching, everything works as it's supposed to, and I've got HTTP authentication and compression working as well.
Once I enable caching, by setting request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable);, things go awry - in the production environment. When connecting to a simple WEBrick instance, things work fine, I get HTTP/1.1 304 Not Modified as expected and HttpWebRequest delivers the cached content.
When I try the same against the production server, running nginx/0.8.53 + Phusion Passenger 3.0.0, the application breaks. First request (uncached) is served properly, but on the second request which results in the 304 response, I get a WebException stating that "The request was aborted: The request was canceled." as soon as I invoke request.GetResponse().
I've run the connections through fiddler, which hasn't helped a whole lot; both WEBrick and nginx return an empty entity body, albeit different response headers. Intercepting the request and changing the response headers for nginx to match those of WEBrick didn't change anything, leading me to think that it could be a keep-alive issue; setting request.KeepAlive = false; changes nothing, though - it doesn't break stuff when connecting to WEBrick, and it doesn't fix stuff when connecting to nginx.
For what it's worth, the WebException.InnerException is a NullReferenceException with the following StackTrace:
at System.Net.HttpWebRequest.CheckCacheUpdateOnResponse()
at System.Net.HttpWebRequest.CheckResubmitForCache(Exception& e)
at System.Net.HttpWebRequest.DoSubmitRequestProcessing(Exception& exception)
at System.Net.HttpWebRequest.ProcessResponse()
at System.Net.HttpWebRequest.SetResponse(CoreResponseData coreResponseData)
Headers for the (working) WEBrick connection:
########## request
GET /users/current.json HTTP/1.1
Authorization: Basic *REDACTED*
Content-Type: application/json
Accept: application/json
Accept-Charset: utf-8
Host: testbox.local:3030
If-None-Match: "84a49062768e4ca619b1c081736da20f"
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
########## response
HTTP/1.1 304 Not Modified
X-Ua-Compatible: IE=Edge
Etag: "84a49062768e4ca619b1c081736da20f"
Date: Wed, 01 Dec 2010 18:18:59 GMT
Server: WEBrick/1.3.1 (Ruby/1.8.7/2010-08-16)
X-Runtime: 0.177545
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: *REDACTED*
Headers for the (exception-throwing) nginx connection:
########## request
GET /users/current.json HTTP/1.1
Authorization: Basic *REDACTED*
Content-Type: application/json
Accept: application/json
Accept-Charset: utf-8
Host: testsystem.local:8080
If-None-Match: "a64560553465e0270cc0a23cc4c33f9f"
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
########## response
HTTP/1.1 304 Not Modified
Connection: keep-alive
Status: 304
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.0
ETag: "a64560553465e0270cc0a23cc4c33f9f"
X-UA-Compatible: IE=Edge,chrome=1
X-Runtime: 0.240160
Set-Cookie: *REDACTED*
Cache-Control: max-age=0, private, must-revalidate
Server: nginx/0.8.53 + Phusion Passenger 3.0.0 (mod_rails/mod_rack)
UPDATE:
I tried doing a quick-and-dirty manual ETag cache, but turns out that's a no-go: I get a WebException when invoking request.GetResponce(), telling me that "The remote server returned an error: (304) Not Modified." - yeah, .NET, I kinda knew that, and I'd like to (attempt to) handle it myself, grr.
UPDATE 2:
Getting closer to the root of the problem. The showstopper seems to be a difference in the response headers for the initial request. WEBrick includes a Date: Wed, 01 Dec 2010 21:30:01 GMT header, which isn't present in the nginx reply. There's other differences as well, but intercepting the initial nginx reply with fiddler and adding a Date header, the subsequent HttpWebRequests are able to process the (unmodified) nginx 304 replies.
Going to try to look for a workaround, as well as getting nginx to add the Date header.
UPDATE 3:
It seems that the serverside issue is with Phusion Passenger, they have an open issue about lack of the Date header. I'd still say that HttpWebRequest's behavior is... suboptimal.
UPDATE 4:
Added a Microsoft Connect ticket for the bug.
I think the designers find it reasonable to throw an exception when the "expected behavior"---i.e., getting a response body---cannot be completed. You can handle this somewhat intelligently as follows:
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var statusCode = ((HttpWebResponse)ex.Response).StatusCode;
// Test against HttpStatusCode enumeration.
}
else
{
// Do something else, e.g. throw;
}
}
So, it turns out to be Phusion Passenger (or nginx, depending on how you look at it - and Thin as well) that doesn't add a Date HTTP response header, combined with what I see as a bug in .NET HttpWebRequest (in my situation there's no If-Modified-Since, thus Date shouldn't be necessary) leading to the problem.
The workaround for this particular case was to edit our Rails ApplicationController:
class ApplicationController < ActionController::Base
# ...other stuff here
before_filter :add_date_header
# bugfix for .NET HttpWebRequst 304-handling bug and various
# webservers' lazyness in not adding the Date: response header.
def add_date_header
response.headers['Date'] = Time.now.to_s
end
end
UPDATE:
Turns out it's a bit more complex than "just" setting HttpRequestCachePolicy - to repro, I also need to have manually constructed HTTP Basic Auth. So the involved components are the following:
HTTP server that doesn't include a HTTP "Date:" response header.
manual construction of HTTP Authorization request header.
use of HttpRequestCachePolicy.
Smallest repro I've been able to come up with:
namespace Repro
{
using System;
using System.IO;
using System.Net;
using System.Net.Cache;
using System.Text;
class ReproProg
{
const string requestUrl = "http://drivelog.miracle.local:3030/users/current.json";
// Manual construction of HTTP basic auth so we don't get an unnecessary server
// roundtrip telling us to auth, which is what we get if we simply use
// HttpWebRequest.Credentials.
private static void SetAuthorization(HttpWebRequest request, string _username, string _password)
{
string userAndPass = string.Format("{0}:{1}", _username, _password);
byte[] authBytes = Encoding.UTF8.GetBytes(userAndPass.ToCharArray());
request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(authBytes);
}
static public void DoRequest()
{
var request = (HttpWebRequest) WebRequest.Create(requestUrl);
request.Method = "GET";
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable);
SetAuthorization(request, "user#domain.com", "12345678");
using(var response = request.GetResponse())
using(var stream = response.GetResponseStream())
using(var reader = new StreamReader(stream))
{
string reply = reader.ReadToEnd();
Console.WriteLine("########## Server reply: {0}", reply);
}
}
static public void Main(string[] args)
{
DoRequest(); // works
DoRequest(); // explodes
}
}
}