I am trying to get access to my projects in TFS online from my C# code in order to get all the data about builds, tasks, projects etc. with the RESTapi, I have been following the documentation available online to do so (http://www.visualstudio.com/en-us/integrate/get-started/get-started-rest-basics-vsi), however, when I want to get the Json response from the url, I always get: HTTP code 203: Non-Authoritative Information, and therefore I am not able to get the Json data. If I try to get the response using POSTMAN (chrome extension) I get an HTTP code 200 and the data I need.
This is my code:
public static async void GetBuilds()
{
try
{
var username = "userTest";
var password = "PassTest";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
using (HttpResponseMessage response = client.GetAsync(
"https://myproject.visualstudio.com/DefaultCollection/_apis/build/builds?api-version=1.0-preview.1").Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
I always get in the response a high amount of HTML but nothing close to what I need, what am I doing wrong?
Many thanks in advance for your time.
Your code seems correct to me. Have you enabled alternate credentials for your VSO account? It won't work without it. Here's the link explaining how to do it.
You can also check out my project on the codeplex: https://vsorest.codeplex.com/ It shows how to use some of the VSO REST APIs using C#
Related
I am trying to use C# HttpClient from ASP.NET MVC to make a request to an API. My API is running on .NET 6.0.
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"{token}");
var serialized = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync(urlToSend, serialized);
Here is my code. I tried all the possibilities I saw on google. But when sending request, I can't send Authorization header.
I can send it with Postman.
Here is my API code:
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IResult))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(IResult))]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword([FromBody] ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return GetResponseOnlyResult(await Mediator.Send(changePasswordCommand));
}
Note: In my _url, I use http, not https.
I'm not sure but maybe the [AllowAnonymous]attribute remove the Authorization header from request just because it does not make sense if no authorization is needed.
Have you checked if the sent request contains the header using a tool like fiddler ?
I solved the problem by changing my base url from HTTP to HTTPS.
I tried with Fiddler and I got the same problem when I request to HTTP.
So thanks to #olivier-duhart .
To add to the accepted answer, the problem gets solved by changing from HTTP to HTTPS is due to the fact that, the Authorization header gets stripped during redirects.
This behavior is for security concerns and is by design, as mentioned in the github discussion here.
The same behavior may not be seen when using Postman vs HttpClient for example, is due to the way that different clients, have differing mechanisms, by which the subsequent requests (following a response status 30X) to the redirect location are handled.
Also a great answer elsewhere on stackoverflow : Authorization header is lost on redirect
Please review this link. Allow Anonymous will ignore the authentication header
https://github.com/dotnet/aspnetcore/issues/30546
I tried with the code. It seems working fine for me. Here is my code of console app
try
{
ChangePasswordCommand passobj = new ChangePasswordCommand() { password = "new password"};
string _url = "https://localhost:44395/api/Values/";
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"MYTOKEN");
var serialized = new StringContent(JsonConvert.SerializeObject(passobj), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync("changeuserpassword", serialized);
}
catch (Exception ex) {
}
And here is controler Api
[AllowAnonymous]
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword(ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return Ok();
}
I'm testing WSO2 Api Manager.
I have some Apis working good and wow I need to integrate them with Api Manager 2.6.0. I've make some test and all Get requests work very good, but when I make Post Request sometimes failed. For some reason the parameters from body are nulls, another times the parameters are received by the APIs.
However if I make the requests without Api Manager, they all work good all the time.
My APIs are developed in .Net Web Api 2.0.
This is my code to call APIs, I make some test with HttpClient and RestSharp getting the same result. However when I test using Postman, the APIS always work good, event through the API Manager.
Example using HttpClient:
public async Task PostHttpClient(TarificacionParametros entidad, string url)
{
var personaJson = JsonConvert.SerializeObject(entidad);
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Clear();
//httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", BEARER);
HttpContent httpContent = new StringContent(personaJson, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = httpClient.PostAsync(url, httpContent).Result;
if (response.IsSuccessStatusCode)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<object>(responseBody);
}
}
Example using Restsharp:
public async Task PostRestSharp(TarificacionParametros entidad, string url)
{
var client = new RestClient(url);
var request = new RestRequest(Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(entidad);
Console.WriteLine(entidad.Capital);
// easily add HTTP Headers
request.AddParameter("Authorization", "Bearer " + BEARER, ParameterType.HttpHeader);
var response = client.Execute<object>(request);
}
Note: I don't post API's code because they are good, they have time working with any problem in production, now we just need to integrate them with WSO2 Api Manager.
Regards
I have tried to create a simple console application.
We have a call system from 8x8 that provide a web streaming API but their documentation is very limited and nothing in C#.
The api service streams call statuses in near real time and I would like to get that 'stream' and be able to read and process it in realtime if possible. The response or Content Type is 'text/html'. But the actual body of the response can be declared as json - sample below:
{"Interaction":{"attachedData":{"attachedDatum":[{"attachedDataKey":"#pri","attachedDataValue":100},{"attachedDataKey":"callingName","attachedDataValue":999999999999},{"attachedDataKey":"cha","attachedDataValue":99999999999},{"attachedDataKey":"cnt","attachedDataValue":0},{"attachedDataKey":"con","attachedDataValue":0},{"attachedDataKey":"med","attachedDataValue":"T"},{"attachedDataKey":"pho","attachedDataValue":9999999999},{"attachedDataKey":"phoneNum","attachedDataValue":9999999999},{"attachedDataKey":"tok","attachedDataValue":999999999}]},"event":"InteractionCreated","inboundChannelid":9999999999,"interactionEventTS":9999999,"interactionGUID":"int-15b875d0da2-DJOJkDhDsrh3AIaFP8VkICv9t-phone-01-testist","resourceType":0}}
I have seen several posts concerning httpClient and the GetAsync methods but none of these appear to work as they appear to be for calls when a response is made, not something that constantly has a response.
Using fiddler for the call it does not appear to close so the stream is constantly running, so fiddler does not display any data until a separate user or instance connects.
When I use a browser the content is 'streamed' to the page and updates automatically and shows all the content (as above).
The api contains authentication so when another client connects and retrieves data the connected client closes and finally I am able to see the data that was gathering.
This is the code so and does return the big stream when another client connects but ideally I want a real time response and appears to just get stuck in the GETASYNC method:
var response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = await responseContent.ReadAsStringAsync();
Console.WriteLine(responseString);
}
Hopefully that's enough information for one of you clever people to help me in my predicament.
I was also having an issue consuming their streaming API and the examples I found that worked with the Twitter and CouchBase streaming API's did not work with 8x8. Both Twitter and CouchBase send line terminators in their pushes so the solution relied on ReadLine to pull in the feed. Since 8x8 does not send terminators you'll need to use ReadBlock or better ReadBlockAsync.
The following code shows how to connect using credentials and consume their feed:
private static async Task StreamAsync(string url, string username, string password)
{
var handler = new HttpClientHandler()
{
Credentials = new NetworkCredential {UserName = username, Password = password},
PreAuthenticate = true
};
// Client can also be singleton
using (var client = new HttpClient(handler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Connection.Add("keep-alive");
using (var response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead))
{
using (var body = await response.Content.ReadAsStreamAsync())
{
using (var reader = new StreamReader(body))
{
while (!reader.EndOfStream)
{
var buffer = new char[1024];
await reader.ReadBlockAsync(buffer, 0, buffer.Length);
Console.WriteLine(new string(buffer));
}
}
}
}
}
}
I am testing a REST API post, and it works well when I try it on Postman. However, in some scenario (related to the posting XML data) if I post with HttpClient API, I would receive the following error:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
But the same XML content works fine on Postman with status OK and proper response.
What is the differences between using the C# HttpClient API and the postman testing? How can I configure my API call to match with the behavior on postman?
Here I attached the source code, and the Postman screenshot
public void createLoan()
{
string baseCreateLoanUrl = #"https://serverhost/create?key=";
var strUCDExport = XDocument.Load(#"C:\CreateLoan_testcase.xml");
using (var client = new HttpClient())
{
var content = new StringContent(strUCDExport.ToString(), Encoding.UTF8, Mediatype);
string createLoanApi = string.Concat(baseCreateLoanUrl, APIKey);
try
{
var response = client.PostAsync(createLoanApi, content).Result;
}
catch (Exception ex)
{
MessageBox.Show("Error Happened here...");
throw;
}
if (response.IsSuccessStatusCode)
{
// Access variables from the returned JSON object
string responseString = response.Content.ReadAsStringAsync().Result;
JObject jObj = JObject.Parse(responseString);
if (jObj.SelectToken("failure") == null)
{
// First get the authToken
string LoanID = jObj["loanStatus"]["id"].ToString();
MessageBox.Show("Loan ID: " + LoanID);
}
else
{
string getTokenErrorMsg = string.Empty;
JArray errorOjbs = (JArray) jObj["failure"]["errors"];
foreach (var errorObj in errorOjbs)
{
getTokenErrorMsg += errorObj["message"].ToString() + Environment.NewLine;
}
getTokenErrorMsg.Dump();
}
}
}
Thanks for Nard's comment, after comparing the header, I found the issue my client header has this:
Expect: 100-continue
While postman doesn't has.
Once I removed this by using the ServicePointManager:
ServicePointManager.Expect100Continue = false;
Everything seems fine now. Thanks all the input!
My gut tells me it's something simple. First, we know the API works, so I'm thinking it's down to how you are using the HttpClient.
First things first, try as suggested by this SO answer, creating it as a singleton and drop the using statement altogether since the consensus is that HttpClient doesn't need to be disposed:
private static readonly HttpClient HttpClient = new HttpClient();
I would think it would be either there or an issue with your content encoding line that is causing issues with the API. Is there something you are missing that it doesn't like, I bet there is a difference in the requests in Postman vs here. Maybe try sending it as JSON ala:
var json = JsonConvert.SerializeObject(strUCDExport.ToString());
var content = new StringContent(json, Encoding.UTF8, Mediatype);
Maybe the header from Postman vs yours will show something missing, I think the real answer will be there. Have fiddler running in the background, send it via Postman, check it, then run your code and recheck. Pay close attention to all the attribute tags on the header from Postman, the API works so something is missing. Fiddler will tell you.
I was struggling with this for 2 days when I stumbled over Fiddler which lets you record the traffic to the service. After comparing the calls I saw that I had missed a header in my code.
I need to upgrade an old TFS 2013 class for Visual Studio Team Services.
To get the Burndown-Chart I used to download the image via HttpWebRequest direcly from the url.
Somehow Iam not able to do this in VSTS. I always get the error message "invalid parameters". Everything else works fine. (I had to setup the Alternate authentication credentials in my profile to get it working for my application)
Here my code:
public Image GetChart(string uri)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
httpWebRequest.Credentials = new NetworkCredential("MyUserNameForApplication", "MyPWForApplication");
using (HttpWebResponse httpWebReponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
using (Stream stream = httpWebReponse.GetResponseStream())
{
return Image.FromStream(stream); //Error occourse
}
}
}
The url which gets passed as a parameter usally looks like this:
https://YourVSName.visualstudio.com/DefaultCollection/a5d2310b-d3f8-4365-b693-3826ab60e939/_api/_teamChart/Burndown?chartOptions={%22Width%22%3A1248%2C%22Height%22%3A161%2C%22ShowDetails%22%3Atrue%2C%22Title%22%3A%22%22}&counter=1&iterationPath=Developing\Sprint+1&__v=5
What I think the problem is:
First I thought this might be a security issue, because this code is able to download normal google images. And when I try to get the content of the url It returns a lot of code with a message in it:
Microsoft Internet Explorer's Enhanced Security Configuration is currently enabled on your environment. This enhanced level of security prevents our web integration experiences from displaying or performing correctly. To continue with your operation please disable this configuration or contact your administrator
I set my Internet security settings to the lowest level and still the same result.
Another reason why this might not working is, because the url linking to the burndown-chart doesnt contain an Image extension. Iam not quite shure here this effects the result.
Or that the parameters which are getting past in the url are incorrect...
What I have tried so far:
I have used bunch of other code to get the image from that link. For example using WebClient or tried to upload cookies (credentials) to the tfs and than tried to connect.
My Question
Is it possible to get that image from the chart via url, and if so, how?
Thanks for any kind of help :).
EDIT
Currently Iam using this code (Thanks to #Eddie - MSFT):
public static async void GetChart(string uri,string username, string password)
{
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
using (HttpResponseMessage response = client.GetAsync(uri).Result)
{
response.EnsureSuccessStatusCode();
var responseStream = await response.Content.ReadAsStreamAsync();
var img = Image.FromStream(responseStream);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static void Main(string[] args)
{
string uri = "https://Name.visualstudio.com/DefaultCollection/a5d2310b-d3f8-4365-b693-3826ab60e939/_api/_teamChart/Burndown?chartOptions=%7B%22Width%22%3A1248%2C%22Height%22%3A636%2C%22ShowDetails%22%3Atrue%2C%22Title%22%3A%22%22%7D&counter=1&iterationPath=Developing%5CSprint+1&__v=5";
TFSHelper.TFSHelper.GetChart(uri, username,pw)
}
I use "httpclient" with alternative credential to do this:
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Net.Http;
using System.Net.Http.Headers;
using System.IO;
namespace GetImageA
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("your image uri");
GetImage(uri);
}
public static void GetImage(Uri uri)
{
var username = "username";
var password = "password";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
Stream str = client.GetStreamAsync(uri).Result;
Image im = Image.FromStream(str);
im.Save("E:\\image.png");
}
}
}
}
Did you try the authentication with your personal access token instead of username and password, something like this?
I am using the below code to download the attachments, inline images of the Work items from VSTS.
try
{
var personalaccesstoken = "Your_VSTS_Personal_Access_Token";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", personalaccesstoken))));
using (HttpResponseMessage response = client.GetAsync(uri).Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}