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);
Related
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'm making a web-app that needs data through an API. I am trying to re-purpose some of the developer's example code of a different function than the original code was written for. Here's the important part:
WebRequest request = WebRequest.Create(urlPrefix + "getthingjson/" + Key);
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
reader.Close();
response.Close();
// Parse returned JSON into data
//
using (var web = new WebClient())
{
web.Encoding = System.Text.Encoding.UTF8;
var jsonString = responseFromServer;
var jss = new JavaScriptSerializer();
var ThingsList = jss.Deserialize<List<Things>>(jsonString);
string ThingsListStr = "";
foreach (Things x in ThingsList)
ThingsListStr = ThingsListStr + ", " + x.Name;
MessageBox.Show(ThingsListStr);
}
I know that I need to change 'Name' in order to get a different piece of info on the 'Things' call. The thing is, I need to call results on a different function, instead of 'Things' say 'Details'. I don't know what to look for in place of 'Name' since when I search that it returns nothing. How could I just deserialize all of what JSON returns? Sorry if my terminology was off or I made a simple mistake, I'm new to JSON and C#. Thanks!
To get Details instead of Name property you should:
Check if you receive this data in json.
Adjust your c# class with Details property if it doesn't exist. (Go to Things class and add new Details property)
Change
ThingsListStr = ThingsListStr + ", " + x.Name;
to
ThingsListStr = ThingsListStr + ", " + x.Details;
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.
I have the following curl code:
curl 'localhost:8983/solr/sessions/update?commit=true' -H 'Content-type:application/json' -d '[{"Session_SessionId":"da7007e9-fe7a-4bdf-b9e4-1a55034cf08f","Session_HasComments":{"set":true}}]'
I am trying to convert to C#, but I am getting an error every time so unsure if my code is correct...
Here's what I have so far:
string path = "http://localhost:8983/solr/sessions/update?commit=true";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(path);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"Session_SessionId\":\"" + sessionId + "\"," +
"\"" + fieldName + "\":{\"set\":\"" + fieldValue + "\"}}";
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
}
It always seems to error () on this line:
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
The error i get is:
The remote server returned an error: (400) Bad Request.","StackTrace":" at System.Net.HttpWebRequest.GetResponse()
Any ideas? Thanks in advance!
Dave
Maybe it's that you have removed square brackets in your JSON content that you are streaming into request? Try adding the [ ] back to the start/end of data. Although the "BadRequest" is usually quite strict error that tells you that your HTTP request is malformed, your server may actually return that code also for other cases - like missing session id - which probably occurred here.
note the diff:
-d '[{"Session_SessionId":"da70.....
^ bracket
and
string json = "{\"Session_SessionId\":\"" + sessionId + "\"," + ....
^ no bracket
and the same at the end of data.
But, of course, that's just a guess. :)
Your request cannot be understood by the server. Did you check the output of json variable. I believe the JSON string is not generating properly.
Why don't you use JavaScriptSerializer.Serialize to create JSON string.
Your use the Update Handler with a JSON object will need to follow the JSON format outlined in Update JSON - Update Commands
As another user has suggested your JSON output does not match the CURL. Try the following rather than typing the text.
var data = new[]
{
new
{
Session_SessionId = "da7007e9-fe7a-4bdf-b9e4-1a55034cf08f",
Session_HasComments = new {set = true}
}
};
var json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(data);
Also, you are using the using keyword to write the data through, and attempt to handle response while inside the block - it probably does make a difference but it might be worth moving this outside this block.
Lastly, you may need to encode the data as a byte array.
Here is the code implementing the above suggestions.
string path = "http://localhost:8983/solr/sessions/update?commit=true";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(path);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
var data = new[]
{
new
{
Session_SessionId = "da7007e9-fe7a-4bdf-b9e4-1a55034cf08f",
Session_HasComments = new {set = true}
}
};
string json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(data);
byte[] byteData = new System.Text.ASCIIEncoding().GetBytes(json);
httpWebRequest.ContentLength = byteData.Length;
using (Stream stream = httpWebRequest.GetRequestStream())
{
stream.Write(byteData,0,byteData.Length);
}
HttpWebResponse resp = (HttpWebResponse)httpWebRequest.GetResponse();
string respStr = new StreamReader(resp.GetResponseStream()).ReadToEnd();
Console.WriteLine("Response : " + respStr);
Just use SolrNet. If you are doing this with Solr 4+, you need to download latest code and built it yourself, but that's very easy.
Dave, it worked for me.
You were missing square brackets. Just replace respective line with below:
string json = "[{\"Session_SessionId\":\"" + sessionId + "\"," +
"\"" + fieldName + "\":{\"set\":\"" + fieldValue + "\"}}]";
I have a REST Service that accepts a id in the URL for PUT requests. So far the PUT request looks like this:
string url = "http://localhost:3596/WidgetManager.svc/Widgets/" + TextBox3.Text;
WebRequest req = WebRequest.Create(url);
req.Method = "PUT";
using (HttpWebResponse resp = req.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
Label4.Text = reader.ReadToEnd();
}
But I also need to send a Widget object in my request.
Widget w = new Widget();
w.Name = "worked!!!";
w.CogCount = 1000;
w.SprocketSize = 2000;
I saw a lot of examples on how to send strings. But what about objects like this?
You could serialise it using XML or JSON.
If it is such a small object, you could write your own small method like
.toJSON() {
return '{"Name":"' + this.name + '", "CogCount":' + this.CogCount + ', "SprocketSize":' + this.SprocketSize + '}';
}
//Output: '{"Name":"worked!!!", "CogCount":1000, "SprocketSize":2000}'
On the other hand: C# provides powerful (XML) serialisation tools!
This here: http://www.codeproject.com/Articles/1789/Object-Serialization-using-C is only one of many examples.
But if you use PHP or similar, JSON might even be more interesting.