我正在开发一个内部控制台应用程序,该应用程序允许用户从我们的分页 JSON API 获取全套结果。我们有许多不同的类型,但都属于相同的基本结构。
public class Tickets
{
public Ticket[] tickets { get; set; }
public string nextPageURL { get; set; }
public string previousPageURL { get; set; }
public int recordCount { get; set; }
}
public class Ticket
{
...
}
我有一个异步任务来进行调用并循环分页结果并将结果发送到单个 JSON 文件中。但我想让它变得通用,而不是本质上相同的代码重复 17 次,每种类型一个。
我当前的代码是:
private static async Task<List<Ticket>> GetTicketsAsync(Action<Tickets> callBack = null)
{
var tickets = new List<Ticket>();
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"-----")));
httpClient.BaseAddress = new Uri("-----");
var nextUrl = "/api/tickets";
do
{
await httpClient.GetAsync(nextUrl)
.ContinueWith(async (ticketSearchTask) =>
{
var response = await ticketSearchTask;
if (response.IsSuccessStatusCode)
{
string jsonString = await response.Content.ReadAsStringAsync();
try
{
var result = JsonSerializer.Deserialize<Tickets>(jsonString);
if (result != null)
{
// Build the full list to return later after the loop.
if (result.tickets.Any())
tickets.AddRange(result.tickets.ToList());
// Run the callback method, passing the current page of data from the API.
if (callBack != null)
callBack(result);
// Get the URL for the next page
nextUrl = (result.nextPageURL != null) ? result.nextPageURL : string.Empty;
}
} catch (Exception ex)
{
Console.WriteLine($"\n We ran into an error: {ex.Message}");
nextUrl = string.Empty;
}
}
else
{
// End loop if we get an error response.
nextUrl = string.Empty;
}
});
} while (!string.IsNullOrEmpty(nextUrl));
return tickets;
}
private static void TicketsCallBack(Tickets tickets)
{
if (tickets != null && tickets.count > 0)
{
foreach (var ticket in tickets.tickets)
{
Console.WriteLine($"fetched ticket: {ticket.id}");
}
}
}
我已经开始使用通用方法,但引用第二级(在他的实例中为票据对象)给我带来了问题以及获取 nextPageURL。保留回调结构以保持控制台随着已处理的数据滴答作响也很好。
private static async Task<List<T>> GetAsync<T>(string type, Action<T> callBack = null, string ticketId = null)
{
var results = new List<T>();
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"-----")));
httpClient.BaseAddress = new Uri("-----");
var nextUrl = $"/api/{type}.json";
do
{
await httpClient.GetAsync(nextUrl)
.ContinueWith(async (searchTask) =>
{
var response = await searchTask;
if (response.IsSuccessStatusCode)
{
string jsonString = await response.Content.ReadAsStringAsync();
try
{
var result = JsonSerializer.Deserialize<T>(jsonString);
if (result != null)
{
// Build the full list to return later after the loop.
if (result.tickets.Any())
results.AddRange(result.tickets.ToList());
// Run the callback method, passing the current page of data from the API.
if (callBack != null)
callBack(result);
// Get the URL for the next page
nextUrl = (result.GetType().GetProperty("nextPageURL") != null) ? result.GetType().GetProperty("nextPageURL").ToString() : string.Empty;
}
}
catch (Exception ex)
{
Console.WriteLine($"\nWe ran into an error: {ex.Message}");
nextUrl = string.Empty;
}
}
else
{
// End loop if we get an error response.
nextUrl = string.Empty;
}
});
} while (!string.IsNullOrEmpty(nextUrl));
return results;
}
非常感谢任何帮助。
首先,如果您有多个遵循相同结构的对象,您可能会使其通用:
public class Paged<T>
{
T[] Data { get; set;}
string nextPageURL { get; set;}
string previousPageURL { get; set;}
int recordCount { get; set; }
}
您应该能够或多或少地使用它来代替门票对象:
private static async Task<List<T>> GetAsync<T>(string type, Action<Paged<T>> callBack = null)
...
var result = JsonSerializer.Deserialize<Paged<T>>(jsonString);
如果由于向后兼容性而无法使用通用类型,您仍然可以为所有类似的类创建通用接口:
public interface IPaged<T>
{
public T[] Data{ get;}
public string nextPageURL { get; set; }
public string previousPageURL { get; set; }
public int recordCount { get; set; }
}
public class Tickets : IPaged<Ticket>
{
public Ticket[] Data => tickets;
public Ticket[] tickets { get; set; }
public string nextPageURL { get; set; }
public string previousPageURL { get; set; }
public int recordCount { get; set; }
}
private static async Task<List<TData>> GetAsync<T, TData>(string type, Action<T> callBack = null) where T : IPaged<TData>
{
...
var result = JsonSerializer.Deserialize<T>(jsonString);
...
}
var allTickets = await GetAsync<Tickets, Ticket>(...);
但是,我会考虑使用 IAsyncEnumerable 接口,例如:
private static async IAsyncEnumerable<T[]> GetAsync<T>(string type){
...
var result = JsonSerializer.Deserialize<Paged<T>>(jsonString);
if (result != null)
{
yield return result.Data;
}
...
这应该在获取数据的代码和处理数据的代码之间提供更清晰的分离。