I have the following function which makes a HTTP request and returns the response body as a string:
private string getResponseBody(string method, string partialUrl)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(_hostPath + partialUrl);
req.ContentLength = 0;
req.KeepAlive = false;
req.Method = method;
return new StreamReader(req.GetResponse().GetResponseStream()).ReadToEnd();
}
The first parameter, method, can only have the general HTTP methods as values (GET, POST, PUT, DELETE).
To force the input to be one of these values, I know I would need to create an object, but the details are escaping me. Can anyone help out?
PS: I'm using 4.0 framework
To force the input to be one of these values, I know I would need to create an object,
How about an enum:
public enum HttpMethod
{
GET,
POST,
PUT,
DELETE
}
and then:
private string getResponseBody(HttpMethod method, string partialUrl)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(_hostPath + partialUrl);
req.ContentLength = 0;
req.KeepAlive = false;
req.Method = method.ToString();
using (var response = req.GetResponse())
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
Also notice how I have fixed the flaw you had in the GetResponse code where you didn't dispose any of the IDisposable objects which could result in leaks and all kind of nasty stuff. For example if you don't dispose the response object it could result in timeouts if this method is called many times.
Related
After I create connection, I need to send IDs and then store response, then send again and again (usually ~60-100 IDs). Currently I did it this way:
public void EThread()
{
HttpWebRequest HWRequest = (HttpWebRequest)WebRequest.Create(_AjaxURL);
HWRequest.Method = "POST";
HWRequest.ContentType = "text/xml; encoding='utf-8'";
string StrID;
while(!_RequiredIDs.IsEmpty)
if (_RequiredIDs.TryDequeue(out StrID))
Extract(StrID, ref HWRequest);
}
public void Extract(string ID, ref HttpWebRequest HWRequest)
{
string data = "strID=" + ID;
byte[] Bytes = Encoding.ASCII.GetBytes(data);
using (Stream SWriter = HWRequest.GetRequestStream())
{
SWriter.Write(Bytes, 0, Bytes.Length);
SWriter.Close();
}
HttpWebResponse resp = (HttpWebResponse)HWRequest.GetResponse();
using (Stream s = resp.GetResponseStream())
using (StreamReader sr = new StreamReader(s))
_RespnseCollection.Enqueue(sr.ReadToEnd());
}
However, I got this error: System.Net.WebException: 'The request was aborted: The connection was closed unexpectedly.. If I put HttpWebRequest inside Extract method it works, but that works long. This was checked on 1 thread. What is wrong with the code or the server does not support this.
The problem is that you are re-using the HWRequest. But an HttpWebRequest is a single use item. In other words you can fire off the request only once.
So if thr _RequiredIDs contains more than one item, the first item will work properly, but change the state of the HwRequest to completed (ie it now contains a response) and the HaveResponse property will be set to true.
If you then again execute GetRequestStream()or GetResponse() it determines that, having already received a response, it does not make sense to set up the request again.
So you could modify your code as follows:
public void EThread()
{
string StrID;
while(!_RequiredIDs.IsEmpty)
if (_RequiredIDs.TryDequeue(out StrID))
{
HttpWebRequest HWRequest = (HttpWebRequest)WebRequest.Create(_AjaxURL);
HWRequest.Method = "POST";
HWRequest.ContentType = "text/xml; encoding='utf-8'";
Extract(StrID, ref HWRequest);
}
}
BUT, I suppose this is really functionally equivalent to declaring the HttpRequest and initializing inside the Extract method.
SUGGESTIION:
Try calling HWRequest.Reset():
public void EThread()
{
HttpWebRequest HWRequest = (HttpWebRequest)WebRequest.Create(_AjaxURL);
HWRequest.Method = "POST";
HWRequest.ContentType = "text/xml; encoding='utf-8'";
string StrID;
while(!_RequiredIDs.IsEmpty)
if (_RequiredIDs.TryDequeue(out StrID))
{
Extract(StrID, ref HWRequest);
HWRequest.Reset();
}
}
I originally asked a question regarding a WCF web service that I was trying to write and then found that the ASP.net web API was more appropriate to my needs, due to some feedback on here.
I've now found a good tutorial that tells me how to create a simple REST service using Web API which works well pretty much out of the box.
My question
I have a POST method in my REST service server:
// POST api/values/5
public string Post([FromBody]string value)
{
return "Putting value: " + value;
}
I can POST to this using POSTER and also my C# client code.
However the bit I don't understand is why I have to prepend an '=' sign to the POST data so that it reads: "=Here is my data which is actually a JSON string"; rather than just sending: "Here is my data which is actually a JSON string";
My C# Client that talks to the REST service is written as follows:
public string SendPOSTRequest(string sFunction, string sData)
{
string sResponse = string.Empty;
// Create the request string using the data provided
Uri uriRequest = GetFormRequest(m_sWebServiceURL, sFunction, string.Empty);
// Data to post
string sPostData = "=" + sData;
// The Http Request obj
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriRequest);
request.Method = m_VERB_POST;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(sPostData);
request.ContentLength = byteArray.Length;
request.ContentType = m_APPLICATION_FORM_URLENCODED;
try
{
using (Stream dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
sResponse = reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
//Log exception
}
return sResponse;
}
private static Uri GetFormRequest(string sURL, string sFunction, string sParam)
{
StringBuilder sbRequest = new StringBuilder();
sbRequest.Append(sURL);
if ((!sURL.EndsWith("/") &&
(!string.IsNullOrEmpty(sFunction))))
{
sbRequest.Append("/");
}
sbRequest.Append(sFunction);
if ((!sFunction.EndsWith("/") &&
(!string.IsNullOrEmpty(sParam))))
{
sbRequest.Append("/");
}
sbRequest.Append(sParam);
return new Uri(sbRequest.ToString());
}
Is anybody able to explain why I have to prepend the '=' sign as in the above code (string sPostData = "=" + sData;)?
Many thanks in advance!
The content type x-www-form-urlencoded is a key-value format. With form bodies you are only able to read a single simple type from a request body. As a name is expected, but in this case not allowed, you have to prefix the equal sign to indicate that there is no name with the followed value.
However, you should lean away from accepting simple types within the body of your web-api controller actions.
You are limited to only a single simple type if you attempt to pass data in the body of an HttpPost/HttpPut without directly implementing your own MediaTypeFormatter, which is unlikely to be reliable. Creating a light-weight complex type is generally much more preferable, and will make interoperating with other content-types, like application/json, much easier.
I am a new user in C# and I'm haveing some problems with my code.
I cant deserialize JSON data and I cant understand why:
webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........"); // Create a request to get server info
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookieJar; //set container for HttpWebRequest
webResponse = (HttpWebResponse)webRequest.GetResponse(); // Get the response.
reader = new StreamReader(webResponse.GetResponseStream());
ServerInfo outObject = JsonConvert.DeserializeObject<ServerInfo>(reader.ToString());
my_label_ServerInfo.Text = outObject.message;
the server info class:
public class ServerInfo
{
public string message { get; set; }
public string message_timestamp { get; set; }
}
In C#,
reader.ToString()
will by default return the name of the class. In this case, "System.IO.StreamReader"
What you want is
reader.ReadToEnd()
which will return the entire contents of the stream as a string.
That should cause it to work, but be aware that it's not best practice. A few areas for consideration as you learn more about C#:
As Aron mentioned, you should wrap all your streams and readers in "using" statement to take advantage of the Dispose pattern which will let the runtime know it can release resources right away rather than waiting for the finalizer
As Fred demonstrated in his code, you can avoid converting the stream to a string and just let the Json.Net library do that.
To ensure that you properly escape and format the request URL, you could use the UriBuilder class: new UriBuilder("http", ip, port, path).Uri)
You could use the newer and async friendly HttpClient class to download the data.
Although Jeff is correct in WHY it doesn't work correctly. His answer still isn't the correct way to "fix" your code. Strings are very inefficient in C# (like almost EVERY programming language, and we avoid them as much as possible).
So you should be doing this instead.
//STOP USING member fields (when possible),
//you encourage threading problems with member fields.
var webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........"); // Create a request to get server info
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookieJar; //set container for HttpWebRequest
var webResponse = (HttpWebResponse)webRequest.GetResponse();
//ALWAYS dispose your disposable correctly
//Not disposing HttpStreams will cause you to leak TCP/IP
//ports.
using(var stream = webResponse.GetResponseStream())
using(var reader = new StreamReader(stream))
{
JsonSerializer serializer = new JsonSerializer();
ServerInfo outObject = (ServerInfo)serializer.Deserialize(reader, typeof(ServerInfo));
my_label_ServerInfo.Text = outObject.message;
}
What is the json that comes in your response body?
You might want to start off with a little test to make sure that the response body can correctly deserialize into your ServerInfo class. This is a matter of personal preference, but I like to do things a bit more explicitly as it helps to minimize unexpected behavior down the road.
For example, you could decorate your ServerInfo class like this:
// I chose MemberSerialization.OptIn so that all members need to be
// included explicitly, rather than implicitly (which is the default)
[JsonObject(MemberSerialization.OptIn)]
public class ServerInfo
{
[JsonProperty]
public string message { get; set; }
[JsonProperty]
public string message_timestamp { get; set; }
}
Then, you read the full HttpWebResponse body into a string like this:
reader = new StreamReader(webResponse.GetResponseStream());
string responseBody = reader.ReadToEnd();
reader.Close();
And lastly, you deserialize the response body into your ServerInfo class like this:
ServerInfo serverInfo = JsonConvert.DeserializeObject<ServerInfo>(responseBody);
This is assuming your json will come in the following format (or of a similar structure):
{
"message": "Test Message",
"message_timestamp": "2015-04-04T20:00:00"
}
Of course you should first check if your actual input deserializes correctly. I tried the format above in a unit test with this simple snippet:
var sb = new StringBuilder();
sb.Append("{");
sb.AppendLine();
sb.AppendFormat("\"{0}\": \"{1}\"", "message", "Test Message");
sb.Append(",");
sb.AppendLine();
sb.AppendFormat("\"{0}\": \"{1}\"", "message_timestamp", "2015-04-04T20:00:00");
sb.AppendLine();
sb.Append("}");
string json = sb.ToString();
ServerInfo serverInfo = JsonConvert.DeserializeObject<ServerInfo>(json);
EDIT: I completely agree with Aron in that you shouldn't unnecessarily use member fields and always make sure to dispose streams properly.
Improving my original answer with his suggestions, the following code I suggested earlier:
webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........");
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookieJar;
webResponse = (HttpWebResponse)webRequest.GetResponse();
reader = new StreamReader(webResponse.GetResponseStream());
string responseBody = reader.ReadToEnd();
reader.Close();
ServerInfo serverInfo = JsonConvert.DeserializeObject<ServerInfo>
my_label_ServerInfo.Text = serverInfo.message;
Would change into this, which will perform better and is less prone to errors (I removed the comments for brevity, see Aron's answer for the explanations):
var webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........");
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookieJar;
var webResponse = (HttpWebResponse)webRequest.GetResponse();
using (var stream = webResponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
JsonSerializer serializer = new JsonSerializer();
ServerInfo serverInfo = (ServerInfo)serializer.Deserialize(reader, typeof(ServerInfo));
my_label_ServerInfo.Text = serverInfo.message;
}
This will still work with the explicit JSON serialization attributes I added to your ServerInfo class. Note that they are not strictly necessary if the property names match up. I do this mainly just to show you how to gain more control over the serialization behavior without the need to implement a custom JsonSerializer.
I'd suggest the approach of accessing JSON values with structured JSON types (object and array), without having to predefine types (such as the ServerInfo type) to deserialize into.
JsonObject serverinfo = (JsonObject)JsonObject.Load(responseStream);
I have the following MVC method.
[System.Web.Mvc.HttpPost]
public ActionResult Listen(string status)
{
CFStatusMessage statusMessage = new CFStatusMessage();
if (!string.IsNullOrEmpty(status))
{
statusMessage = Newtonsoft.Json.JsonConvert.DeserializeObject<CFStatusMessage>(status);
}
return Content(Server.HtmlEncode(status));// View(statusMessage);
}
I am trying to call the above method from Other application .. (Console). I am using HttpWebRequest to make a call to the MVC Method. Using the below code its able to call the method but the Parameter is always coming as empty string.
string content = "{\"status\":\"success\",\"payload\":\"some information\"}";
string url = "http://myrl.com";
var httpWRequest = (HttpWebRequest) WebRequest.Create(url);
httpWRequest.Method = "POST";
httpWRequest.ContentType = "text/json";
var encoding = new ASCIIEncoding();
byte[] data = encoding.GetBytes(string.Format("status={0}", Uri.EscapeDataString(content)));
httpWRequest.ContentLength = data.Length;
Stream stream = httpWRequest.GetRequestStream();
stream.Write(data, 0, data.Length);
var response = (HttpWebResponse)httpWRequest.GetResponse();
With this its making a call to Listen method but status parameter is always coming blank. whereas I want the json string {status:"success",payload:"some information"} as parameter.
What am I doing wrong?
P.S.: I tried the below statement as well, while sending the actual content.
byte[] data = encoding.GetBytes(content);
Regards,
M
If do you need to provide any kind of service from MVC tryout WebApi instead. You can use HTTP REST to do this easily.
Read more here ASP.NET WebApi
You appear to be saying the request is json, but sending it using wwwencoding.
Remove the status={0} line bit & just send the json as is.
You can try something like this
using (var sw = new StreamWriter(httpWRequest.GetRequestStream()))
{
sw.Write(content);
sw.Flush();
sw.Close();
}
I'm trying to get this correct as I feel I'm missing something. I want to use the keyword using whenever I have an IDisposable object. Please note that the code works, I just want to optimize it.
I have two questions here:
1) For this code:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
What does it mean to add (HttpWebRequest) like that? Am I converting WebRequest to HttpWebRequest?
Why can't I do this?
HttpWebRequest rq = new HttpWebRequest();
rq.Create(url);
2) In the functional code below, how would I go about using the keyword using where applicable?
public static int UploadFileToixLibrary(string url, string file)
{
NetworkCredential credentials = new NetworkCredential();
credentials.UserName = AppVars.Username;
credentials.Password = AppVars.Password;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = credentials;
request.Method = "POST";
request.ContentType = "image/tiff";
request.Headers.Add("X-Object-Key", Path.GetFileName(file));
byte[] bytes = File.ReadAllBytes(file);
Stream st = null;
try
{
request.ContentLength = bytes.Length;
st = request.GetRequestStream();
st.Write(bytes, 0, bytes.Length);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return 1;
}
finally
{
if (st != null)
{
st.Close();
}
}
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
response.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return 1;
}
return 0;
}
Question 1:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
The reason for the cast, is that the static Create method on WebRequest returns an instance of WebRequest which is most appropriate for the scheme you supply in the url (ie, http:// address will return an HttpWebRequest, ftp:// will return FtpWebRequest etc). As you know your url is http, you know you'll get back an HttpWebRequest, so you can explicitly cast to the right type to get access to the extra functionality HttpWebRequest over the abstract WebRequest.
Now, WebRequest is not IDisposable, so you cannot use it inside a using statement!
Question 2: In your functional code, the only place you could use a using statement is around the Stream access:
try
{
request.ContentLength = bytes.Length;
st = request.GetRequestStream();
st.Write(bytes, 0, bytes.Length);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return 1;
}
finally
{
if (st != null)
{
st.Close();
}
}
Could be re-written:
request.ContentLength = bytes.Length;
using(var st = request.GetRequestStream())
{
st.Write(bytes, 0, bytes.Length);
st.Close();
}
WebRequest is a abstract class, it does not expose most of the properties/methods that you're going to use. You must cast it to the appropriate type to use it. The static method Create will return the specialized WebRequest object according to your URL. As you know that this URL uses the HTTP protocol (http://), it's safe to cast it to HttpWebRequest. It could, however, be a FtpWebRequest.
You can put the GetRequestStream initilization inside using(...) statements. Looks like WebRequest (and its children) does not implement IDisposable
1.
The Webrequest.Create method returns a WebRequest reference, but the actual object has different types depending on what kind of request you want to do. In your case you are doing a HTTP request, so the actual object is an HttpWebRequest object. You cast the reference to the actual type of the object to get access to the members that are specific for the HttpWebRequest class.
2.
The HttpWebRequest is not disposable. Use a using block for the HttpWebResponse object:
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
...
}