Call webservice on outside server from javascript using asp.net and C# - c#

I'm trying to test web service calls using an ASP.NET page that creates a form with username and password fields and a "Submit" button. (Both jQuery and the .js file I'm using are included in script tags in the head element.)
The "Submit" button calls a function created in the C# code behind file that makes a call to a separate JavaScript file.
protected void mSubmit_Click(object sender, EventArgs eventArgs)
{
String authenticate = String.Format("Authentication(\"{0}\",\"{1}\");", this.mUsername.Text,this.mPassword.Text);
Page.ClientScript.RegisterStartupScript(this.GetType(), "ClientScript", authenticate, true);
}
The JavaScript function, Authenticate, makes web service call, using jQuery and Ajax, to a different server, sending JSON parameters and expecting back JSON in response.
function Authentication(uname, pwd) {
//gets search parameters and puts them in json format
var params = '{"Header":{"AuthToken":null,"ProductID":"NOR","SessToken":null,"Version":1},"ReturnAuthentication":true,"Password":"' + pwd + '","Username":"' + uname + '",ā€¯ReturnCredentialsā€¯:false }';
var xmlhttp = $.ajax({
async: false,
type: "POST",
url: 'https://myHost.com/V1/Identity/Authenticate',
data: params,
contentType: 'application/json'
});
alert(xmlhttp.statusText);
alert(xmlhttp.responseText);
return;
}
However, because the web service I'm calling is on a different server than the ASP.NET, C# and JavaScript files, I'm not getting a statusText or responseText alert.
Somehow, nothing is being sent to the web service and I'm not getting anything back, not even an error. I tried putting a function in the beforeSend attribute, but that didn't fire. Is there a special way I need to handle calling an off-server web service?
UPDATE!
At the advice of jjnguy, Janie and Nathan, I'm now trying a server side call to the web service using HttpWebRequest. Using some of jjnguy's code as well as code from this question, I've come up with this.
public static void Authenticate(string pwd, string uname)
{
string ret = null;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://myhost.com/V1/Identity/Authenticate");
request.ContentType = "application/json";
request.Method = "POST";
string data = "{\"Header\":{\"AuthToken\":null,\"ProductID\":\"NOR\",\"SessToken\":null,\"Version\":1},\"ReturnAuthentication\":true,\"Password\":\"" + pwd + "\",\"Username\":\"" + uname + "\",\"ReturnCredentials\":false }'";
byte[] byteData = UTF8Encoding.UTF8.GetBytes(data);
request.ContentLength = byteData.Length;
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (response)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
// Console application output
ret = reader.ReadToEnd();
}
Console.WriteLine(ret);
}
However, I'm getting a (400) Bad Request error from the remote server when I try to get the response from my HttpWebRequest. The value of the Response property of the exception says {System.Net.HttpWebResponse} and the value of the Status property is ProtocolError. I'm pretty sure this is because the URL is using HTTP SSL protocol. What can I do to get around that, other than having the ASP.NET page URL start with HTTPS (not an option)?

Turns out the code that I posted in my update was correct, I just had a typo and one setting incorrect in the data string.
string data = "{\"Header\":{\"AuthToken\":null,\"ProductID\":\"NOR\",\"SessToken\":null,\"Version\":1},\"ReturnAuthentication\":true,\"Password\":\"" + pwd + "\",\"Username\":\"" + uname + "\",\"ReturnCredentials\":true}";

For simplicity's sake, why don't you write the call to the webservice in C# on the Server Side?
You have the same abilities to send requests and get responses in C# as you do with Javascript.
Here is a crack at your function in C#:
public static string Authenticate(string pwd, string uname)
{
HttpWebRequest requestFile = (HttpWebRequest)WebRequest.Create("https://myHost.com/V1/Identity/Authenticate");
requestFile.ContentType = "application/json";
requestFile.Method = "POST";
StreamWriter postBody = new StreamWriter(requestFile.GetRequestStream())
using (postBody) {
postBody.Write("{\"Header\":{\"AuthToken\":null,\"ProductID\":\"NOR\",\"SessToken\":null,\"Version\":1},\"ReturnAuthentication\":true,\"Password\":\"" + pwd + "\",\"Username\":\"" + uname + "\",\"ReturnCredentials\":false }'");
}
HttpWebResponse serverResponse = (HttpWebResponse)requestFile.GetResponse();
if (HttpStatusCode.OK != serverResponse.StatusCode)
throw new Exception("Url request failed. Connection to the server inturrupted");
StreamReader responseStream = new StreamReader(serverResponse.GetResponseStream());
string ret = null;
using (responseStream) {
ret = responseStream.ReadLine();
}
return ret;
}
Disclaimer This has not been tested.

Instead of using the client script to make the request from the server; use server side code to make the request
EDIT to expand answer:
From your web project in visual studio, click add web reference, and point to the service you were originally accessing via your client script: (I believe it was 'https://myHost.com/V1/Identity/Authenticate)
You can now talk to the service using c# code instead of js (and pass in the users provided credentials.)
Also, since the request against the service is coming from a server, rather than a browser; you bypass the cross-domain restrictions that apply.
FURTHER EDIT to show additional technique:
If you don't like the idea of using Visual Studio to generate a service proxy for you, then you can handcraft the request yourself using WebClient or HttpRequest
WebClient:
http://msdn.microsoft.com/en-us/library/system.net.webclient(VS.80).aspx
HttpWebRequest:
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest(VS.80).aspx

Seems like you're running into the same origin policy
http://en.wikipedia.org/wiki/Same_origin_policy
I believe there are ways to circumvent it, but I think the other posters are right. On the server, write methods that use a HttpWebRequest to call the web service, and then use JavaScriptSerializer to parse out the JSON. I spent most of the afternoon researching this cause I'll have to write something similar myself.
>>>> Nathan
P.S. I like #Janie's plan better... Can you do that with a web service that returns JSON as well as one that would pass back XML?

Related

500 Error when trying to access SAP Service Layer through C# Application

I'm writing a C# application that interacts with the SAP B1 Service Layer and I'm attempting to Login through a HTTP POST call, using JSON for the body.
If I send over the login request using POSTMAN, it logs me in fine and I receive the session cookie as expected. When I send over the JSON through my C# app, I receive a 500 error - I have absolutely no idea why. I have another method that sends over a request for a specific item which returns a 401 - Unauthorized so I know it's hitting the Service Layer.
Below is the code in the method so far.
var request = (HttpWebRequest)WebRequest.Create("https://172.16.101.38:50000/b1s/v1/Login");
request.ContentType = "application/json";
request.Method = "POST";
request.ServerCertificateValidationCallback = AcceptAllCertifications;
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(new
{
UserName = "user",
Password = "pass",
CompanyDB = "db"
});
streamWriter.Write(json);
}
var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
And the "AcceptAllCertifications" method is as follows:
public bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification,
System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true;
}
If I don't include this method, I get a "Could not establish a trust relationship for the SSL/TLS secure channel..." error. In POSTMAN, I have to ensure "SSL Certificate verification" is OFF so that I can access the Service Layer.
I know the JSON produced is valid as I've tested it in POSTMAN.
Anyone run into this before and managed to figure out how to rectify this?
Add below line in your request.
request.ServicePoint.Expect100Continue = false;
Here is a link to the solution I've had to implement:
SAP Consume OData Services
So instead of trying to access everything through JSON, I've had to take some code from here which creates a Service Layer instance and logs in through a method implemented in there.
Very fiddly to get working at first, but once it is working it's pretty solid.

pass string from C# Windows Form Application to php webpage

How can I pass some data to a webpage from C#.net? I'm currently using this:
ProcessStartInfo p1 = new ProcessStartInfo("http://www.example.com","key=123");
Process.Start(p1);
but how can I access it from PHP? I tried:
<?php echo($_GET['key']); ?>
but it prints nothing.
Try passing it with the url itself
ProcessStartInfo p1 = new ProcessStartInfo("http://timepass.comule.com?key=123","");
Process.Start(p1);
you should put the key parameter as a query string :
ProcessStartInfo p1 = new ProcessStartInfo("http://timepass.comule.com?key=123");
I would suggest using the HttpWebRequestClass.
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
This way, you would also have the ability to post data to your page, add auth parameters, cookies etc - in case you might need it.
I'm not sure if this matters in your particular setup, passing data thru the query string is not secure. But if security is an issue as well, I would POST the data thru an SSL connection.
Update:
so if you POST'ed data to your php page like so:
string dataToSend = "data=" + HttpUtility.UrlEncode("this is your data string");
var dataBytes = System.Text.Encoding.UTF8.GetBytes(dataToSend);
HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://localhost/yourpage.php");
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = dataBytes.Length;
req.Method = "POST";
using (var stream = req.GetRequestStream())
{
stream.Write(dataBytes, 0, dataBytes.Length);
}
// -- execute request and get response
HttpWebResponse resp = (HttpWebResponse) req.GetResponse();
if (resp.StatusCode == HttpStatusCode.OK)
Console.WriteLine("Hooray!");
you can retrieve it by using the following code in your php page:
echo $_POST["data"])
Update 2:
AFAIK, ProcessStartInfo/Process.Start() actually starts a process - in this case, I think it will start your browser. The second parameter is the command line arguments. This information is used by programs so they know how to behave when started (hidden, open a default document etc). Its not related to the Query string in anyway. if you prefer to use Process.Start(), then try something like this:
ProcessStartInfo p1 = new ProcessStartInfo("iexplore","http://google.com?q=test");
Process.Start(p1);
If you run that, it will open internet explorer and open google with test on the search box. If that were you're page, you could access "q" by calling:
echo $_GET["q"])
In my applications i used different method i.e using webClient i done it
WebClient client1 = new WebClient();
string path = "dtscompleted.php";//your php path
NameValueCollection formData = new NameValueCollection();
byte[] responseBytes2=null;
formData.Add("key", "123");
try
{
responseBytes2 = client1.UploadValues(path, "POST", formData);
}
catch (WebException web)
{
//MessageBox.Show("Check network connection.\n"+web.Message);
}

Why my Http client making 2 requests when I specify credentials?

I created RESTful webservice (WCF) where I check credentials on each request. One of my clients is Android app and everything seems to be great on server side. I get request and if it's got proper header - I process it, etc..
Now I created client app that uses this service. This is how I do GET:
// Create the web request
var request = WebRequest.Create(Context.ServiceURL + uri) as HttpWebRequest;
if (request != null)
{
request.ContentType = "application/json";
// Add authentication to request
request.Credentials = new NetworkCredential(Context.UserName, Context.Password);
// Get response
using (var response = request.GetResponse() as HttpWebResponse)
{
// Get the response stream
if (response != null)
{
var reader = new StreamReader(response.GetResponseStream());
// Console application output
var s = reader.ReadToEnd();
var serializer = new JavaScriptSerializer();
var returnValue = (T)serializer.Deserialize(s, typeof(T));
return returnValue;
}
}
}
So, this code get's my resource and deserializes it. As you see - I'm passing credentials in my call.
Then when debugging on server-side I noticed that I get 2 requests every time - one without authentication header and then server sends back response and second request comes bach with credentials. I think it's bad for my server - I'd rather don't make any roundtrips. How should I change client so it doesn't happen? See screenshot of Fiddler
EDIT:
This is JAVA code I use from Android - it doesn't do double-call:
MyHttpResponse response = new MyHttpResponse();
HttpClient client = mMyApplication.getHttpClient();
try
{
HttpGet request = new HttpGet(serviceURL + url);
request.setHeader(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
request.addHeader("Authorization", "Basic " + Preferences.getAuthorizationTicket(mContext));
ResponseHandler<String> handler = new BasicResponseHandler();
response.Body = client.execute(request, handler);
response.Code = HttpURLConnection.HTTP_OK;
response.Message = "OK";
}
catch (HttpResponseException e)
{
response.Code = e.getStatusCode();
response.Message = e.getMessage();
LogData.InsertError(mContext, e);
}
The initial request doesn't ever specify the basic header for authentication. Additionally, since a realm is specified, you have to get that from the server. So you have to ask once: "hey, I need this stuff" and the server goes "who are you? the realm of answering is 'secure area'." (because realm means something here) Just because you added it here:
request.Credentials = new NetworkCredential(Context.UserName, Context.Password);
doesn't mean that it's going to be for sure attached everytime to the request.
Then you respond with the username/password (in this case you're doing BASIC so it's base64 encoded as name:password) and the server decodes it and says "ok, you're all clear, here's your data".
This is going to happen on a regular basis, and there's not a lot you can do about it. I would suggest that you also turn on HTTPS since the authentication is happening in plain text over the internet. (actually what you show seems to be over the intranet, but if you do go over the internet make it https).
Here's a link to Wikipedia that might help you further: http://en.wikipedia.org/wiki/Basic_access_authentication
Ok, I got it. I manually set HttpHeader instead of using request.Credentials
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(Context.UserName + ":" + Context.Password)));
Now I see only single requests as expected..
As an option you can use PreAuthenticate property of HttpClientHandler. This would require a couple of lines more
var client = new HttpClient(new HttpClientHandler
{
Credentials = yourCredentials,
PreAuthenticate = true
});
With using this approach, only the first request is sent without credentials, but all the rest requests are OK.

getting this error: "The remote server returned an error: (422) Unprocessable Entity." when doing post from C# to RoR

This code is for an outlook plugin. We're trying to POST to a page and are getting this error:
The remote server returned an error: (422) Unprocessable Entity.
The C# code is here:
webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
Byte[] postData = asciiEncoding.GetBytes("email=e2#email.com&password=hunter2");
char[] resultHTML = asciiEncoding.GetChars(webClient.UploadData("http://url", "POST", postData));
string convertedResultHTML = new string(resultHTML);
Any idea what could be causing this?
POST data must be encoded prior to be sent out on the wire as ASCII, if you are sending character not in the ASCII range. You should try something like:
Byte[] postData = asciiEncoding.GetBytes(HttpUtility.UrlEncode("email=e2#email.com&password=hunter2"));
Because of its limited functionality, I avoid using WebClient and use WebRequest instead. The code below:
does not expect an HTTP 100 status code to be returned,
creates a CookieContainer to store any cookies we pick up,
sets the Content Length header, and
UrlEncodes each value in the post data.
Give the following a try and see if it works for you.
System.Net.ServicePointManager.Expect100Continue = false;
System.Net.CookieContainer cookies = new System.Net.CookieContainer();
// this first request just ensures we have a session cookie, if one exists
System.Net.WebRequest req = System.Net.WebRequest.Create("http://localhost/test.aspx");
((System.Net.HttpWebRequest)req).CookieContainer = cookies;
req.GetResponse().Close();
// this request submits the data to the server
req = System.Net.WebRequest.Create("http://localhost/test.aspx");
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
((System.Net.HttpWebRequest)req).CookieContainer = cookies;
string parms = string.Format("email={0}&password={1}",
System.Web.HttpUtility.UrlEncode("e2#email.com"),
System.Web.HttpUtility.UrlEncode("hunter2"));
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(parms);
req.ContentLength = bytes.Length;
// perform the POST
using (System.IO.Stream os = req.GetRequestStream())
{
os.Write(bytes, 0, bytes.Length);
}
// read the response
string response;
using (System.Net.WebResponse resp = req.GetResponse())
{
if (resp == null) return;
using (System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream()))
{
response = sr.ReadToEnd().Trim();
}
}
// the variable response holds the results of the request...
Credits: Hanselman, Simon (SO Question)
This is the RoR application telling you that you have not formed a request that it can handle; the destination script exists (otherwise you'd see a 404), the request is being handled (otherwise you'd get a 400 error) and it's been encoded correctly (or you'd get a 415 error) but the actual instruction can't be carried out.
Looking at it, you seem to be loading some email information. The RoR application could be telling you that the username and password is wrong, or that the user doesn't exist, or something else. It's up to the RoR application itself.
I think the code itself is good; it's just that the app at the other end isn't happy about doing what you ask it. Are you missing something else in the request information, like a command? (eg command=getnetemails&email=e2#email.com&password=hunter2) Are you sure the email/password combination you are passing is good?
see here for more on the 422 error.
Add the below line above your code.
System.Net.ServicePointManager.Expect100Continue = false;
Are you trying to access an authentication required page?
it was solved by returning xml instead of just unstructured text on the RoR side

How do you programmatically fill in a form and 'POST' a web page?

Using C# and ASP.NET I want to programmatically fill in some values (4 text boxes) on a web page (form) and then 'POST' those values. How do I do this?
Edit: Clarification: There is a service (www.stopforumspam.com) where you can submit ip, username and email address on their 'add' page. I want to be able to create a link/button on my site's page that will fill in those values and submit the info without having to copy/paste them across and click the submit button.
Further clarification: How do automated spam bots fill out forms and click the submit button if they were written in C#?
The code will look something like this:
WebRequest req = WebRequest.Create("http://mysite/myform.aspx");
string postData = "item1=11111&item2=22222&Item3=33333";
byte[] send = Encoding.Default.GetBytes(postData);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = send.Length;
Stream sout = req.GetRequestStream();
sout.Write(send, 0, send.Length);
sout.Flush();
sout.Close();
WebResponse res = req.GetResponse();
StreamReader sr = new StreamReader(res.GetResponseStream());
string returnvalue = sr.ReadToEnd();
You can use the UploadValues method on WebClient - all it requires is passing a URL and a NameValueCollection. It is the easiest approach that I have found, and the MS documentation has a nice example:
http://msdn.microsoft.com/en-us/library/9w7b4fz7.aspx
Here is a simple version with some error handling:
var webClient = new WebClient();
Debug.Info("PostingForm: " + url);
try
{
byte [] responseArray = webClient.UploadValues(url, nameValueCollection);
return new Response(responseArray, (int) HttpStatusCode.OK);
}
catch (WebException e)
{
var response = (HttpWebResponse)e.Response;
byte[] responseBytes = IOUtil.StreamToBytes(response.GetResponseStream());
return new Response(responseBytes, (int) response.StatusCode);
}
The Response class is a simple wrapper for the response body and status code.
View the source of the page and use the WebRequest class to do the posting. No need to drive IE. Just figure out what IE is sending to the server and replicate that. Using a tool like Fiddler will make it even easier.
I had a situation where I needed to post free text from a html textarea programmatically and I had issues where I was getting <br /> in my param list i was building.
My solution was a replace of the br tags with linebreak characters and htmlencoding just to be safe.
Regex.Replace( HttpUtility.HtmlDecode( test ), "(<br.*?>)", "\r\n" ,RegexOptions.IgnoreCase);
Where you encode the string:
Encoding.Default.GetBytes(postData);
Use Ascii instead for the google apis:
Encoding.ASCII.GetBytes(postData);
this makes your request the same as and equivalent "curl --data "..." [url]" request
you can send a post/get request with many ways. Different types of library is there to help.
I found it is confusing to choose which one I should use and what are the differences among them.
After surfing stack overflow this is the best answer I found. this thread explains all
https://stackoverflow.com/a/4015346/1999720

Categories