c# - C# - 如何使用 HttpClient 对象从 JSON API 中提取 CSV 文件?
问题描述
我的公司使用 C# .net 应用程序以编程方式从供应商网站以 CSV 文件的形式检索数据。但是,供应商最近将其 API 更改为 JSON API。我是一个新手程序员,对 JSON API 调用没有任何经验。当供应商注册我的新 API 用户 ID 时,他们提供了一个示例,说明如何使用curl下载必要的 csv 数据文件。这是示例:
echo 'Going to log in to sso'
data=$(curl -c cookies.txt -X POST https://sso.theice.com/api/authenticateTfa -H "Content-Type: application/json" \
--data '{"userId":"yourApiUserId","password":"yourpassword","appKey":"yourAppKey"}')
echo 'Going to download icecleared_oil_2020_06_26.dat file from Settlement_Reports_CSV/Oil'
curl -b cookies.txt -O ' https://downloads2.theice.com/Settlement_Reports_CSV/Oil/icecleared_oil_2020_06_26.dat
由于我没有使用 Curl 的经验,因此我使用https://curl.olsh.me/将上面的示例转换为 C#。这样做会生成以下代码:
//Authenticate userId (and get cookie?)
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://sso.theice.com/api/authenticateTfa"))
{
request.Content = new StringContent("{\"userId\":\"yourApiUserId\",\"password\":\"yourpassword\",\"appKey\":\"yourAppKey\"}");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
var response = await httpClient.SendAsync(request);
}
}
//Using authentication cookie, from previous code block, retrieve dat file
var handler = new HttpClientHandler();
handler.UseCookies = false;
using (var httpClient = new HttpClient(handler))
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), "https://downloads2.theice.com/Settlement_Reports_CSV/Oil/icecleared_oil_2020_06_26.dat"))
{
request.Headers.TryAddWithoutValidation("Cookie", "cookies.txt");
var response = await httpClient.SendAsync(request);
}
}
我将此代码集成到我的项目中,并且能够让它对我的用户 ID(POST 请求)进行身份验证,但我终生无法弄清楚如何从第二个代码块(GET 请求)中检索 DAT 文件。我观察到无论我是否使用 POST 请求成功登录,GET 请求的响应都是相同的。在这一点上,我假设 POST 请求中的验证 cookie 没有被 GET 请求获取,但即使它是我也不知道如何从 GET 请求中的响应对象中提取文件。
谁能建议我需要做什么?以下是已集成到我的项目中的代码:
public async Task DownloadAsync(List<string> iceReportFileNamesFullUrl)
{
await AuthenticateUserCredentialsOnICE();
await RetrieveIceRportFiles(iceReportFileNamesFullUrl);
}
private async Task AuthenticateUserCredentialsOnICE()
{
using (HttpClient httpClient = new HttpClient())
{
using (HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("POST"), "https://sso.theice.com/api/authenticateTfa"))
{
request.Content = new StringContent("{\"userId\":\"xxxxxxxxx\",\"password\":\"xxxxxxxxxx\",\"appKey\":\"xxxxxxxxxxx\"}");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
HttpResponseMessage response = await httpClient.SendAsync(request);
var value = response.Content.ReadAsStringAsync();
}
}
}
private static async Task RetrieveIceRportFiles(List<string> iceReportFileNamesFullUrl)
{
foreach (string iceReportUrl in iceReportFileNamesFullUrl)
{
HttpClientHandler handler = new HttpClientHandler();
handler.UseCookies = false;
using (HttpClient httpClient = new HttpClient(handler))
{
using (HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("GET"), iceReportUrl))
{
request.Headers.TryAddWithoutValidation("Cookie", "cookies.txt");
HttpResponseMessage response = await httpClient.SendAsync(request);
var value = response.Content.ReadAsStringAsync();
}
}
}
}
-更新:2020 年 8 月 25 日-
谢谢大家的意见。您明确表示我没有从 POST 请求中捕获 cookie。经过一些研究(this、this和this),我更新了代码,以便它现在可以成功下载 CSV/DAT 文件。我已经在下面发布了我的内容,尽管它仍然需要错误处理、检查登录失败等。我也相信会有重要的反馈。
这里是:
private CookieContainer authenticationCookieContainer;
//main method for downloading file
public async Task DownloadAsync(string iceReportDownloadUrl)
{
await AuthenticateUserCredentialsOnIceAsync();
await SendDownloadReportRequestAsync(iceReportDownloadUrl);
}
通过 JSON API 对凭证进行身份验证并将身份验证 cookie 存储在 authenticationCookieContainter
private async Task AuthenticateUserCredentialsOnIceAsync()
{
//This will contain the ICE authentication cookies
//if the login is successful
authenticationCookieContainer = new CookieContainer();
//Create and assign handler for proxy config and cookie storage
using (HttpClientHandler handler = new HttpClientHandler() { CookieContainer = authenticationCookieContainer })
{
using (HttpClient httpClient = new HttpClient(handler))
{
using (HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("POST"), "https://sso.theice.com/api/authenticateTfa"))
{
request.Content = new StringContent("{\"userId\":\"xxxxxxxxxxx\",\"password\":\"xxxxxxxxxxxxxx\",\"appKey\":\"xxxxxxxxxxxxx\"}");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
HttpResponseMessage response = await httpClient.SendAsync(request);
}
}
}
}
如果登录成功, authenticationCookieContainer 将包含登录 cookie 并允许您在指定的 url 请求数据
private async Task SendDownloadReportRequestAsync(string iceReportDownloadUrl)
{
if (authenticationCookieContainer != null)
{
//Create and assign handler for proxy config and cookie storage
using (HttpClientHandler handler = new HttpClientHandler() { CookieContainer = authenticationCookieContainer })
{
//Set to true to use the cookies obtained during authentication call
handler.UseCookies = true;
using (HttpClient httpClient = new HttpClient(handler))
{
Uri iceReportUri = new Uri(iceReportDownloadUrl);
//Request the ICE data file using the url passed through
using (HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("GET"), iceReportUri.AbsoluteUri))
{
HttpResponseMessage response = await httpClient.SendAsync(request);
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
//Write the extracted file to the file path
byte[] content = StreamToByteArray(responseStream);
File.WriteAllBytes(@"C:\Users\my.name\Desktop\" + iceReportUri.Segments.Last(), content);
}
}
}
}
}
}
private static byte[] StreamToByteArray(Stream stream)
{
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
解决方案
推荐阅读
- jquery - DataTables 按列排序数据
- javascript - 如何在循环中获得正确的二进制数组以可视化冒泡排序?
- azure - 通过 Visual Studio 开发时的 Azure 函数编程启用/禁用
- node.js - ReferenceError:批评未在 Node/Express 应用程序中定义
- javascript - 如何等到响应来自 angularjs 中的 $resource 请求?
- r - 基于R中现有数据框中观察值之间的链接的新数据框
- c++11 - Switch 语句的大 O 在其 Case 中具有函数调用?
- python - 为什么我在 facenet 中收到此错误?
- rust - 为什么在 if let 中添加分号可以避免借用检查器错误?
- python - 为什么这个脚本在 Python 中这么慢?