WebClient NotFound error but working with HttpWebRequest/Response - windows-phone-7

In my WinPhone app I'm accessing a REST service.
At the beginnings I was using this code:
WebClient wc = new WebClient();
wc.Credentials = credentials;
wc.Headers["App-Key"] = appKey;
wc.DownloadStringCompleted +=
(o, args) => MessageBox.Show(args.Error == null ? "OK" : "Error");
wc.DownloadStringAsync(uri);
but it suddenly stopped working returning me a "The remote server returned an error: NotFound" error. After a google session and some clicks in the control panel, I didn't get it to work.
I decided to try this other way:
HttpWebRequest request = HttpWebRequest.CreateHttp(uri);
request.Credentials = credentials;
request.Headers["App-Key"] = appKey;
request.BeginGetResponse(asResult =>
{
var response = request.EndGetResponse(asResult) as HttpWebResponse;
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseString = reader.ReadToEnd();
Dispatcher.BeginInvoke(
() => MessageBox.Show(response.StatusCode.ToString()));
}, null);
and it works.
I also tried to run the first snipped pointing the URI to google's home page and it works (I had to remove the credentials, of course).
Can anyone explain what's going on?
UPDATE
I managed to get it working by replacing the
wc.Credentials = new NetworkCredentials(username, password);
with
wc.Headers["Authorization"] = "Basic someBase64encodedString";
but i still wonder what happened and which are the differences between the first and the second line.
PS: the test URI is: https://api.pingdom.com/api/2.0/checks but you will need an app-key from them.

When using the Credentials property, the HttpWebRequest implementation will wait the challenge response from server before to send the 'Authorization' header value.
But this can be an issue in some cases, so you have to force Basic authentication by providing directly the Authorization header.
Example when using a REST Client library like Spring.Rest :
RestTemplate template = new RestTemplate("http://example.com");
template.RequestInterceptors.Add(new BasicSigningRequestInterceptor("login", "password"));
string result = template.GetForObject<string>(uri);

Related

Get "API key is missing" error when querying account details to Mailchimp API 3.0 using RestSharp

When using RestSharp to query account details in your MailChimp account I get a "401: unauthorized" with "API key is missing", even though it clearly isn't!
We're using the same method to create our RestClient with several different methods, and in all requests it is working flawlessly. However, when we're trying to request the account details, meaning the RestRequest URI is empty, we get this weird error and message.
Examples:
private static RestClient CreateApi3Client(string apikey)
{
var client = new RestClient("https://us2.api.mailchimp.com/3.0");
client.Authenticator = new HttpBasicAuthenticator(null, apiKey);
return client;
}
public void TestCases() {
var client = CreateApi3Client(_account.MailChimpApiKey);
var req1 = new RestRequest($"lists/{_account.MailChimpList}/webhooks", Method.GET);
var res1 = client.Execute(req1); // works perfectly
var req2 = new RestRequest($"automations/{account.MailChimpTriggerEmail}/emails", Method.GET);
var res2 = client.Execute(req2); // no problem
var req3 = new RestRequest(Method.GET);
var res3 = client.Execute(req3); // will give 401, api key missing
var req4 = new RestRequest(string.Empty, Method.GET);
var res4 = client.Execute(req4); // same here, 401
}
When trying the api call in Postman all is well. https://us2.api.mailchimp.com/3.0, GET with basic auth gives me all the account information and when debugging in c# all looks identical.
I'm trying to decide whether to point blame to a bug in either RestSharp or MailChimp API. Has anyone had a similar problem?
After several hours we finally found what was causing this..
When RestSharp is making the request to https://us2.api.mailchimp.com/3.0/ it's opting to omit the trailing '/'
(even if you specifically add this in the RestRequest, like: new RestRequest("/", Method.GET))
so the request was made to https://us2.api.mailchimp.com/3.0
This caused a serverside redirect to 'https://us2.api.mailchimp.com/3.0/' (with the trailing '/') and for some reason this redirect scrubbed away the authentication header.
So we tried making a
new RestRequest("/", Method.GET)
with some parameters (req.AddParameter("fields", "email")) to make it not scrub the trailing '/', but this to was failing.
The only way we were able to "fool" RestSharp was to write it a bit less sexy like:
new RestRequest("/?fields=email", Method.GET)

WebRequest returns 404 when switching to SSL

Having built an app using PCL method in Xamarin and have had it working 100% using standard HTTP I now changed the remote test server to use SSL with self signed certs.
The app contacts a custom API for logging onto a server and querying for specific data.
I've changed the app to look at SSL now and initially got an error regarding Authentication not working or something but turned off SSL related errors for testing using:
ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
in my AppDelegate files FinishedLaunching method which got over that error.
I'm now getting a 404 / protocol error when trying to do my Login POST to the given URL.
I am using HttpWebRequest for my RESTful calls and this works fine if I change back to plain http.
Not sure why but some articles suggested using ModernHttpClient, which I did. I imported the component (also added the package using NuGet) to no avail.
Am I missing something else that I should be configuring in my code related to httpwebresponse when contacting the SSL server or is this component simply incapable of speaking to an SSL server?
My login function is as follows (Unrelated code removed/obfuscated):
public JsonUser postLogin(string csrfToken, string partnerId, string username, string password){
string userEndPoint = SingletonAppSettngs.Instance ().apiEndPoint;
userEndPoint = userEndPoint.Replace ("druid/", "");
var request = WebRequest.CreateHttp(string.Format(this.apiBaseUrl + userEndPoint + #"user/login.json"));
// Request header collection set up
request.ContentType = "application/json";
request.Headers.Add ("X-CSRF-Token", csrfToken);
// Add other configs
request.Method = "POST";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json_body_content = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}";
streamWriter.Write(json_body_content);
streamWriter.Flush();
streamWriter.Close();
}
try{
HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse();
using (StreamReader reader = new StreamReader (httpResponse.GetResponseStream ())) {
var content = reader.ReadToEnd ();
content = content.Replace ("[],", "null,");
content = content.Replace ("[]", "null");
if (content == null) {
throw new Exception ("request_post_login - content is NULL");
} else {
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.NullValueHandling = NullValueHandling.Ignore;
JsonUser deserializedUser = JsonConvert.DeserializeObject<JsonUser>(content, jss);
if(content.Contains ("Hire company admin user")){
deserializedUser.user.roles.__invalid_name__5 = "Hire company admin user";
deserializedUser.user.roles.__invalid_name__2 = "authenticated user";
}
return deserializedUser;
}
}
}catch(Exception httpEx){
Console.WriteLine ("httpEx Exception: " + httpEx.Message);
Console.WriteLine ("httpEx Inner Exception: " + httpEx.InnerException.Message);
JsonUser JsonUserError = new JsonUser ();
JsonUserError.ErrorMessage = "Error occured: " + httpEx.Message;
return JsonUserError;
}
}
When making a Web Request using ModernHttpClient, I generally follow the pattern below. Another great library created by Paul Betts is refit, and can be used to simplify rest calls.
using (var client = new HttpClient(new NativeMessageHandler(false, false)))
{
client.BaseAddress = new Uri(BaseUrl, UriKind.Absolute);
var result = await Refit.RestService.For<IRestApi>(client).GetData();
}
The second parameter for NativeMessageHandler should be set to true if using a customSSLVerification.
Here's a look at IRestApi
public interface IRestApi
{
[Get("/foo/bar")]
Task<Result> GetMovies();
}
Number of things I had to do to get this to work.
The Self Signed Cert had to allow TLS 1.2
As the API is Drupal based, HTTPS had to be enabled on the server and a module installed to manage the HTTP specific pages.

How to export a Confluence "Space" to PDF using remote API

How can I export a Confluence 'space' as a pdf? It looks like it might still be supported in Confluence 5.0 using the XML-RPC API. I cannot find an example of what to call, though.
https://developer.atlassian.com/display/CONFDEV/Remote+API+Specification+for+PDF+Export#RemoteAPISpecificationforPDFExport-XML-RPCInformation
That link says calls should be prefixed with pdfexport, but then doesn't list any of the calls or give an example.
This works using Bob Swift's SOAP library ('org.swift.common:confluence-soap:5.4.1'). I'm using this in a gradle plugin, so you'll need to change a few things
void exportSpaceAsPdf(spaceKey, File outputFile) {
// Setup Pdf Export Service
PdfExportRpcServiceLocator serviceLocator = new PdfExportRpcServiceLocator()
serviceLocator.setpdfexportEndpointAddress("${url}/rpc/soap-axis/pdfexport")
serviceLocator.setMaintainSession(true)
def pdfService = serviceLocator.getpdfexport()
// Login
def token = pdfService.login(user, password)
// Perform Export
def pdfUrl = pdfService.exportSpace(token, spaceKey)
// Download Pdf
HttpClient client = new DefaultHttpClient();
HttpGet httpget = new HttpGet(pdfUrl)
httpget.addHeader(
BasicScheme.authenticate(
new UsernamePasswordCredentials(user,password),"UTF-8", false))
HttpResponse response = client.execute(httpget)
HttpEntity entity = response.getEntity()
if (entity != null) {
InputStream inputStream = entity.getContent()
FileOutputStream fos = new FileOutputStream(outputFile)
int inByte
while ((inByte = inputStream.read()) != -1)
fos.write(inByte)
inputStream.close()
fos.close()
} else {
throw new GradleException("""Cannot Export Space to PDF:
Space: ${spaceKey}
Dest: ${outputFile.absolutePath}
URL: ${pdfUrl}
Status: ${response.getStatusLine()}
""")
}
}
I know this is a PHP example, not Ruby, but you can check out the XML-RPC example in VoycerAG's PHP project on Github at https://github.com/VoycerAG/confluence-xmlrpc-pdf-export/blob/master/src/Voycer/Confluence/Command/PdfExportCommand.php ... hope it helps.
Basically you just need to make a call to the login method and user the authentication token returned to make a call to the exportSpace method. That in turn gives you back a URL which an authenticated user can then download the PDF from.
Turns out the soap API is the only currently available api for exporting a space
Using the Savon library in Ruby here:
require 'savon'
# create a client for the service
# http://<confluence-install>/rpc/soap-axis/pdfexport?wsdll
client = Savon.client(wsdl: 'https://example.atlassian.net/wiki/rpc/soap-axis/pdfexport?wsdl', read_timeout: 200)
# call the 'findUser' operation
response = client.call(:login, message: {username: "user", password: "pass"})
token = response.body[:login_response][:login_return]
response = client.call(:export_space, message:{token: token, space_key: "SPACE KEY"})

(401) Unauthorized Error When Calling Web API from a Console Application

When I call my WEB API from my Console Application, I encounter:
The remote server returned an error: (401) Unauthorized.
This application runs in Interanet (Windows Authentication)
Uri uri = new Uri("http://myServer/api/main/foo");
WebClient client = new WebClient();
client.Credentials = CredentialCache.DefaultCredentials;
using (Stream data = client.OpenRead(uri))
{
using (StreamReader sr = new StreamReader(data))
{
string result = sr.ReadToEnd();
Console.WriteLine(result);
}
}
Updated
If I replace
client.Credentials = CredentialCache.DefaultCredentials;
with this line
client.Credentials = new NetworkCredential( username, password);
it works fine but I need the current credential to be set automatically.
Any idea?
Thanks in advance ;)
You use the default windows credentials here
client.Credentials = CredentialCache.DefaultCredentials;
Specify the credential that you want to authenticate using the following code:
var credential = new NetworkCredential(, , );
serverReport.ReportServerCredentials.NetworkCredentials = credential;
following line is the cause of this behaviour :
client.Credentials = CredentialCache.DefaultCredentials;
Actually this line assigns the credentials of the logged in user or the user being impersonated ( which is only possible in web applications ) , so what I believe is that you have to provide credentials explicitly (http://msdn.microsoft.com/en-us/library/system.net.credentialcache(v=vs.110).aspx) , thanks.

SignalR .Net client fails to connect (upd: how to set auth. cookie?)

This thing is dragging me nuts.
I have a .net 4.0 console app and I have an MVC web app.
javascript clients can connect and talk to the server - no problems here...
but my .net client throws System.AggregateException with InnerException = "Unexpected character encountered while parsing value: <. Path...
so I created an empty MVC3 app, added SignalR libraries, and .net client surprisingly connects to that. But for some reason it doesn't to the other one. I've checked everything, both MVC3 apps, both use the same SignalR libs, the same NewtonsoftJson... I thought it must be something with the routing, I guess no - js client works.
var connection = new HubConnection("http://localhost:58746");
var hubProxy = connection.CreateProxy("myProxy");
connection.Start().Wait() // it fails here on Wait
What could it be?
UPD: I have figured... it's because FormsAuthentication on the server. Now is there any way to feed .ASPXAUTH cookie to SignalR so it can connect to the server?
The solution by Agzam was really helpful, but if anyone else uses the posted code it is critical that you close the HttpWebResponse before exiting GetAuthCookie. If you don't you will find that whenever you use SignalR to invoke a method on the server, the request (under most circumstances) will queue indefinitely on the client and will neither succeed nor fail.
Note. The original code worked in the test environment when everything was on my PC, but failed consistently when the website was hosted on a remote server.
here is the modified code I ended up using
private Cookie GetAuthCookie(string user, string pass)
{
var http = WebRequest.Create(_baseUrl+"Users/Login") as HttpWebRequest;
http.AllowAutoRedirect = false;
http.Method = "POST";
http.ContentType = "application/x-www-form-urlencoded";
http.CookieContainer = new CookieContainer();
var postData = "UserName=" + user + "&Password=" + pass + "&RememberMe=true&RememberMe=false&ReturnUrl=www.google.com";
byte[] dataBytes = System.Text.Encoding.UTF8.GetBytes(postData);
http.ContentLength = dataBytes.Length;
using (var postStream = http.GetRequestStream())
{
postStream.Write(dataBytes, 0, dataBytes.Length);
}
var httpResponse = http.GetResponse() as HttpWebResponse;
var cookie = httpResponse.Cookies[FormsAuthentication.FormsCookieName];
httpResponse.Close();
return cookie;
}
its a very minor change , but it will save you a lot of debugging time.
Ok... stupid me... SignalR failed to connect because it cannot breach server's Forms authentication. So what needed to be done is to get the auth cookie and stick it to the HubConnection.CookieContainer...
so I wrote this method method to login with a username and get the cookie:
private Cookie GetAuthCookie(string user, string pass)
{
var http = WebRequest.Create(_baseUrl+"Users/Login") as HttpWebRequest;
http.AllowAutoRedirect = false;
http.Method = "POST";
http.ContentType = "application/x-www-form-urlencoded";
http.CookieContainer = new CookieContainer();
var postData = "UserName=" + user + "&Password=" + pass + "&RememberMe=true&RememberMe=false&ReturnUrl=www.google.com";
byte[] dataBytes = System.Text.Encoding.UTF8.GetBytes(postData);
http.ContentLength = dataBytes.Length;
using (var postStream = http.GetRequestStream())
{
postStream.Write(dataBytes, 0, dataBytes.Length);
}
var httpResponse = http.GetResponse() as HttpWebResponse;
var cookie = httpResponse.Cookies[FormsAuthentication.FormsCookieName];
httpResponse.Close();
return cookie;
}
And used it like this:
var connection = new HubConnection(_baseUrl)
{
CookieContainer = new CookieContainer()
};
connection.CookieContainer.Add(GetAuthCookie(_user, _pass));
Works perfectly!
Just use this for reading cookies:
var cookie = response.Cookies[".AspNet.ApplicationCookie"];

Resources