I've implemented C# code using the Google Translation V2 api with the GET Method.
It successfully translates small texts but when increasing the text length and it takes 1,800 characters long ( including URI parameters ) I'm getting the "URI too large" error.
Ok, I burned down all the paths and investigated the issue across multiple pages posted on Internet. All of them clearly says the GET method should be overriden to simulate a POST method ( which is meant to provide support to 5,000 character URIs ) but there is no way to find out a code example to of it.
Does anyone has any example or can provide some information?
[EDIT] Here is the code I'm using:
String apiUrl = "https://www.googleapis.com/language/translate/v2?key={0}&source={1}&target={2}&q={3}";
String url = String.Format(apiUrl, Constants.apiKey, sourceLanguage, targetLanguage, text);
Stream outputStream = null;
byte[] bytes = Encoding.ASCII.GetBytes(url);
// create the http web request
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.KeepAlive = true;
webRequest.Method = "POST";
// Overrride the GET method as documented on Google's docu.
webRequest.Headers.Add("X-HTTP-Method-Override: GET");
webRequest.ContentType = "application/x-www-form-urlencoded";
// send POST
try
{
webRequest.ContentLength = bytes.Length;
outputStream = webRequest.GetRequestStream();
outputStream.Write(bytes, 0, bytes.Length);
outputStream.Close();
}
catch (HttpException e)
{
/*...*/
}
try
{
// get the response
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
if (webResponse.StatusCode == HttpStatusCode.OK && webRequest != null)
{
// read response stream
using (StreamReader sr = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8))
{
string lista = sr.ReadToEnd();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(TranslationRootObject));
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(lista));
TranslationRootObject tRootObject = (TranslationRootObject)serializer.ReadObject(stream);
string previousTranslation = string.Empty;
//deserialize
for (int i = 0; i < tRootObject.Data.Detections.Count; i++)
{
string translatedText = tRootObject.Data.Detections[i].TranslatedText.ToString();
if (i == 0)
{
text = translatedText;
}
else
{
if (!text.Contains(translatedText))
{
text = text + " " + translatedText;
}
}
}
return text;
}
}
}
catch (HttpException e)
{
/*...*/
}
return text;
}
Apparently using WebClient won't work as you cannot alter the headers as needed, per the documentation:
Note: You can also use POST to invoke the API if you want to send more data in a single request. The q parameter in the POST body must be less than 5K characters. To use POST, you must use the X-HTTP-Method-Override header to tell the Translate API to treat the request as a GET (use X-HTTP-Method-Override: GET).
You can use WebRequest, but you'll need to add the X-HTTP-Method-Override header:
var request = WebRequest.Create (uri);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.Headers.Add("X-HTTP-Method-Override", "GET");
var body = new StringBuilder();
body.Append("key=SECRET");
body.AppendFormat("&source={0}", HttpUtility.UrlEncode(source));
body.AppendFormat("&target={0}", HttpUtility.UrlEncode(target));
//--
body.AppendFormat("&q={0}", HttpUtility.UrlEncode(text));
var bytes = Encoding.ASCII.GetBytes(body.ToString());
if (bytes.Length > 5120) throw new ArgumentOutOfRangeException("text");
request.ContentLength = bytes.Length;
using (var output = request.GetRequestStream())
{
output.Write(bytes, 0, bytes.Length);
}
The accepted answer appears to be out of date. You can now use the WebClient (.net 4.5) successfully to POST to the google translate API making sure to set the X-HTTP-Method-Override header.
Here is some code to show you how.
using (var webClient = new WebClient())
{
webClient.Headers.Add("X-HTTP-Method-Override", "GET");
var data = new NameValueCollection()
{
{ "key", GoogleTranslateApiKey },
{ "source", "en" },
{ "target", "fr"},
{ "q", "<p>Hello World</p>" }
};
try
{
var responseBytes = webClient.UploadValues(GoogleTranslateApiUrl, "POST", data);
var json = Encoding.UTF8.GetString(responseBytes);
var result = JsonConvert.DeserializeObject<dynamic>(json);
var translation = result.data.translations[0].translatedText;
}
catch (Exception ex)
{
loggingService.Error(ex.Message);
}
}
? What? it is trivial to post using C# - it is right there in the documentation.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
{
// Set type to POST
request.Method = "POST";
From there on you bascially put the data into fom fields into the content stream.
This is not "simulate a post meethod", it is fully doing a post request as per specifications.
Btw. hwhere does json enter here? You say "in C#". There is no need to use json?
Related
I have a Node Http triggered azure function that returns an array in the response body. I want to call this function inside a C# console application and access the array returned by the azure function. My azure function returns the expected response body when I hit it on postman. However this is not the case when I call this endpoint on the c# application as I'm getting a response object with status 200 but content-length is -1. Please note if I do the same call for a normal express application in the c# application, I'm able to get the expected response
Here is my azure function
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
context.log('HTTP trigger function processed a request.');
const adresses = req.body && req.body.adresses;
if (adresses) {
//make an asyn function call
const locationsPromise = await adresses.map(async (address) => await getLocationCoordinates(address));
const resolvedLocations = await Promise.all(locationsPromise);
context.res = await {
// status: 200, /* Defaults to 200 */
body:{ data: resolvedLocations}
};
}
else {
context.res = await {
status: 400,
body: "Please pass a adresses in the request body"
};
}
};
Here is my C# code I'm using for the call
{
string[] adresses = new string[] { "nairobi", "nakuru", "kericho" };
string apiUrl = "http://localhost:7071/api/Adress-call";
Dictionary<string, string[]> postData1 = new Dictionary<string, string[]>()
{
{ "adresses", adresses },
};
var postData = JsonConvert.SerializeObject(postData1);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = postData.Length;
using (Stream webStream = request.GetRequestStream())
using (StreamWriter requestWriter = new StreamWriter(webStream, Encoding.ASCII))
{
requestWriter.Write(postData);
}
try
{
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
DataTable ds = new DataTable();
var pageViewModel = JsonConvert.DeserializeObject<List<LocationModel>>(reader.ReadToEnd());
}
}
catch (WebException ex)
{
WebResponse errorResponse = ex.Response;
using (Stream responseStream = errorResponse.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
String errorText = reader.ReadToEnd();
// log errorText
}
throw;
}
}
The Content-Header may be missing from the response. If you check the HttpWebResponse.ContentLength you'll get a -1. As the docs explain:
If the Content-Length header is not set in the response, ContentLength is set to the value -1.
HttpWebRequest and HttpWebResponse are really old classes that are largely replaced by HttpClient, especially in .NET Core. There were no nullable value types back when these classes were created, so they can only return -1.
All of this should be replaced with HttpClient. The code isn't just simpler, the class itself is thread-safe and can be reused. It caches sockets underneath, which means the app doesn't have to pay the DNS resolution tax each time it tries to call the same endpoint :
var data=new { adresses = new [] { "nairobi", "nakuru", "kericho" }};
var postData = JsonConvert.SerializeObject(data);
var content=new StringContent(data,Encoding.UTF8,"application/json");
var response=await client.PostAsync(content);
At this point you can inspect the status code :
if (response.StatusCode == ...) // Check status code here.
{
...
}
The Content-Length is a Content header. It may be missing, in which case the ContentLength property will be null :
if (response.Content.Headers.ContentLength!=null)
{
long length=response.Content.Headers.ContentLength.Value;
//Use the Content length here
}
Or, using pattern matching :
if (response.Content.Headers.ContentLength is long length)
{
//Use the Content length here
}
Finally, you can read the response as a string with ReadAsStringAsync and deserialize it. That method will take care of decoding the response body :
var json=await response.Content.ReadAsStringAsync();
var models=JsonConvert.DeserializeObject<List<LocationModel>>(json);
I am attempting to create a console app that sends a WebRequest to a website so that I can get some information back from it in JSON format. Once I build up the request and try to get response I just want to simply print out the data, but when I call httpWebRequest.getResponse() it returns NULL.
I have tried multiple other methods of sending the data to the the url but those are all giving me like 404, or 400 errors, etc. This method at least isn't giving me any error, just a NULL.
Here is a snapshot of the documentation I am using for the API (albeit the docs aren't complete yet):
Here is the console app code that I have right now:
try
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://api.remot3.it/apv/v27/user/login");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
httpWebRequest.Headers.Add("developerkey", "***KEY***");
using (var streamWriter = new
StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(new
{
email = "***EMAIL***",
password = "***PASSWORD***"
});
Console.WriteLine(json);
streamWriter.Write(json);
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
Console.WriteLine(result);
Console.ReadLine();
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
Console.ReadLine();
}
Expected output is some JSON data, but I am getting a NULL back from getResponse().
Try to serialize the credential in your form and for header send as parameter for this class.
Check below for my code. It is not 100 % fit to your requirement, but atleast it will help to get through your logic.
Here is what I get Json Response from this code. Its work Perfect. Please remember to add timeout option on your webrequest and at the end close the streamreader and stream after completing your task. please check this code.
public static string httpPost(string url, string json)
{
string content = "";
byte[] bs;
if (json != null && json != string.Empty)
{
bs = Encoding.UTF8.GetBytes(json);
}
else
{
bs = Encoding.UTF8.GetBytes(url);
}
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
req.Method = "POST";
if (json != string.Empty)
req.ContentType = "application/json";
else
req.ContentType = "application/x-www-form-urlencoded";
req.KeepAlive = false;
req.Timeout = 30000;
req.ReadWriteTimeout = 30000;
//req.UserAgent = "test.net";
req.Accept = "application/json";
req.ContentLength = bs.Length;
using (Stream reqStream = req.GetRequestStream())
{
reqStream.Write(bs, 0, bs.Length);
reqStream.Flush();
reqStream.Close();
}
using (WebResponse wr = req.GetResponse())
{
Stream s = wr.GetResponseStream();
StreamReader reader = new StreamReader(s, Encoding.UTF8);
content = reader.ReadToEnd();
wr.Close();
s.Close();
reader.Close();
}
return content;
}
I am doing an API Post request and cant seem to get it to work. I always get a sendFailure webexception and the response for the exception is always null so catching the exception is useless. It keeps happening when I try to get the httpWebResponse. I noticed too the request.contentlength gave errors at postream getrequeststream so i commented it out. Test.json is the file I use for the request body. I also tested this on different API testers by including the URL, body, and content-type in the header and they worked. I just cant seem to code it for myself. The credentials work I just dont know if im doing the request correctly?
JSON File:
{
"email": "abc#123.com",
"password": "12345",
"facilityNumber": "987654"
}
string filepath = "test.json";
string result = string.Empty;
using (StreamReader r = new StreamReader(filepath))
{
var json = r.ReadToEnd();
var jobj = JObject.Parse(json);
foreach (var item in jobj.Properties())
{
item.Value = item.Value.ToString().Replace("v1", "v2");
}
result = jobj.ToString();
Console.WriteLine(result);
}
try
{
string setupParameters;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://www.test.com/abcde");
request.AllowAutoRedirect = true;
setupParameters = result;
ServicePointManager.ServerCertificateValidationCallback = (s, cert, chain, ssl) => true;
ASCIIEncoding encoding = new ASCIIEncoding();
var postData = setupParameters;
request.Method = "POST";
request.ContentType = "application/json";
byte[] data = encoding.GetBytes(postData);
//request.ContentLength = data.Length;
using (StreamWriter postStream = new StreamWriter(request.GetRequestStream()))//error if uncomment contentlength
{
postStream.Write(postData);
postStream.Flush();
postStream.Close();
}
HttpWebResponse wr = (HttpWebResponse)request.GetResponse();//error occurs
Stream receiveStream = wr.GetResponseStream();
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
}
catch (WebException ex)
{
if (ex.Response != null)
{
using (var errorResponse = (HttpWebResponse)ex.Response)
{
using (var reader = new StreamReader(errorResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
result = error;
}
}
}
I suggest modifiying your request to follow this format. Especially pay attention to the request.Method and request.ContentType which have caught me out multiple times.
Also, handling the response is easier this way.
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(YOURURL);
request.ContentType = "application/json; charset=utf8";
request.Headers.Add(ADD HEADER HERE IF YOU NEED ONE);
request.Method = WebRequestMethods.Http.Post; // IMPORTANT
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(JsonConvert.SerializeObject(JSONBODYSTRING));
// I USUALLY YOU JSONCONVERT HERE TO SIMPLY SERIALIZE A STRING CONTAINING THE JSON INFO.
//BUT I GUESS YOUR METHOD WOULD ALSO WORK
streamWriter.Flush();
streamWriter.Close();
}
WebResponse response = request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
string result = streamReader.ReadToEnd();
// DO WHATEVER YOU'D LIKE HERE
}
} catch (Exception ex)
{
// HANDLE YOUR EXCEPTIONS
}
A friend show me this sample code to implement HTTP POST in C# and did work out in a WINFORM App:
http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/
And implemented in a METRO APP:
// this is what we are sending
string post_data = "user=user#example.com&pass=example123";
// this is where we will send it
string uri = "http://app.proceso.com.mx/win8/login";
// create a request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
// turn our request string into a byte stream
byte[] postBytes = Encoding.UTF8.GetBytes(post_data);
// this is important - make sure you specify type this way
request.ContentType = "application/x-www-form-urlencoded";
Stream requestStream = await request.GetRequestStreamAsync();
// now send it
requestStream.Write(postBytes, 0, postBytes.Length);
// grab te response and print it out to the console along with the status code
WebResponse response = await request.GetResponseAsync();
//var a = new StreamReader(response.GetResponseStream()).ReadToEnd();
StreamReader requestReader = new StreamReader(response.GetResponseStream());
String webResponse = requestReader.ReadToEnd();
I realized, HttpWebRequest does not contain ProtocolVersion and is throwing me this error in this line:
WebResponse response = await request.GetResponseAsync();
// ERROR: The remote server returned an error: (417) Expectation Failed.
I guess the last property is the solution. How can I solve this problem?
Thanks in advance
I recently wrote a small function to handle the posting of trivial data to a server.
private struct HttpPostParam
{
private string _key;
private string _value;
public string Key { get { return HttpUtility.UrlEncode(this._key); } set { this._key = value; } }
public string Value { get { return HttpUtility.UrlEncode(this._value); } set { this._value = value; } }
public HttpPostParam(string key, string value)
{
this._key = key;
this._value = value;
}
};
private static string PostTrivialData(Uri page, HttpPostParam[] parameters)
{
string pageResponse = string.Empty;
try
{
var request = (HttpWebRequest)WebRequest.Create(page); //create the initial request.
request.Method = WebRequestMethods.Http.Post; //set the method
request.AllowAutoRedirect = true; //couple of settings I personally prefer.
request.KeepAlive = true;
request.ContentType = "application/x-www-form-urlencoded";
//create the post data.
byte[] bData = Encoding.UTF8.GetBytes(string.Join("&", Array.ConvertAll(parameters, kvp => string.Format("{0}={1}", kvp.Key, kvp.Value))));
using (var reqStream = request.GetRequestStream())
reqStream.Write(bData, 0, bData.Length); //write the data to the request.
using (var response = (HttpWebResponse)request.GetResponse()) //attempt to get the response.
if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.NotModified) //check for a valid status (should only return 200 if successful)
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
pageResponse = reader.ReadToEnd();
}
catch (Exception e)
{
/* todo: any error handling, for my use case failing gracefully was all that was needed. */
}
return pageResponse;
}
Essentially it posts the value pairs defined in the "parameters" argument. Will require a reference and import of the System.Web namespace to compile.
I just tested it with your website and got a response back:
HttpPostParam[] httpparams = {
new HttpPostParam("user", "censored#email.com"),
new HttpPostParam("pass", "example123")
};
string response = PostTrivialData(new Uri("http://app.proceso.com.mx/win8/login"), httpparams);
Let me know if there's any issues.
I am trying to make an Http POST to an Apache web server.
I am finding that setting ContentLength seems to be required for the request to work.
I would rather create an XmlWriter directly from GetRequestStream() and set SendChunked to true, but the request hangs indefinitely when doing so.
Here is how my request is created:
private HttpWebRequest MakeRequest(string url, string method)
{
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
request.Method = method;
request.Timeout = Timeout; //Property in my class, assume it's 10000
request.ContentType = "text/xml"; //I am only writing xml with XmlWriter
if (method != WebRequestMethods.Http.Get)
{
request.SendChunked = true;
}
return request;
}
How can I make SendChunked work so I do not have to set ContentLength? I do not see a reason to store the XmlWriter's string somewhere before sending it to the server.
EDIT: Here is my code causing the problem:
using (Stream stream = webRequest.GetRequestStream())
{
using (XmlWriter writer = XmlWriter.Create(stream, XmlTags.Settings))
{
Generator.WriteXml<TRequest>(request, writer);
}
}
Before I did not have a using on the Stream object returned from GetRequestStream(), I assumed XmlWriter closed the stream when disposed, but this is not the case.
One of the answers below, let me to this. I'll mark them as the answer.
As far as HttpWebRequest is concerned, my original code works just fine.
This should work the way you have it written. Can we see the code that actually does the uploading? Are you remembering to close the stream?
Looking at the example at http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.sendchunked.aspx they still set a content length. Really the bottom line is that if you are sending data you need to tell the receiver how much data you will be sending. Why don't you know how much data you are sending before you send the request?
ContentLength:
Property Value
Type: System..::.Int64
The number of bytes of data to send to the Internet resource. The default is -1, which indicates the property has not been set and that there is no request data to send.
Edit for Aaron (I was wrong):
HttpWebRequest httpWebRequest = HttpWebRequest.Create("http://test") as HttpWebRequest;
httpWebRequest.SendChunked = true;
MessageBox.Show("|" + httpWebRequest.TransferEncoding + "|");
From System.Net.HttpWebRequest.SerializeHeaders():
if (this.HttpWriteMode == HttpWriteMode.Chunked)
{
this._HttpRequestHeaders.AddInternal("Transfer-Encoding", "chunked");
}
else if (this.ContentLength >= 0L)
{
this._HttpRequestHeaders.ChangeInternal("Content-Length", this._ContentLength.ToString(NumberFormatInfo.InvariantInfo));
}
I prefer to use a generic method to comply this kind of stuff. Take a look at the XML sender request below. It will serialize your XML and then send it with the appropriate ContentType :
public bool SendXMLRequest<T>(T entity, string url, string method)
{
HttpWebResponse response = null;
bool received = false;
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
var credCache = new CredentialCache();
var netCred = new NetworkCredential(YOUR_LOGIN_HERE, YOUR_PASSWORD_HERE, YOUR_OPTIONAL_DOMAIN_HERE);
credCache.Add(new Uri(url), "Basic", netCred);
request.Credentials = credCache;
request.Method = method;
request.ContentType = "application/xml";
request.SendChunked = "GET" != method.ToUpper();
using (var writer = new StreamWriter(request.GetRequestStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringWriter textWriter = new Utf8StringWriter())
{
serializer.Serialize(textWriter, entity);
var xml = textWriter.ToString();
writer.Write(xml);
}
}
response = (HttpWebResponse)request.GetResponse();
received = response.StatusCode == HttpStatusCode.OK; //YOu can change the status code to check. May be created, etc...
}
catch (Exception ex) { }
finally
{
if(response != null)
response.Close();
}
return received;
}