Zip file empty when downloading from Azure Devops pipeline - c#

I am trying to download pipeline artifacts from an Azure Devops Pipeline.
The artifacts are zip files. I have been referencing this post,
https://andrewlock.net/downloading-artifacts-from-azure-devops-using-dotnet/
and am nearly there.
Problem is, when I go to extract my zip files to check the content windows explorer says they are empty. I have tried other tools as well, same thing. I know the artifact is valid and the download url is valid, because I can break on entry to the following function and check the url. It matches up with what I get if I use the web UI.
Has to be something I am doing wrong with the download/creation of the zip file. Any help would be appreciated.
static private async Task DownloadArtifactWithHttpClient(string artifactDownloadUrl, string artifactName, string downloadPath)
{
try
{
var localZipFilePath = downloadPath + $"\\{artifactName}.zip";
// Use HttpClient to download the artifact
using (var tempClient = new HttpClient())
{
// Send request to the DownloadUrl specificed in the BuildArtifact
HttpResponseMessage response = await tempClient.GetAsync(artifactDownloadUrl);
if (!response.IsSuccessStatusCode)
{
// Something went wrong, shouldn't happen
throw new Exception($"Error downloading artifact: {response.StatusCode}:{response.ReasonPhrase}");
}
// Save the stream to a file
using (Stream zipFile = File.Create(localZipFilePath))
{
await response.Content.CopyToAsync(zipFile);
}
}
// All done!
Console.WriteLine($"Done downloading too, {localZipFilePath}");
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
if (ex.InnerException != null) Console.WriteLine("Detailed Info: " + ex.InnerException.Message);
Console.WriteLine("Stack:\n" + ex.StackTrace);
}
}

I realized my mistake. I wasn't authenticating the new HttpClient.
Earlier in the example the 'Microsoft.VisualStudio.Services.WebApi.VssConnection' was authenticated with our PAT and was used to get a BuildHttpClient to work with. That client was what I used to get the download URL.
The new HttpClient isn't authenticated in the example. However, I kept digging and looked at the REST API offered by MS and they show how to authenticate an HttpClient with a PAT in this example getting a list of projects
public static async void GetProjects()
{
try
{
var personalaccesstoken = "PAT_FROM_WEBSITE";
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 = await client.GetAsync(
"https://dev.azure.com/{organization}/_apis/projects"))
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
After doing that in my function above, my downloads succeeded.
The response was succeeding with a 203 vs 200 code previously.. Should have been my first clue. Now I know.

Related

Howto access REST API methods without username and Password - but only with a token?

I'm trying to access/call methods in a REST API with a token from c#/.net- but I can't get any response back. I have googlet a lot - but without any success :-( I am new to call methods via a REST API.
I have an endpoint and a token which I need to use for communicating with a REST API. And I need to GET, POST, PUT and DELETE data on the server via those methods
The output from the API is in JSON format.
Maybe it is simple - but I don't know howto do it.
Any help is appreciated.
I have tried the following solution - but with no success :-(
private static async void DoIt()
{
using (var stringContent = new StringContent("{ \"firstName\": \"Andy\" }", System.Text.Encoding.UTF8, "application/json"))
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
// 1. Consume the POST command
var response = await client.PostAsync(endpoint, stringContent);
var result = await response.Content.ReadAsStringAsync();
//Console.WriteLine("Result from POST command: " + result);
// 2. Consume the GET command
response = await client.GetAsync(endpoint);
if (response.IsSuccessStatusCode)
{
var id = await response.Content.ReadAsStringAsync();
//Console.WriteLine("Result from GET command: " + result);
}
}
catch (Exception ex)
{
//Console.ForegroundColor = ConsoleColor.Red;
//Console.WriteLine(ex.Message);
//Console.ResetColor();
}
}
}
In your code you initialize AuthenticationHeaderValue with "Basic", which means Basic authentication based on username and password. If you have a token, you do it with:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ACCESS_TOKEN);
replace ACCESS_TOKEN with the token you have.
This is the most probable solution, but I can only guess here, as I don't know the API you're trying to access. If it still doesn't work, try ommiting "Bearer".
Reference

I got a System error in C# post

It is my first time to ask a question in here (I'm from Asia).
Platform:UWP 17632
IDE : Visual Studio 2017
Based on the reqiurement of the project, I need to post some information to a website.
I refer the answer about How to make HTTP POST web request Method A.
Here is my code:
public async void PostDataAsync(string pTemperture, string pHumidity, string pFireStatus, string pLightStatus, string pBodyStatus)
{
var values = new Dictionary<string, string>
{
{"count", "1" },
{"temperture_0", pTemperture },
{"Humidity_0", pHumidity },
{"FireStatus_0", pFireStatus },
{"LightStatus_0" ,pLightStatus},
{"BodyDetect_0", pBodyStatus }
};
var content = new FormUrlEncodedContent(values);
try
{
var response = await client.PostAsync("http://115.159.36.210/api/onehome/upload", content);//Here throw an exception
System.Diagnostics.Debug.WriteLine(response);
var responseString = response.Content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine(responseString);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.HelpLink);
System.Diagnostics.Debug.WriteLine(ex.Message);
throw;
}
}
And then it throws an exception
“An error occurred while sending the request.”
in
var response = await client.PostAsync("http://115.159.36.210/api/onehome/upload", content);
I want to know why and gain the solution which can solve it.
I will be grateful if you can help me.
It is recommend that use HttpClient and the rest of the Windows.Web.Http namespace API to send and receive information using the HTTP 2.0 and HTTP 1.1 protocols within UWP.
For your requirement, you could make a method to package http POST method like the follow
public async void SendPostMethod(string url, Dictionary<string, string> param, Action<string> response)
{
HttpClient client = new HttpClient();
HttpResponseMessage httpResponse = new HttpResponseMessage();
Uri requestUri = new Uri(url);
var content = new HttpFormUrlEncodedContent(param);
try
{
httpResponse = await client.PostAsync(requestUri, content);
response(await httpResponse.Content.ReadAsStringAsync());
}
catch (Exception ex)
{
}
}
Usage
this.SendPostMethod("http://115.159.36.210/api/onehome/upload",Param, (res) =>
{
var response = res;
});
And there are official code sample and document that you could refer.
I am the author of the server.
The reality is I have not finish the code of the server.
Thus , {"status":-1,"msg":"Error! Invalid Request."} is the default result .....

Quickbook invoice create returns 400 bad request (SANDBOX, Quickbook Online, C# MVC, OAuth 2.0)

Well, I am working on creating Quickbook online entries, and for some reason I am getting error (400 bad request).
I know something is invalid in my request but I am unable to figure it out. I am using Sandbox account. I have copied default data from API explorer and made request using this data only and finally getting 400 bad request.
My code is working fine for "Select * from invoice" query request.
The base URL I am using sandbox-quickbooks.api.intuit.com
My Code is as Follow:-
var principal = User as ClaimsPrincipal;
Session["realmId"] = XXXXXXX;
var result = new HttpResponseMessage();
if (Session["realmId"] != null)
{
string realmId = Session["realmId"].ToString();
string qboBaseUrl = ConfigurationManager.AppSettings["QBOBaseUrl"];
//add qbobase url and query
string uri = string.Format("{0}/v3/company/{1}/invoice", qboBaseUrl, realmId);
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json;charset=UTF-8");
client.DefaultRequestHeaders.Add("ContentType", "application/json;charset=UTF-8");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + Session["AccessToken"]);
result = await client.PostAsync(uri, new StringContent(JsonData, System.Text.Encoding.UTF8, "application/json"));
return result;
}
catch (Exception ex)
{
return result;
}
}
else
return result;
Check the body of the response and see if it provides any details about what is wrong with the request.
//...
var responseContent = await result.Content.ReadAsStringAsync();
//...

Post rest web api working from Postman but not returning any response from code in C# project

There is a POST rest api which used to work from code before. But recently it is broken and is not returning any response. However if I try to call the api from the Postman, then it works fine.
In what way can I debug this to find the root cause of the issue ?
Following is the C# code which I am using to call this post rest api
public async Task SaveToServerAsync()
{
string filePath = #"<filePath>";
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// tried this line of code from another SO answer, but this didn't work either
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://<server name>/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "d2ebf9aefbaa416adcd0");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var content = new MultipartFormDataContent();
content.Add(new StreamContent(fileStream), "file", filePath);
content.Add(new StringContent("e8d002f9-f381-44c2-bce0-13416929f14d"), "Id");
try
{
var response = await client.PostAsync("<rest api end point>", content).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
Debug.Write("Response received");
}
}
catch (Exception ex)
{
Debug.Write("Exception occured");
Debug.Write(ex.Message);
}
finally
{
}
}
}
}
It always goes to the exception block with exception as "The task was cancelled"
Not sure how can I debug it when it anyway works from the Postman.
So the problem was related to ExpectContinue header which goes as true by default. Somehow server was not handling it properly and client was waiting for continue (100) message for indefinite time.
For the time being manually setting this header to be false worked for us:
httpClient.DefaultRequestHeaders.ExpectContinue = false;

Sending HTTP Request with a file AND strings as post parameters using HttpClient

Hi I'm writing a Windows Phone 8.1 Application, In this app i need to send a request to the Sonic API server, the request should contain a access_id (string) a format (string) and input_file (mp3 file) , I'm stuck with putting both the file and the string in one request : this is what I've tried :
public async static Task<string> RequestSong(StorageFile mp3File, string responseFormat)
{
try
{
using (var webClient = new HttpClient())
{
using (var form = new MultipartFormDataContent())
{
var mp3FileByteArray = await readBytesFromStorageFile(mp3File);
form.Add(new StringContent(ApiAccessId), "access_id");
form.Add(new StringContent(responseFormat), "format");
form.Add(new ByteArrayContent(mp3FileByteArray, 0, mp3FileByteArray.Count()), "input_file", mp3File.Name);
HttpResponseMessage responseMessage = await webClient.PostAsync(ApiAccessUrl, form);
//responseMessage.EnsureSuccessStatusCode();
string responseString = responseMessage.Content.ReadAsStringAsync().Result;
return responseString;
}
}
}
catch (Exception e)
{
//handle this
}
return "";
}
the request executes successfully but it says that missing or wrong access_id , access_id is used to access the sonic API and every user have a unique one, this does not work i guess because the parameters access_id and format are put in the request in a wrong content type(just guessing). because if i put the strings in formUrlEncodedContent, it works fine but i'm not able to upload the file with FormUrlEncodedContent.
Is there any way i can send the file and the strings both in the same request??
BTW it's System.NET.Http namespace.
Your access_id should be sent as a querystring parameter in your url.
ApiAccessUrl += "?access_id=" + ApiAccessId;
And remove the following line:
form.Add(new StringContent(ApiAccessId), "access_id");

Categories