Getting 404 error only when using .NET framework - c#

I'm trying to create a version in JIRA for a specific project.
I'm able to do the process via Postman by building my requests manually, but it fails with a 404 when creating the version record via .NET.
I'm assuming .NET adds pesky parameters to the request that Postman doesn't do.
The weird thing is that the authentication call works, but the the version creation fails.
Here's the helper I wrote:
public class JIRA
{
private string AuthToken { get; set; }
private const string c_JIRAUrl = "https://org.atlassian.net";
private const string c_LoginUrl = c_JIRAUrl + "/rest/auth/1/session";
private const string c_CreateVersionUrl = c_JIRAUrl + "/rest/api/2/version";
public JIRA()
{
//this works...
var authResponse = ExecuteRequest(c_LoginUrl, "POST", new
{
username = "login",
password = "password"
});
AuthToken = authResponse["session"]["value"].ToString();
}
public void CreateVersion(string name, string projectKey, ProjectEnvironment environment)
{
//lets hardcode the same data I use in Postman for testing purposes...
var createVersionResponse = ExecuteRequest(c_CreateVersionUrl, "POST", new
{
description = "An excellent version",
name = "1.1.2",
archived = false,
released = false,
project = "TEST"
});
}
private JObject ExecuteRequest(string url, string method, object data)
{
HttpWebResponse response;
var jsonDataString = JsonConvert.SerializeObject(data);
byte[] dataBytes = Encoding.Default.GetBytes(jsonDataString);
var responseText = string.Empty;
var wr = (HttpWebRequest)WebRequest.Create(url);
wr.ContentType = "application/json";
if (!string.IsNullOrEmpty(AuthToken))
wr.Headers.Add(HttpRequestHeader.Authorization, $"Bearer {AuthToken}");
wr.Method = method;
wr.ContentLength = dataBytes.Length;
wr.Accept = "application/json";
using (var webStream = wr.GetRequestStream())
{
webStream.Write(dataBytes, 0, dataBytes.Length);
response = (HttpWebResponse)wr.GetResponse();
}
using (var sr = new StreamReader(response.GetResponseStream()))
{
responseText = sr.ReadToEnd();
}
return JObject.Parse(responseText);
}
}
The CreateVersion method always fails with a 404.
As I've said, doing the same (retrieving the token, creating the version) all works in Postman.
Any ideas what's going on ?
Thanks.

Apparently, when retrieving the token (/rest/auth/1/session) the response contains cookies that POSTMAN was sending back in the 2nd request (creating the version). I had to fire up Fiddler to find out it was doing so because its UI was not saying so.
My .NET client was not doing so. When making it do so, it works.
I'm a little miffed that a REST service expects cookies...

Related

C# RestClient returning "Login Required" when Login Details are specified

Just started at a new company and we all use Jira, the customers are determined to not use it as they don't like it so I have decided to build a simple Windows Form when they can both Log tickets and get Updates and Comments in a nice simple UI.
Now I have never done any coding before 2 weeks ago so it has been a struggle to get my head around both C# and Rest (Have made scripts for basic IT fixes but never anything as complex as this!)
Back onto point, Set up and got a Rest API set up with a Rest Client but everytime I try pull data from a ticket on Jira I get the error:
{"errorMessages":["You do not have the permission to see the specified issue.","Login Required"],"errors":{}}
Here is the code from the Form:
private void button3_Click_1(object sender, EventArgs e)
{
var client = new RestClient("https://jira.eandl.co.uk/rest/api/2/issue/ITREQ-" + textBox1.Text
);
client.Authenticator = new SimpleAuthenticator("username", "abc", "password", "123");
var request = new RestRequest(Method.GET);
request.AddParameter("token", "saga001", ParameterType.UrlSegment);
// request.AddUrlSegment("token", "saga001");
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
var queryResult = client.Execute(request);
Console.WriteLine(queryResult.Content);
}
And here is the code from the Rest Client itself:
public Restclient()
{
endPoint = string.Empty;
httpMethod = httpVerb.GET;
}
private string logonAttempt;
public string makeRequest()
{
string strResponseValue = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endPoint);
request.Method = httpMethod.ToString();
String authHeader = System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(userName + ":" + userPassword));
request.Headers.Add("Authorization", authType.ToString() + " " + authHeader);
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
//Process the Response Stream... (Could be JSON, XML ot HTML etc...)
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream != null)
{
using (StreamReader reader = new StreamReader(responseStream))
{
strResponseValue = reader.ReadToEnd();
}//End of Stream Reader
}
}//end of Response Stream
}
catch (Exception ex)
{
strResponseValue = "(\"errorMessages\":[\"" + ex.Message.ToString() + "\"],\"errors\":{}}";
}
finally
{
if(response != null)
{
((IDisposable)response).Dispose();
}
}
return strResponseValue;
}
}
}
Now obviously I am expecting that I have missed something absolutely bigginer as like I said, I've never taken on a project like this before and had 0 experience.
Just looking for someone to bluntly tell me what I'm doing wrong
Changed to this as per answer:
private void button3_Click_1(object sender, EventArgs e)
{
var client = new
RestClient("https://jira.eandl.co.uk/rest/api/2/issue/ITREQ-" + textBox1.Text
);
client.Authenticator = new HttpBasicAuthenticator("username", "password");
var request = new RestRequest(Method.GET);
string authHeader = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes("cdale!" + ":" + "Chantelle95!"));
request.AddHeader("Authorization", "Basic " + authHeader);
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
var queryResult = client.Execute(request);
Console.WriteLine(queryResult.Content);
}
By default with the Jira REST API, you can use Basic Authentication or OAuth2. I think that more easy way for you will be to use the Basic one.
I'm not sure why you have a class where you define your custom RestClient since the first block of code uses the RestSharp one from http://restsharp.org.
In this case, you will need to modify your authenticator:
client.Authenticator = new HttpBasicAuthenticator(userName, password);
And I think that you should remove the line where you specify a token. I don't think that it's required.
Finally, the class Restclient doesn't seem to be used, then remove it.
You could also uses what you have created in your custom RestClient and manually specify a Basic header:
string authHeader = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(userName + ":" + userPassword));
request.AddHeader("Authorization", "Basic " + authHeader);
However, it's essentially the behavior of the HttpBasicAuthenticator class.
If you don't want to encode your credentials in every request here is how to do it using cookies.
When requesting the cookie you don't need to add any authorization on the headers. This method will accept a JSON string with the user name and password and the URL. It will return the cookie values.
public async Task<JiraCookie> GetCookieAsync(string myJsonUserNamePassword, string JiraCookieEndpointUrl)
{
using (var client = new HttpClient())
{
var response = await client.PostAsync(
JiraCookieEndpointUrl,
new StringContent(myJsonUserNamePassword, Encoding.UTF8, "application/json"));
var json = response.Content.ReadAsStringAsync().Result;
var jiraCookie= JsonConvert.DeserializeObject<JiraCookie>(json);
return jArr;
}
}
public class JiraCookie
{
public Session session { get; set; }
}
public class Session
{
public string name { get; set; }
public string value { get; set; }
}
When I call it using url: http://[baseJiraUrl]/rest/auth/1/session it returns the following JSON response:
{
"session" : -{
"name" : JSESSIONID,
"value" : cookieValue
}
Keep in mind the URL above is valid in the version of JIRA I'm using and may vary depending on which version you're using. Read the JIRA API documentation for the correct URL for the version you are using. I'm using the following:
https://docs.atlassian.com/software/jira/docs/api/REST/7.6.1/#auth/1/session
Remember you'll have to store your cookie and use it on every subsequent request.
Check out this answer on how add cookies to your HttpClient request: How do I set a cookie on HttpClient's HttpRequestMessage.
Once you're done with the cookie (logging out) simply send a delete http request with the same URL as the post.
Reference: https://stackoverflow.com/a/49109192/7763903

Rest API GET Method using Visual Studio 2015 and Xamarin

i'm new on creating Rest API using PHP. I have create one, my codes works but i'm not sure about it, here is the code :
// connect to the mysql database
$link = mysql_connect('localhost', 'root', 'pass');
mysql_select_db('database',$link);
// retrieve the table and key from the path
$nama_tujuan = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
$nama_tabel = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
//Menu Login
if($nama_tujuan=="login")
{
$username = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
$password = preg_replace('/[^a-z0-9_]+/i','',array_shift($request));
if($method=='GET')
{
$sql = "select * from $nama_tabel where username_reg='$username' and pass_reg='$password'";
$result = mysql_query($sql,$link);
if (!$result) {
http_response_code(404);
die(mysql_error());
}
$cek_user=mysql_num_rows($result);
//cek user
if($cek_user==1)
{
//Kirim data yang dperlukan
for ($i=0;$i<mysql_num_rows($result);$i++)
{
echo ($i>0?',':'').json_encode(mysql_fetch_object($result));
}
}
}
}
for example i try it with:
http://localhost/api.php/login/member/neo/qw
and it show all of the fields like this :
{"nik":"46464646457349187","username_reg":"neo","nm_dpn_reg":"neo","nm_blg_reg":"alit","email_reg":"neo#neo.com","pass_reg":"qw","jns_kel":"P","pin_reg":"111111","tmp_lahir_reg":"lumajang","tgl_lahir_reg":"1-1-1945","alamat_reg":"jolotundo","kota_reg":"surabaya","kd_pos":"60131","propinsi":"Aceh","alamat_kirim":"","kota_kirim":"","kdpos_kirim":"","propinsi_kirim":"","wilayah":"jawa","tlp_hp_reg":"08964507555","tgl_daf":"2016-04-27 16:32:00","ikut_resel":"","bonus":"0"}
Is this called Rest API? And my second question, i want to get JSON data on my Android app, i'm using Visual Studio 2015 and Xamarin. I already create some code, but i don't know the rest, because i'm new on developing android app. Here is my activity code :
button.Click += async (sender, e) => {
string url = "http://localhost/api.php/login/login_mbr/" +
txtUsername.Text +
"/" +
txtPassword.Text;
string m = JsonConvert.DeserializeObject<string>(await FetchWeatherAsync(url));
// ParseAndDisplay (json);
};
}
private async Task<string> FetchWeatherAsync(string url)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.ContentType = "application/json";
request.Method = "GET";
}
Thanks in advance :)
You can get the json string by this:
private async Task<string> FetchWeatherAsync (string url)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create (new Uri (url));
request.ContentType = "application/json";
request.Method = "GET";
// Send the request to the server and wait for the response:
using (WebResponse response = await request.GetResponseAsync ())
{
// Get a stream representation of the HTTP web response:
using (Stream stream = response.GetResponseStream ())
{
string strContent = sr.ReadToEnd();
return strContent;
}
}
}
now this already returns you the JSON.
If you want to deserialize the JSON into the object you should create the object with the specified properties like:
public class WeatherResult ()
{
public string nik {get; set;}
public string username_reg {get; set;}
...
...
...
}
and then you can get it by
WeatherObject result = JsonConvert.DeserializeObject<WeatherResult>(await FetchWeatherAsync(url));
If you don't want to create new object you can use dynamic object
dynamic result = JObject.Parse(await FetchWeatherAsync(url));
Note: you need Newtonsoft library for JSON.

Unable to Post on Pinterest using their API

I'm receiving a 400 Bad Request error message when posting a pin on Pinterest. It works using Postman, but doesn't work programmatically. Using C#, has anyone been able to successfully post a pin on Pinterest without using the pinsharp wrapper?
private void postPinterest(string messages, string id, string usertoken, string image, string boardname, string username)
{
string link = null;
boardname = boardname.Replace(" ", "-");
string board = username + "/" + boardname;
string url = "https://api.pinterest.com/v1/pins?access_token=" + usertoken;
StringBuilder sb = new StringBuilder();
if (!string.IsNullOrEmpty(board))
sb.Append("&board=" + HttpUtility.UrlEncode(board));
if (!string.IsNullOrEmpty(messages))
sb.Append("&note=" + HttpUtility.UrlEncode(messages));
if (!string.IsNullOrEmpty(link))
sb.Append("&image_url=" + HttpUtility.UrlEncode(link));
string postdata = sb.ToString().Substring(1);
PostData(url, postdata);
}
private object PostData(string url, string postdata)
{
object json=null;
try
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
// req.Accept = "application/json";
using (var stream = req.GetRequestStream())
{
byte[] bindata = Encoding.ASCII.GetBytes(postdata);
stream.Write(bindata, 0, bindata.Length);
}
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
string response = new StreamReader(resp.GetResponseStream()).ReadToEnd();
json = JsonConvert.DeserializeObject<dynamic>(response);
return json;
}
catch (WebException wex)
{
if (wex.Response != null)
{
using (var errorResponse = (HttpWebResponse)wex.Response)
{
using (var reader = new StreamReader(errorResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
return json;
}
}
}
}
return json;
}
EDIT:
It doesn't work using the JSON format or x-www-form-urlencoded format.
I changed the content type to application/x-www-form-urlencoded and now I'm receiving the error message below. I receive 400 Bad Request error using JSON format:
"{\n \"message\": \"405: Method Not Allowed\",\n \"type\": \"http\"\n}"
The problem is the the parameter that you are posting.
In the Api i could find board as a parameter but both note and image comes under field parameter which specifies the return type JSON.
As per documentation on this page you can post in this format
https://api.pinterest.com/v1/boards/anapinskywalker/wanderlust/pins/?
access_token=abcde&
limit=2&
fields=id,link,counts,note
So I tried the following and its getting response
https://api.pinterest.com/v1/boards/?access_token="YourTokenWithoutQuotes"&fields=id%2Ccreator
Would suggest you to first test the Api you are hitting putting a breakpoint inside the PostData function and check if the passed url is in the correct format and compare it with Pininterest API Explorer.
As you might have already received authorization code and access token so I am assuming your post function should be working fine.
public string postPinterest(string access_token,string boardname,string note,string image_url)
{
public string pinSharesEndPoint = "https://api.pinterest.com/v1/pins/?access_token={0}";
var requestUrl = String.Format(pinSharesEndPoint, accessToken);
var message = new
{
board = boardname,
note = note,
image_url = image_url
};
var requestJson = new JavaScriptSerializer().Serialize(message);
var client = new WebClient();
var requestHeaders = new NameValueCollection
{
{"Content-Type", "application/json" },
{"x-li-format", "json" }
};
client.Headers.Add(requestHeaders);
var responseJson = client.UploadString(requestUrl, "POST", requestJson);
var response = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(responseJson);
return response;
}

Error in json post request for mailchimp api

I am trying to create a wrapper for:
this mailchimp method using C#.Net
I understand that there are already .Net wrappers available. I am using Mailchimp.Net by Dan Esparza. Method in the wrapper class is giving exception for the api method mentioned above.It is throwing internal server exception (500) which I am not sure why, so I decided to create my own wrapper for the particular method. I have following code:
private void CreateGrouping(string apiKey, string listId,string groupName,string groupValue)
{
string url = "https://us9.api.mailchimp.com/2.0/lists/interest-grouping-add";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "text/json";
httpWebRequest.Method = "POST";
List<string> values = new List<string>();
values.Add(groupValue);
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(new
{
apiKey = apiKey,
id = listId,
name = groupName,
type="radio",
groups = values
});
streamWriter.Write(json);
}
var response = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
}
But on execution of var response = (HttpWebResponse)httpWebRequest.GetResponse(); is throwing same exception - internal server error (500).
It might be that I am passing data in wrong way ? Can someone please help me in finding out what am I missing here ?
As I suspected I was passing the data in wrong way: apiKey has to be apikey (k was in caps)
string json = new JavaScriptSerializer().Serialize(new
{
apikey = apiKey,
id = listId,
name = groupName,
type="radio",
groups = values
});
Other than this, I added:
streamWriter.Flush();
streamWriter.Close();
It might help someone save sometime.

Cannot access apicontroller method - returns 404

I have 2 c# asp.net projects. 1 is an api. 1 is consuming this api.
My api:
public class MyApiController : ApiController
{
public dynamic ValidateToken(string token)
{
return myValidationMethod(token);
}
}
Consuming my api from another project:
public class MyController : Controller
{
[HttpPost]
public ActionResult ValidateToken(string token)
{
var url = "http://localhost:1234/myapi/validatetoken";
var parameters = "token=" + token;
using (var client = new WebClient())
{
var result = client.UploadString(url, parameters);
return Json(result);
}
}
}
In project 2 where I consume the api, client.UploadString throws a System.Net.WebException - The remote server returned an error: (404) Not Found.
When I test the api with the chrome rest client it works with http://localhost:1234/myapi/validatetoken?token=myToken
Why can WebClient not find it?
Solved
I got this to work thanks to #BrentMannering with a small change to add content length:
var url = "http://localhost:1234/myapi/validatetoken?token=" + token;
var request = WebRequest.Create(url);
request.Method = "POST";
request.ContentLength = 0; //got an error without this line
var response = request.GetResponse();
var data = response.GetResponseStream();
string result;
using (var sr = new StreamReader(data))
{
result = sr.ReadToEnd();
}
return Json(result);
I don't think UploadString is sending the data as params, so the routing engine on the API side cannot map to an action, hence the 404. According to the MSDN documentation the method is encoding to a Byte[] prior to uploading, this could be part of the problem.
Try using the UploadValues method
var url = "http://localhost:1234/myapi/validatetoken";
var nv = new NameValueCollection { { "token", token } };
using (var client = new WebClient())
{
var result = client.UploadValues(url, nv);
return Json(result);
}
Otherwise to mimic the test you are doing with the chrome client use WebRequest
var url = "http://localhost:1234/myapi/validatetoken?token=" + token;
var request = WebRequest.Create(url);
request.Method = "POST";
var data = request.GetResponse().GetResponseStream();
string result = String.Empty;
using (StreamReader sr = new StreamReader(data))
{
result = sr.ReadToEnd();
}
return Json(result);
WebClient.UploadString method sends an Http POST method. Did you try to access your APIController with a POST method from the test client ? I am assuming your test client sent a GET request and it worked.
There is a different overload where you can mention the Action method type.
var url = "http://localhost:1234/myapi/validatetoken";
var parameters = "token=" + token;
string method = "GET"; //or POST as needed
using (var client = new WebClient())
{
var result = client.UploadString(url,method , parameters);
return Json(result);
}

Categories