Making POST API request via c# with hashed API Key - c#

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"
}

Related

HttpClient not returning json value of URI

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!

Why am I not being able to add basic Authorization in SOAP header?

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);

"{\"error\":\"unsupported_grant_type\",\"error_description\":\"Grant Type is NULL\"}"

I'm getting a return response of:
"{\"error\":\"unsupported_grant_type\",\"error_description\":\"Grant
Type is NULL\"}"
I have tried a few different ways to build the JSON string it wants but I'm not having any luck. I seen a few samples where people got it to work but they must have changed it.
Here is my code:
public string PostPayment([FromBody]Payment_DTO payment)
{
//Request token
var client = new RestClient(_EndPoint);
var request = new RestRequest(Method.POST);
string json = BuildTokenRequest();
string svcCredentials =
Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(_UserName + ":" +
_Password));
request.AddHeader("cache-control", "no-cache");
request.AddHeader("Authorization", "Basic " + svcCredentials);
request.AddHeader("content-type", "application/x-www-form-
urlencoded");
request.AddParameter("application/json", json,
ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return response.Content.ToString();
}
I think the issue is in my json builder function its self. I'm sure I'm doing something wrong in here:
public string BuildTokenRequest()
{
//string request = "grant_type=" + _Password + "&client_id=" + _UserName + "&client_secret=" + _Password + "$username=" + _UserName + "&password=" + _Password;
string request = "client_id="+ _UserName + "secret=" + _Password + "grant_type=client_credentials";
return JsonConvert.SerializeObject(request);
}
Your code doesn't produce anything resembling a worthwhile JSON object, it just serialises a simple string to JSON. When you do that, all you really get out of the other end is just... a simple string - because that's valid JSON already. The serialiser has no way to know you intended these to be separate fields, it just sees one long piece of text. It has no way to ascribe any extra meaning to it.
For example:
string _UserName = "123";
string _Password = "abc";
string request = "client_id=" + _UserName + "secret=" + _Password + "grant_type=client_credentials";
Console.WriteLine(JsonConvert.SerializeObject(request));
will output just
"client_id=123secret=abcgrant_type=client_credentials"
Demo: https://dotnetfiddle.net/DTDDjI
Now as I said, technically it's valid JSON, but it's highly unlikely to be what the remote server is expecting - again it will not know that it needs to parse that string and extract the values from it. I can't the spec of your remote API (as you haven't told us what endpoint you're calling or linked us to any documentation) but I imagine will be expecting an object with the values in separate fields. To get this kind of thing from the C# you need to start with a C# object to begin with:
For example:
string _UserName = "123";
string _Password = "abc";
var request = new { client_id = _UserName, secret = _Password, grant_type = "client_credentials" };
Console.WriteLine(JsonConvert.SerializeObject(request));
will output
{"client_id":"123","secret":"abc","grant_type":"client_credentials"}
Demo: https://dotnetfiddle.net/wCpMhV
Note the use of an anonymous object containing discrete fields to pass to the JSON serialiser, and then the object containing discrete fields which is the result.
As I say I can't check to see if this is precisely the layout which the remote server expects, but you should be able to check the documentation to see if that's correct or not. And if not, you now hopefully understand how to correctly generate a useful JSON object which would match the specification.
Another point. This line of your code:
request.AddHeader("content-type", "application/x-www-form-urlencoded");
is unnecessary. You can remove it because
a) it sets the wrong content-type for a request containing JSON and
b) the line below it will (as per the RestSharp docs set both the correct content-type header and the JSON body content in one call.
In my case, the problem was that my request was incorrectly encoded for the "content-type" : "application/x-www-form-urlencoded".
Based on this solution, I constructed my request.httpBody differently and it worked.

AWS Item LookUp URL Signature

I'm new to AWS, I'm trying to lookup an item by UPC within my UWP App. I'm having issues adding the signature to the request URL. I keep receiving "The request signature we calculated does not match the signature you provided."
Has any had any experience with making calls to Amazon? There api documentation isn't really aimed towards C# in most places.
public async void ItemLookup(string itemID)
{
Dictionary<string, string> fields = new Dictionary<string, string>();
fields.Add("AssociateTag", associateTag);
fields.Add("Condition", "All");
fields.Add("IdType", "UPC");
fields.Add("ItemId", itemID);
fields.Add("Operation", "ItemLookup");
fields.Add("ResponseGroup", "Large");
fields.Add("SearchIndex", "All");
fields.Add("Service", "AWSECommerceService");
// fields.Add("Timestamp", Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:sssZ")));
// Build Url for signing
string url = string.Empty;
foreach (var pair in fields)
{
url += "&" + pair.Key + "=" + pair.Value;
}
url = Uri.EscapeUriString(endpoint
+ "AWSAccessKeyId=" + accessKeyId
+ url);
// Add Timestamp
url += "&Timestamp=" + Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture));
// URL http://webservices.amazon.co.uk/onca/xml?AWSAccessKeyId=REMOVED&AssociateTag=REMOVED&Condition=All&IdType=UPC&ItemId=786936724943&Operation=ItemLookup&ResponseGroup=Large&SearchIndex=All&Service=AWSECommerceService&Timestamp=2017-11-22T11%3A34%3A42.602Z
// SCRATCHPAD http://webservices.amazon.co.uk/onca/xml?AWSAccessKeyId=REMOVED&AssociateTag=REMOVED&Condition=All&IdType=UPC&ItemId=786936724943&Operation=ItemLookup&ResponseGroup=Large&SearchIndex=All&Service=AWSECommerceService&Timestamp=2017-11-22T09%3A20%3A35.000Z
// &Signature=Lvqlenpx0wos4Hg6ZzSNHqOc1QwktXgt8nFHBTfTON4%3D
Byte[] secretBytes = UTF8Encoding.UTF8.GetBytes(secretKey);
HMACSHA256 hmac = new HMACSHA256(secretBytes);
Byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(url);
Byte[] hash = hmac.ComputeHash(dataBytes);
String signature = Convert.ToBase64String(hash);
// Full URL
string requestURL = url + "&Signature=" + signature;
HttpClient httpClient = new HttpClient();
HttpResponseMessage responseMessage = await httpClient.GetAsync(requestURL);
var response = await responseMessage.Content.ReadAsStringAsync();
}
}
I have the url from amazon scratchpad commented out against my own url, they are aligned but I'm not 100% sure if I'm working on the signature correctly.
Any help would be greatly appreciated
Kevin
I haven't tested the following yet. But there are doc about what you ruquest. Have you checked the following to create your signature?
First, the whole process listed here told us that you need to use a signed URL
Second, for how to create a URL Signature Using C# and the .NET, you can refer to the following doc: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CreateSignatureInCSharp.html
By the way, you can check the example code first.
You can use the following nuget package.
PM> Install-Package Nager.AmazonProductAdvertising
Lookup example
var authentication = new AmazonAuthentication();
authentication.AccessKey = "accesskey";
authentication.SecretKey = "secretkey";
var wrapper = new AmazonWrapper(authentication, AmazonEndpoint.UK);
var result = wrapper.Lookup("B00BYPW00I");

Specified value has invalid HTTP Header characters

Im trying to access WOWZA Streaming Cloud through their REST API from an ASP.NET site, however have little experience in RESTSharp.
Here is an example curl I'm trying to create:
curl -H 'wsc-api-key: KEY' -H 'wsc-access-key: KEY' -H 'Content-Type:
application/json' -X POST -d '{"live_stream": {"name": "MyNewLiveStream",
"transcoder_type": "transcoded", "billing_mode": "pay_as_you_go",
"broadcast_location": "us_west_california", "encoder": "other_rtmp",
"delivery_method": "push", "aspect_ratio_width": 1920,
"aspect_ratio_height": 1080}}'https://api.cloud.wowza.com/api/v1/live_streams/
see: https://sandbox.cloud.wowza.com/apidocs/v1/
Here is the c# code im using:
var client = new RestClient("https://api.cloud.wowza.com/");
var newRequest = new RestRequest("api/v1/live_streams",Method.POST);
newRequest.AddHeader("wsc-api-key", ConfigurationManager.AppSettings["WowzaAPIKey"]);
newRequest.AddHeader("wsc-access-key", ConfigurationManager.AppSettings["WowzaAccessKey"]);
newRequest.AddHeader("Content-Type", "application/json");
var body = "{\"live_stream\": {" +
"\"aspect_ratio_height\": 720," +
"\"aspect_ratio_width\": 1280," +
"\"billing_mode\": \"pay_as_you_go\"," +
"\"broadcast_location\": \"us_west_california\"," +
"\"closed_caption_type\": \"none\"," +
"\"delivery_method\": \"push\"," +
"\"encoder\": \"wowza_gocoder\"," +
"\"hosted_page\": true," +
"\"hosted_page_sharing_icons\": true," +
"\"name\": \"MyLiveStream\"," +
"\"player_countdown\": false," +
"\"player_responsive\": true," +
"\"player_type\": \"original_html5\"," +
"\"player_width\": 0," +
"\"recording\": false," +
"\"target_delivery_protocol\": \"hls\"," +
"\"transcoder_type\": \"transcoded\"," +
"\"use_stream_source\": false}";
newRequest.AddJsonBody(body);
IRestResponse myResponse = client.Execute(newRequest);
Following modifications based on the responses, the response code is now
"{\"meta\":{\"status\":401,\"code\":\"ERR-401-InvalidApiKey\",\"title\":\"Invalid Api Key Error\",\"message\":\"Invalid API key.\",\"description\":\"\",\"links\":[]}}"
Your headers are wrong- you dont need to repeat content-type in value and remove : from the api-key name:
newRequest.AddParameter("content-type", "application/json");
newRequest.AddParameter("wsc-api-key", ConfigurationManager.AppSettings["WowzaAPIKey"]);
Update as OP updated the question
So now after the modification, the call is through and is working fine. The current error is regarding invalid api key you are passing - may be the access key/api key you are passing is invlaid/expired.
Thanks everyone for the help - i can now interact seamlessly with Wowza Cloud Streaming through the REST api wrapped by C# and using restsharp.
The solution in the end was to use the visual studio function to generate template JSON classes through Paste Special (see In C#, how do I model a JSON object with multiple nested arrays?) - generating the WowzaLivestream class.
Then using this code in the end:
_restClient = new RestClient("https://api-sandbox.cloud.wowza.com");
var newRequest = new RestRequest("api/v1/live_streams", Method.POST);
newRequest.RequestFormat = DataFormat.Json;
newRequest.AddHeader("wsc-api-key", ConfigurationManager.AppSettings["WowzaAPIKey"]);
newRequest.AddHeader("wsc-access-key", ConfigurationManager.AppSettings["WowzaAccessKey"]);
newRequest.AddHeader("Content-Type", "application/json");
newRequest.AddHeader("Accept", "application/json");
var requestBody = new WowzaLivestream
{
live_stream = new Live_Stream
{
aspect_ratio_height = 1024,
aspect_ratio_width = 1900,
billing_mode = "pay_as_you_go",
broadcast_location = "eu_ireland",
closed_caption_type = "none",
delivery_method = "push",
encoder = "other_rtmp",
name = "WowzaLStest2: " + DateTime.Now.ToShortTimeString(),
player_countdown = true,
player_responsive = true,
player_type = "original_html5",
player_width = 0,
recording = false,
target_delivery_protocol = "hls",
transcoder_type = "transcoded",
use_stream_source = false
}
};
var json = newRequest.JsonSerializer.Serialize(requestBody);
newRequest.AddParameter("application/json; charset=utf-8", json, ParameterType.RequestBody);
IRestResponse myResponse = _restClient.Execute(newRequest);
It turns out that the URL used was wrong - it isnt specifically stated in the documentation but for sandbox use, the URL is https://api-sandbox.cloud.wowza.com.
Are you sure you want to add "content-type", "wsc-api-key" and "wsc-access-key" as request parameters?
They should be added in the request header as following.
newRequest.AddHeader("content-type", "Content-Type: application/json");
newRequest.AddHeader("wsc-api-key:", ConfigurationManager.AppSettings["WowzaAPIKey"]);
newRequest.AddHeader("wsc-access-key", ConfigurationManager.AppSettings["WowzaAccessKey"]);

Categories