HttpRequest (PHP to C#) - deserialize a string - c#

I am trying to send a string to a C# app via php, here is my code:
PHP
//set up variables
$theData = 'test
to see if a string can be deserialized';
$url = 'http://localhost:5900/';
//create the httprequest object
$httpRequest_OBJ = new httpRequest($url, HTTP_METH_POST, $options);
//add the raw post data
$httpRequest_OBJ->setRawPostData ($theData);
//send the http request
$result = $httpRequest_OBJ->send();
//get the object
$response = $result->getBody();
C#
Here is where it fails:
var methodRequestSerializer = new XmlSerializer(typeof(MethodRequest));
var methodRequest = methodRequestSerializer.Deserialize(reader) as MethodRequest;
PHP throws back the following error:
Fatal error: Uncaught exception 'HttpInvalidParamException' with message 'Empty or too short HTTP message: ''' in C:\xampp\htdocs\httpreq.php:39 inner exception 'HttpRequestException' with message 'server returned nothing (no headers, no data); Empty reply from server (http://localhost:5900/)' in C:\xampp\htdocs\httpreq.php:29 Stack trace: #0 C:\xampp\htdocs\httpreq.php(39): HttpRequest->send() #1 {main} thrown in C:\xampp\htdocs\httpreq.php on line 39
thanks.

When you say your reader comes from new StreamReader(context.Request.InputStream), I'm assuming that context is the ASP.NET HttpContext object, and your C# code is somehow deployed so as to receive an HttpRequest.
If so, the stream will contain just the bytes from the body of your HTTP request, which in the case of your example will just be the string "test to see if a string can be deserialized". Since this is not valid XML you should expect the Deserialize method call to fail.
You haven't told us what MethodRequest is, nor what an instance would look like when XML serialized. If the string you sent in the HTTP POST request were a valid XML-serialized instance of MethodRequest, I would expect your C# Deserialize method call to succeed.
If you'd be a little less coy and share fuller details of what you're trying to do and how, we might be able to help more easily.

Related

Error 400: Bad Request while posting through LinkedIn Share API

I have checked a bunch of questions on StackOverflow which may be termed as related but most of them are either too old or too specific, and doesn't help me much.
While working on an application that helps schedule posts on LinkedIn, I follow below process:
Allow user to Sign-up using their LinkedIn account.
After they submit for Sign-up, I store Access Token, Refresh Token and relevant TTL along with the user details.
When they schedule a post at a specific time, I have an Azure function (serverless) that checks for any post to submit every minute, and supposed to post on user's behalf.
Using the Access Tokenthat I saved earlier, I hit https://api.linkedin.com/v2/me to get the person:id of the user. Everything works as expected until here and gets the person:id using GetPersonID()
See my code below for posting using API:
string pid = GetPersonID();
sstring PostCode = #"{""content"":{""contentEntities"":[{""entityLocation"":""" + URL + #""",""thumbnails"":[{""imageSpecificContent"":{},""resolvedUrl"":""" + OriginalURL + #"""}]}],""description"":"" + URL + "",""title"":""" + Title + #"""},""distribution"":{""linkedInDistributionTarget"":{}},""owner"":""urn:li:person:" + pid + #""",""subject"":""" + Title + #""",""text"":{""text"":""" + Description + #"""}}";
string outJ = JsonConvert.SerializeObject(PostCode);
WebClient clientx = new WebClient();
clientx.Headers.Add("Authorization", "Bearer " + access_token);
clientx.Headers.Add("X-Restli-Protocol-Version", "2.0.0");
clientx.Headers.Add("x-li-format", "json");
clientx.Headers["Content-Type"] = "application/json";
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
string LinkedInAccessTokenURL = "https://api.linkedin.com/v2/shares";
string result = clientx.UploadString(LinkedInAccessTokenURL, "POST", data: outJ);
A Sample Json Post
{"content":{"contentEntities":[{"entityLocation":"http://www.test.com","thumbnails":[{"imageSpecificContent":{},"resolvedUrl":"https://picsum.photos/200/300"}]}],"description":" + URL + ","title":"This is a test post"},"distribution":{"linkedInDistributionTarget":{}},"owner":"urn:li:person:<person:id>","subject":"This is a test post for LinkedIn API","text":{"text":"ashdjahs dadjh asdjahs da\nahs djashdjkashdas\nahsd jasdhkjasdh "}}
Everything works until the last line of the above code. Upon POST the LinkedIn API throws an error System.Net.WebException: 'The remote server returned an error: (400) Bad Request.' (LinkedIn documentation reference). I've even tried posting the value in PostCode using Postman and it works perfectly fine, which tells me that the JSON generated it correct.
But despite multiple tries and fixes, nothing seems to work for actual Post.
Exception thrown:
Data: {System.Collections.ListDictionaryInternal}
HResult: -2146233079
HelpLink: null
InnerException: null
Message: "The remote server returned an error: (400) Bad Request."
Response: {System.Net.HttpWebResponse}
Source: "System.Net.Requests"
StackTrace: " at System.Net.HttpWebRequest.GetResponse()\r\n at System.Net.WebClient.GetWebResponse(WebRequest request)\r\n at System.Net.WebClient.DownloadBits(WebRequest request, Stream writeStream)\r\n at System.Net.WebClient.UploadBits(WebRequest request, Stream readStream, Byte[] buffer, Int32 chunkSize, Byte[] header, Byte[] footer)\r\n at System.Net.WebClient.UploadDataInternal(Uri address, String method, Byte[] data, WebRequest& request)\r\n at System.Net.WebClient.UploadString(Uri address, String method, String data)\r\n at System.Net.WebClient.UploadString(String address, String method, String data)"
Status: ProtocolError
TargetSite: {System.Net.WebResponse GetResponse()}
Marking as resolved.
The issue was apparently with the Json being generated. Probably it was encoding, but I'm still confused on how Postman was able to post it without any issues.
I created relevant class, and then using its object, I serialized it into Json using Newtonsoft. Which after passing to the WebClient object rendered the correct results.
Lessons learnt!
I know manually creating a JSON is never a good idea (what I actually did earlier, to save time), but at times being lazy can make you work harder than you would have in the first place. So if we're planning to do an API call with JSON as payload, its never a waste of time to create a relevant class and then serialize to Json.

SendGrid throws "Error while copying content to a stream"

I'm creating a Core 2.1 solution in Visual Studio 2017 where I send emails via Sendgrid. When trying to send an email via SendGrid, I get the following error:
An unhandled exception occurred while processing the request.
IOException: The server returned an invalid or unrecognized response.
System.Net.Http.HttpConnection.FillAsync() HttpRequestException: Error
while copying content to a stream.
System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task
serializeToStreamTask, MemoryStream tempBuffer)
Here is what my code looks like. I'm entering the following in parameters:
recipients: list containing "****#gmail.com"
Subject: "Hello"
Body: Html generated via Heml. It compiles in an online editor without problem.
private async Task<bool> SendAsync(List<string> recipients, string subject, string body)
{
var client = new SendGridClient(this.configuration["Sendgrid:ApiKey"]);
var from = new EmailAddress(
this.configuration["Administration:MainEmailAddress"],
this.configuration["Administration:MainEmailName"]);
var tos = await GetRecipientsForEnvironment(recipients);
var message = MailHelper.CreateSingleEmailToMultipleRecipients(
from,
tos,
subject,
"",
body,
false);
var response = await client.SendEmailAsync(message);
return response.StatusCode == HttpStatusCode.Accepted;
}
What is the cause of this error?
It turns out that there is a known bug in Sendgrid. If the html content entered is very big, the correct error message will not be sent. Instead, this error will show up. In my case, my apiKey was not found and I should therefore have gotten an Unauthorized error message. When I changed my html into one much smaller, this gave me the correct error.
Read more about the issue here.
I'm having the same problem, but in my case it's because the email quota has expired (I'm using the free version for testing):
Lib version 9.21.0
On the mentioned thread https://github.com/sendgrid/sendgrid-csharp/issues/648 some people were passing the wrong apiKey, and others exceeding the HTML size.
As said in the thread above, apparently it's a bug in the sendgrid library when interpreting the error.
Therefore, in addition to the problems mentioned (passing the wrong apiKey, exceeding the HTML size) the return “Error while copying content to a stream” is hiding other problems as well (like in my case: quota expired).

The request body did not contain the specified number of bytes

I am calling an API from by C# Windows service. In some cases the following error is being raised.
The request body did not contain the specified number of bytes. Got 101,379, expected 102,044
In the RAW Request captured using fiddler content length as specified.
Content-Length: 102044
In the response from the API I am receiving the following message.
The request body did not contain the specified number of bytes. Got 101,379, expected 102,044
The strange thing for me is that it does not happen for each and every request, it is generated randomly and different points. Code which I am using to get the content length is specified below.
var data = Encoding.ASCII.GetBytes(requestBody); // requestBody is the JSON String
webReqeust.ContentLength = data.Length;
Is it mandatory to provide content length in REST API calls ?
Edit 1:
This is what my sample code looks like for web request
webReqeust = (HttpWebRequest)WebRequest.Create(string.Format("{0}{1}", requestURI, queryString));
webReqeust.Method = RequestMethod.ToString();
webReqeust.Headers.Add("Authorization", string.Format("{0} {1}", token_type, access_token));
webReqeust.Method = RequestMethod.ToString();
webReqeust.ContentType = "application/json";
var data = Encoding.ASCII.GetBytes(requestBody);
webReqeust.ContentLength = data.Length;
using (var streamWriter = new StreamWriter(webReqeust.GetRequestStream()))
{
streamWriter.Write(requestBody);
streamWriter.Flush();
streamWriter.Close();
}
I would suggest maybe instead try using HttpClient as done in the linked post from mjwills here. You don't have to use content length, but it sounds like that is being enforced by the API and ultimately you are trying to post too much.
Otherwise the way I see it is that something is making the request body too large. Is it serialized input data which gets encoded into a byte array? If that is what is happening then perhaps the correct length requirements are not being enforced on the data that composes the request body, and I would suggest inspecting what goes on in the composition of the request body object itself.

Get XML body of a 400 or 500 HTTP XML response

Long story short, I am sending an XML HTTP post request to an application server, and I am getting back a response, also in the form of XML HTTP.
I have a test site available to me which allows me to see what the server's actual response is, visually, in the form of XML, but I cannot access this XML from my C# code the way it is.
The XML coming back from the application server in my test case looks like this:
<Error><Message>StringErrorMessage</Message></Error>
However, I have had no luck accessing this basic XML to retrieve the value of "StringErrorMessage" for the creation of a detailed error report.
... More code above, all wrapped in a try{}...
_response = Serializer.DeserializeObject<T>(ObjectRequest.GetResponse().GetResponseStream());
}
catch (System.Net.WebException exceptionParameter)
{
var response = (HttpWebResponse)exceptionParameter.Response;
string webExceptionStatus = exceptionParameter.Message;
_exception = exceptionParameter;
return false;
}
I have consulted
C# - Getting the response body from a 403 error
and
Get response body on 400 HTTP response in Android?
The first link's solution doesn't seem to give me access to the basic XML as part of any response object's properties. I am almost positive that there must be a byte[] in there somewhere (in the response, or in the exception object) that can be converted into a char[], which can be converted to a string, which can be converted to my XML body, but I have not been able to find it. The second link's solution is not exactly viable for me because I have to get the response body back in the form of XML, as it might not be an error, but an object that must be deserialized. This particular side of things, I cannot change.
Any advice would be very much appreciated.
- Eli
EDIT: Just wanted to clarify that my basic code is working okay for non-error situations, and is deserializing the XML just fine. It's when my code encounters a HTTP 400 or an HTTP 500 error, where accessing the XML from the catch statement becomes a problem, because my code immediately throws an exception.
The body of a HTTP message (the XML in your case) can be retrieved with the GetResponseStream method of the HttpWebResponse object you have. And, since it's a stream, you can for instance read it with a StreamReader, like so:
HttpWebResponse myWebResponse; // Get this from whereever you want
Stream responseStream = myWebResponse.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);
string niceStringForYou = reader.ReadToEnd();
...and from that point on, you can do whatever to it.
If you're absolutely sure it's always gonna be XML you get back from the service, you can probably even use an XmlReader to get XML directly from the stream:
XmlReader foo = XmlReader.Create(responseStream);
Comment to edit: As long as you have the HttpWebResponse object, reading it's response stream (GetResponseStream()) should work. And as you point out in your own code, you can get the HttpWebResponse by looking at (HttpWebResponse)exceptionParameter.Response.

how c# application can retrieve data from php in an efficient manner

Below is my php code which works fine:
<?php
//This script checks the id and code of the already registered user from the database. If correct it returns the other details otherwise the respective error
//starting session
session_start();
//catching data from client
$id=$_POST['id'];
$code=$_POST['code'];
if($id&&$code)//checking if the data is not empty
{
//Connecting to server
($connect=mysqli_connect('localhost','root','','ohhell')) or exit("Connection Failed");
//Selecting user for given id
$result=mysqli_query($connect,"SELECT * FROM users WHERE id='$id'");
//Counting number of rows
$numrows=mysqli_num_rows($result);
if($numrows!=0)
{
//Creating associative array
$row=mysqli_fetch_assoc($result);
//freeing result set
mysqli_free_result($result);
//fetching code from database
$db_code=$row['code'];
$code=md5($code);
//checking if the codes match
if($code==$db_code)
{
//change status
mysqli_query($connect,"UPDATE users SET status='yellow' WHERE id='$id'");
$_SESSION['id']=$row['id'];
$_SESSION['name']=$row['name'];
$_SESSION['location']=$row['location'];
$name=$row['name'];
$location=$row['location'];
//closing connection
mysqli_close($connect);
//returning values to client
exit("$name\n$location");//Successful Login. Client can now create an object and switch the screen
}
else
{
exit("Invalid Player Code");//Unsuccessful Login
}
}
else
exit("Invalid Player ID");//Unsuccessful Login
}
else
exit("Incomplete Details");//Unsuccessful Login
?>
It returns the respective error message or the corresponding details of the player to the c# client. Below is the client side code to receive the data:
WebRequest request = WebRequest.Create(URL);
Stream dataStream;
WebResponse response;
StreamReader reader;
response = request.GetResponse();
dataStream = response.GetResponseStream();
reader = new StreamReader(dataStream);
responseFromServer = reader.ReadToEnd();
reader.Close();
dataStream.Close();
response.Close();
After receiving the data successfully the c# client separates both the data with the help of "\n" and then makes an object of a class and fill the received data to the respective data members of that class.
Now here comes the problem, since I am testing now everything is working fine. But, my question is that, as the data being read is going to be in the form of string, how can I make sure at the client side that the data is actually received successfully and the string that is retrieved actually contains the data and not the error message.
I mean suppose if an internal error occurs while connecting to php or any other network error returns the corresponding error that too would be in the form of string, now how I am going to differentiate that whether the the client app should start separating the data from the string to make the object or it should terminate with an error.
For the errors that I have included in my php I can use respective if() conditions in the c# client but that's not a proper way as the errors are not guaranteed to be limited to the considered errors in the php script. There might be a numerous number of errors that can be returned so what approach should be taken in this case to actually differentiate between the error and real data.
On possible approach is to prepend a signal say "1" to the data before sending it and to test for the signal at the client's side whether the received string starts with a "1". If yes then going for the separation else displaying the corresponding error message. But this approach is also not optimal as in the case the if the error itself starts with 1, it will fail.
So what should be actually done to send data to c# in an optimal way through a php script?
Sorry for the long description! Waiting for assistance!!!
Thanks a million billion trillion...:)
You could use http status codes to indicate error conditions.
It seems currently you're only using 200 OK (by default), but e.g. Invalid Player Code could result in a 401 Unauthorized or 403 Forbidden (if that's meant for some kind of authorization).
On the c# side you'd get that status code via the HttpWebResponse.StatusCode property
You can use HTTP Status codes to determine weather or not an error occured.
You php could set the respective HTTP header if an error occurs during the run of said php script i.e. using http_response_code.
If the Startus code is okay, you can parse the response (as said in the comments, using json) and retrieve the results. In case an error occured in the network communication, you produce the respective error yourself in the client. If an error occured in PHP, you retrieve the error by also parsing the response, thereby retrieving the error message.
Simple Example in PHP
$response = array();
$responseCode = null;
try {
// some code is executed
$responseCode = 200;
}
catch (Exception $e) {
// an error occured and we have to add it to our response
$response['error'] = array('errorcode' => $e->getCode(), 'error' => $e->getMessage());
$responseCode = 500;
}
http_response_code($responseCode);
echo(json_encode($response ));
Now in your c#, simply check the header. If it is 500 (meaning an error occured), json_decode the result and get the error. If it is 200 (meaning OK), json_encode the result. If it is anything else, responde to it according to the linkes HTTP Error Code list.
I refer to the comment-question of Vikas Prasad: "#ReeCube can I achieve all my requirements for the program trying JSON or XML. If yes can you point me to specific tutorials. I have no idea about these two. Also which one to prefer among both?"
JSON or XML are just the way how you transfer the data and how you read them after, you need php and c# to use JSON or XML. I prefer JSON but im not sure how well it's working on C#, I've never tested it.
Here you can find an JSON API for C#.
But first you should add JSON for your PHP-server. On PHP, JSON is very easy to use, there is an json_encode and json_decode function. In your case, you just need the json_encode function, because on the server side, you just want to generate the JSON for the client. How to use json_encode is very well supported by Google and the PHP documentation, but if you have questions, just ask in a comment.
Here is a little example:
<?php
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5);
echo json_encode($arr);
?>

Categories