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
Related
I am trying to create an attachment using the Support Bee API as documented here:
https://supportbee.com/api#create_attachment
I have written a service that uses an HttpClient to create and send the request using a filename.
If I test in in Postman, it succeeds. I am using form-data for the body and just selecting the file to upload from the UI:
It doesn't work when I try to upload it via my HttpClient Service:
public async Task<string> CreateAttachmentAsync(string fileName)
{
// "client" is HttpClient provided via D.I.
MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StreamContent(new FileStream(fileName, FileMode.Open)), "files[]");
using (HttpResponseMessage response = await client.PostAsync(
"https://xxx.supportbee.com/attachments?auth_token=xxx",
content))
{
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
This results in a 500 Internal Server Error. Inspecting the MultipartFormDataContent object I can see that it's header values are automatically being set:
{
Content-Type: multipart/form-data; boundary="c9be3778-4de5-4460-9929-adcaa6bdda79"
Content-Length: 164
}
I have also tried reading the file to a byte array first and using ByteArrayContent instead of StreamContent to no avail. The response doesn't provide anything helpful, but since the request works in Postman I must have something wrong with my code, but I don't know what else to try.
Edit: I tested with Fiddler to compare the successful Postman request to my code. Here is the request with Postman:
POST
https://xxx.supportbee.com/attachments?auth_token=xxx
HTTP/1.1 User-Agent: PostmanRuntime/7.22.0 Accept: / Cache-Control:
no-cache Postman-Token: f84d22fa-b4b1-4bf5-b183-916a786c6385 Host:
xx.supportbee.com Content-Type: multipart/form-data;
boundary=--------------------------714700821471353664787346
Accept-Encoding: gzip, deflate, br Content-Length: 241 Connection:
close
----------------------------714700821471353664787346 Content-Disposition: form-data; name="files[]"; filename="sample.txt"
Content-Type: text/plain
This contains example text.
----------------------------714700821471353664787346--
And the failing request from my code:
POST
https://xxx.supportbee.com/attachments?auth_token=xxx
HTTP/1.1 Host: xxx.supportbee.com Accept: / Accept-Encoding:
gzip, deflate, br Connection: close Content-Type: multipart/form-data;
boundary="ea97cbc1-70ea-4cc4-9801-09f5feffc763" Content-Length: 206
--ea97cbc1-70ea-4cc4-9801-09f5feffc763 Content-Disposition: form-data; name="files[]"; filename=sample; filename*=utf-8''sample
This contains example text.
--ea97cbc1-70ea-4cc4-9801-09f5feffc763--
The difference I can see is that the individual part in Postman has its own Content-Type: text/plain header for the file, and mine doesn't. I'm unable to add this because if I try content.Headers.Add("Content-Type", "text/plain"); It fails with 'Cannot add value because header 'Content-Type' does not support multiple values.'
First, it's important to note that a 500 response is akin to an unhandled exception, i.e. it's a bug on their end and more or less impossible to know for sure what you did wrong. I would suggest reporting it to them and, although I'm not familiar with Support Bee, I would hope they have good support people who can help you troubleshoot. :)
But if you want to play the guessing game, I agree that subtle differences between your successful Postman call and your code are a good place to start. For that header, note that content is the MultipartFormDataContent. You actually want to set it on the StreamContent object.
Also, look at the request headers Postman is sending and see if Content-Disposition includes a filename. You might need to add that to your code too, if the API is expecting it.
Here's how to do both:
var fileContent = new StreamContent(File.OpenRead(path));
fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
content.Add(fileContent, "files[]", Path.GetFileName(path));
If that's not the problem, look at the "raw" version of the request body in Postman, as well as those 11 request headers, and see if you can spot anything else you might be missing.
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 using the HttpClient class to send some data to specific host. I just want to send a pure header without any additional lines in it like ("Host: http"). So this line is the last to be removed from the header, but I don't know how.
The code:
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Post, aUrl);
msg.Headers.Clear();
msg.Headers.Remove("Host");
msg.Headers.ExpectContinue = false;
Encoding encoding = ConfiguratorASUST.Instance.Encoding ?? Encoding.GetEncoding(ConfiguratorASUST.ENCODING_DEFAULT);
msg.Content = new StringContent(aStr, encoding);
_client.SendAsync(msg);
The result header in Fiddler:
POST http://http//localhost.fiddler:60001/POS/POSTELESPIS HTTP/1.1
Content-Type: text/plain; charset=windows-1251
Host: http
This line Host: http needs to be removed from the message's header. But how on earth can I do that?! I tried the following:
msg.Headers.Clear();
msg.Headers.Remove("Host");
To no avail. Actually I also see the header Proxy-Connection: Keep-Alive being added.
If you carefully inspect your URL, it looks like your it is wrong anyway: http://http// - is your host really named http, and do you really need two slashes after it? Anyway if you fix that, the Host header will carry localhost.fiddler:60001.
By removing the Host header, you're essentially downgrading your request to HTTP/1.0.
You can set the HTTP version in the HttpRequestMessage as explained in Set HTTP protocol version in HttpClient:
msg.Version = HttpVersion.Version10;
But when using Fiddler, it acts as a proxy, and forwards your request as an HTTP/1.1 request - including the host header again. You can also alter the request in Fiddler. This is explained in How do I prevent fiddler from insering "Host" HTTP header?, but note the bold text, emphasis mine:
Per the RFC, as a HTTP/1.1 proxy, Fiddler is required to add a Host header.
It's not clear why this is problematic-- any server that has a problem with this is, by definition, buggy and should be fixed.
You can remove the header if you'd like (although doing so can cause problems elsewhere). Click Rules > Customize Rules. Scroll to OnBeforeRequest and add the following:
if (oSession.oRequest.headers.HTTPVersion == "HTTP/1.0")
{
oSession["x-overridehost"] = oSession.host;
oSession.oRequest.headers.Remove("Host");
}
for my WebAPI OData application, im trying to give my client (browser) the decision what format the data output should be. as $format is not implemented yet in WebAPI OData, im using Raghuramn's example here: https://gist.github.com/raghuramn/5556691
var queryParams = request.GetQueryNameValuePairs();
var dollarFormat = queryParams.Where(kvp => kvp.Key == "$format").Select(kvp => kvp.Value).FirstOrDefault();
if (dollarFormat != null)
{
request.Headers.Accept.Clear();
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(dollarFormat));
// remove $format from the request.
request.Properties[HttpPropertyKeys.RequestQueryNameValuePairsKey] = queryParams.Where(kvp => kvp.Key != "$format");
}
This works for JSON ($format=application/json;odata=fullmetadata) and JSON light (format=application/json;odata=light) but so far not for xml.
if i add $format=application/XML to the querystring, it still outputs to json light. how do i force XML output?
EDIT:
even if i force xml in Fiddler by sending
Content-type: application/xml and
Accept: application/xml
with the request, the response simply lists:
Content-Type: application/json; odata=minimalmetadata; streaming=true; charset=utf-8
EDIT 2:
Accept: application/atom+xml does seem to output xml in the raw response. Unfortunately, "application/atom+xml" throws a FormatException in:
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/atom+xml"));
setting the request ContentType instead of the AcceptHeader did the trick:
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/atom+xml");
Thanks to a search on the keywords request.Headers.Accept.Add and MediaTypeWithQualityHeaderValue that were presented by this question, I found a CodeProject Article that actually presented the syntax to correctly add the Accept header and solve the same issue:
this.Request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/atom+xml"));
instead of MediaTypeWithQualityHeaderValue.Parse("application/atom+xml") which throws the FormatException.