I am trying to use HttpClient to GET information from Jira, but I am unable to see any of the information. I want to be able to get all the bugs that match certain filters so that I can add them to a table in my program.
I have tried to access Jira with the rest api, but every time I do it says that the issue or project doesn't exist. The thing is that if I enter the URI into the bar at the top of my browser I can see the JSON text that I want. This leads me to believe that the reason my code is not returning these values is because of an authorization issue. I am using basic auth to send my credentials. I also want to add that I used cURL in cmd to test my credentials with basic auth and it worked.
public async Task<JiraModel> GetBugs()
{
using (var client = new HttpClient())
{
string url = "https://myurl.atlassian.net/rest/api/3/project/VCMF";
String username = "username";
String password = "apikey";
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("UTF-8").GetBytes(username + ":" + password));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", "Basic " + encoded);
client.BaseAddress = new Uri("https://myurl.atlassian.net/rest/api/3/project/VCMF");
var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<JiraModel>(content);
}
}
I should be getting the json results in string form by the end of this length of code, but I keep getting a 404 error instead that for this code specifically says "No project could be found with key 'VCMF'".
The issue here is that you're creating the authorization header incorrectly.
The constructor you're using for AuthenticationHeaderValue class takes two arguments: scheme and parameter:
public AuthenticationHeaderValue(string scheme, string parameter)
{
}
The first argument should be the scheme (Basic in this case) and the second, the base64-encoded credentials:
So instead of:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", "Basic " + encoded);
It should be:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encoded);
Hope this helps!
Related
I have some limited skills in c++ and have recently moved in C# (asp.net) and azure Web services. As a PoC I'm trying to make REST calls into PayPal (which I'll need to be using professionally in 3 -6 months).
I've set up my personal PayPal account using the instructions here and I get a bearer token back using curl as described in the link. Awesome.
I'm now trying to do this from .NET Core C# and all I get is a 401 error. I've examined the request and it seems the same as the curl in terms of headers; the base64 encoded credentials I think I'm adding are the same as the ones in the verbose curl log (I examined the two base64 strings by eye) so it must be something I'm doing (or not doing) in the set up of the call. I'm looking for suggestions, pointers, or flat out laughter at the obvious mistake I've made.
I've set up what I believe to be a named client thus:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("PayPal", c =>
{
c.BaseAddress = new Uri("https://api.sandbox.paypal.com/v1/");
c.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept-Language", "en_US");
});
(with all the other stuff that comes free with VS under it omitted for brevity).
I attempt the call thus:
string clientCredString = CLIENTID + ":" + SECRET;
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
var client = _clientFactory.CreateClient("PayPal");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(clientCreds));
var messageBody = new Dictionary<string,string > ();
messageBody.Add("grant_type", "client_credientials");
var request = new HttpRequestMessage(HttpMethod.Get, "oauth2/token")
{
Content = new FormUrlEncodedContent(messageBody)
};
string token;
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<string>(json);
}
else
{
throw new ApplicationException("Well that failed");
}
and get a 401 code for my trouble.
Suggestions for troubleshooting, better methods of doing this and laughter at my foolishness all welcomed.
Update:
I read the documentation, a couple of items stand out to me:
Requires a verb of post.
Uses FormUrlEncodedContent for client credentials.
Basic auth requires username and password (Client Id & Secret)
I believe the syntax should be:
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "...");
request.Content = new Dictionary<string, string>() { "grant_type", "client_credentials" };
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", $"{Encoding.UTF8.GetBytes($"{id}:{secret}")}");
HttpResponseMEssage = response = await client.PostAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
For the benefit of future readers:
It was, as suggested, an encoding problem. The line:
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
needed to be
var clientCreds = System.Text.Encoding.ASCII.GetBytes(clientCredString);
It should also be noted that this particular operation requires a POST not a GET as I was using, but once I started sending properly encoded requests the errors started to make a lot more sense.
myAPI is the name of a web service reference I added to my project and I am trying to send Basic Authorization into the header of the SOAP request using C# but it’s not happening no matter what method I try.
I read a lot online and ended up with the following ways but none seem to work with mine.
What I've tried:
Method 1: no error but does nothing
NetworkCredential netCredential = new NetworkCredential("un", "pw");
Uri uri = new Uri(resol.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
myAPI.Credentials = credentials;
myAPI.PreAuthenticate = true;
Method 2: (FYI ResolvingBinding (below) basically consists of all my method which comes from the service reference myAPI) but the problem is : "client" does not have any definition for InnerChannel and ClientCredential so this method fails too
myAPI.ResolvingBinding client = new myAPI.ResolvingBinding();
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " +
Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" +
client.ClientCredentials.UserName.Password));
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
client.DoSomething();
}
My ultimate end goal is to encode username:password into base 64 which I tried in the second example.
How do I go about resolving this? Please feel free to add a comment/leave a suggestion or point out mistakes I might've overlooked.
This code should add your basic authentication to your header
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(<userId> + ":" +<pw>);
string val = System.Convert.ToBase64String(plainTextBytes);
string textValue = "Basic " + val;
httpRequestProperty.Headers.Add("Authorization", textValue);
public ActionResult getJiraTickets(string username, string password, string jiraUrl)
{
string queryString = jiraUrl + "/rest/api/2/search";
var client = new HttpClient { BaseAddress = new Uri(queryString) };
var credentials = Encoding.ASCII.GetBytes(username + ":" + password);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(credentials));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(queryString).Result;
if (response.IsSuccessStatusCode)
{
// use response
}
}
I get StatusCode: 401, ReasonPhrase: 'Unauthorized' in the response.
The queryString is correct because if I hit that URI using the browser I get a JSON response back.
Username and password are correct as well.
What am I doing wrong in the code?
Thanks
I apologise for not posting this as a comment, I don't have enough rep yet to post a comment.
I know you say your username and password are correct but I would double check them because I have taken your code and run it with my login details and Jira Url and it worked as expected.
So basically, I am trying to connect to a REST API online. Easy enough right?
However I am getting a 401 (Unauthorized) error whenever I try to connect. I am using a C# console app to test this, and I have also tried using PUTMAN (Google Chrome App to view HTTP Requests).
Here is the link to the API I am using: https://community.dynatrace.com/community/display/APMSAASDOC/Login+-+REST+API
I follow all the steps listed. I know my username and password is correct (have logged into the Dynatrace Portal). Does anyone have any ideas what could be wrong? Here is my code below (I have removed the actual username and password for obvious reasons):
static async Task RunAsync()
{
string _user;
string _password;
string _authorizationType;
string _contentType;
string _CredentialsToBase64;
string _url = "https://datafeed-api.dynatrace.com";
_user = "MYUSERNAME";
_password = "MYPASSWORD";
_authorizationType = "basic";
_contentType = "application/json";
_CredentialsToBase64 = System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(_user + ":" + _password));
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_contentType));
client.DefaultRequestHeaders.Add("Authorization", _authorizationType + " " + _CredentialsToBase64);
using (HttpResponseMessage httpResponse = await client.GetAsync("publicapi/rest/v1.0/login?user=MYUSERNAME&password=MYPASSWORD HTTP/1.1"))
{
if (httpResponse.IsSuccessStatusCode)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine(string.Format("Service request failed ({0})", httpResponse.StatusCode));
}
}
}
The HTTP/1.1 string at the end of the client.GetAsync method call is probably being translated into password=MYPASSWORD%20HTTP/1.1 (or similar) which results in the error. Try removing that, and see if it works.
Note: %20 is a urlencoded space
Another Option The answer to this post might be related. To summarize, it appears that formatting a request requires the BaseAddress to have a trailing slash and the GetAsync string to not start with a slash.
Sorry for the headache everyone. The problem was an account issue with the company itself. I contacted them and they noticed the problem was with a migration of accounts from a old to new portal. So there was nothing wrong with our end. Thanks for your help everyone!
Remove " HTTP/1.1" from the end of your GET url, its being added to the end of your password, hence the 401
I'm trying to make a POST call (from a C# WPF app) to a web service on an internal intranet (so I can't give the exact URL sorry), which is basically a url shortening service.
The page gives the following instructions:
In order to use the API service, simply make HTTP POST requests to this URL:
https://...internalAddress.../api/<method>
For example, to create a snip, make an HTTP POST request to:
https://...internalAddress.../api/shorten
with these parameters:
api_key hash of a registered API key
url URL to be shortened
Now I have tried to implement this in a couple of different ways with what I've found via google / here, these are:
1:
string apiKey = "xxxx11112222333";
string urlForShortening = #"http://www.codeproject.com/Tips/497123/How-to-make-REST-requests-with-Csharp";
string destination = #"https://internalurl/api/shorten";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(destination);
httpWebRequest.ContentType = "text/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write("{'api_key': '" + apiKey + "', 'url': '" + urlForShortening + "'}");
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
MessageBox.Show(responseText);
}
2: (Using the rest library created in the article found in the shortening link)
string apiKey = "xxxx11112222333";
string urlForShortening = #"http://www.codeproject.com/Tips/497123/How-to-make-REST-requests-with-Csharp";
string destination = #"https://internalurl/api/shorten";
RestClient client = new RestClient(destination, HttpVerb.POST,
"{'api_key': '" + apiKey + "', 'url': '" + urlForShortening + "'}");
var json = client.MakeRequest();
MessageBox.Show(json);
have also tried feeding in the jsonData in double quotes:
var jsonData = "{\"api_key\": \"" + apiKey + "\", \"url\": \"" + urlForShortening + "\"}";
The result from both methods I always get is:
{"status": 400, "message": "Missing API key"}
Can someone please shed some light on what I'm doing wrong?
From the brief, I think the key may need to be hashed in some form and not sure how to do this.
Turns out my whole implementation was wrong, I was trying to send the data as JSON / using the wrong classes instead of a vanilla HTTP POST.
I used Method 2 found in this article and it worked fine: HTTP request with post
ie.
using (var client = new WebClient())
{
var values = new NameValueCollection();
values["api_key"] = "xxxx11112222333";
values["url"] = #"http://www.codeproject.com";
string destination = #"https://internalurl/api/shorten";
var response = client.UploadValues(destination, values);
var responseString = Encoding.Default.GetString(response);
MessageBox.Show(responseString);
}
Thanks for your help though!
I assume that you are attempting to send json data in the request body for the POST call to the API.
You dont seem to be providing a valid json here though. This is what you are sending now:
{'api_key':'someApiKey'}
{'url':'someUrlForShortening'}
Use a json validator to ensure you have a valid json document before you attempt to send it to the API.
A valid json would be
{
"api_key":"someApiKey",
"url":"someUrlForShortening"
}