我使用Flurl查询API,并且我遇到了一些非常奇怪的行为。
namespace StravaAPI
{
public class StravaAPI
{
public StravaAPI()
{
_Client = new FlurlClient().WithOAuthBearerToken(ACCESS_TOKEN);
}
private FlurlClient _Client;
private const string BASE_URL = "https://www.strava.com/api/v3";
// This is my access token, should be replaced by the access token of the logged in and authenticated user
private const string ACCESS_TOKEN = "#####removed for privacy####";
/// <summary>
/// Get a summary representation of an athlete
/// </summary>
/// <param name="id">The id of the required athlete</param>
/// <returns>A summary representation of the athlete even if the indicated athlete matches the authenticated athlete.</returns>
public async Task<AthleteSummary> GetAthleteSummary(int id)
{
var url = $"{BASE_URL}/athletes/{id}";
return await GetData<AthleteSummary>(url);
}
public async Task<Athlete> GetCurrentAthlete()
{
var url = $"{BASE_URL}/athlete/";
return await GetData<Athlete>(url);
}
public async Task<T> GetData<T>(string url) where T : IData
{
Console.WriteLine($"Sending request to {url}");
T data = await _Client.WithUrl(url).GetJsonAsync<T>();
return data;
}
}
}
GetAthleteSummary的调用成功,但调用GetCurrentAthlete时返回401 Unauthorized。
如果我使用另一种传递访问令牌的方法,作为GET请求中的查询参数而不是标题,则对GetCurrentAthlete的调用成功。
上面让我相信它一定是API的一个问题,但我已经使用Fiddler向URL发送请求,包括&#34;授权&#34;标题为&#34; Bearer ####已删除隐私####&#34;这很成功。
所以这个问题并不适用于Flurl,因为它正确地通过GetAthleteSummary方法发送请求。它似乎与API无关,因为它通过Fiddler正确响应请求。所以我必须做错事......但我不知道是什么!
以下是来自API的文档的示例请求:
$ curl -G https://www.strava.com/api/v3/athlete \
-H "Authorization: Bearer 83ebeabdec09f6670863766f792ead24d61fe3f9"
$ curl -G https://www.strava.com/api/v3/athletes/227615 \
-H "Authorization: Bearer 83ebeabdec09f6670863766f792ead24d61fe3f9"
请帮忙!
如果需要更多信息,请告诉我。我很欣赏,如果有人试图运行此代码来调试它,那么您将需要我的访问令牌。任何想要它的人都可以自由地与我联系,我会将其传递出去。
干杯。
编辑:
完整示例控制台应用程序:
using System;
using System.Threading.Tasks;
using Flurl.Http;
namespace SOExample
{
class Program
{
static void Main(string[] args)
{
var api = new StravaAPI();
var summaryUser = api.GetAthleteSummary().Result;
Console.WriteLine($"Welcome {summaryUser.FirstName}");
Athlete currentUser = new Athlete();
try
{
currentUser = api.GetCurrentAthlete().Result;
Console.WriteLine($"Welcome {currentUser.FirstName}");
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException.Message);
}
var stats = api.GetCurrentAthleteStats().Result;
Console.WriteLine($"This year you have done {stats.Ytd_Run_Totals.Count} runs covering {stats.Ytd_Run_Totals.Distance} metres!");
Console.ReadKey();
}
public class StravaAPI
{
public StravaAPI()
{
_Client = new FlurlClient().WithOAuthBearerToken(ACCESS_TOKEN);
}
private FlurlClient _Client;
private const string BASE_URL = "https://www.strava.com/api/v3";
// This is my access token, should be replaced by the access token of the logged in and authenticated user
private const string ACCESS_TOKEN = "####removed for privacy####";
// This is my account id, should be replaced by the ID of the logged in and authenticated user
private const int USER_ID = ####removed for privacy####;
/// <summary>
/// Get a summary representation of an athlete
/// </summary>
/// <param name="id">The id of the required athlete</param>
/// <returns>A summary representation of the athlete even if the indicated athlete matches the authenticated athlete.</returns>
public async Task<AthleteSummary> GetAthleteSummary(int id = USER_ID)
{
var url = $"{BASE_URL}/athletes/{id}";
return await GetData<AthleteSummary>(url);
}
public async Task<Athlete> GetCurrentAthlete()
{
var url = $"{BASE_URL}/athlete/";
return await GetData<Athlete>(url);
}
//TODO: Make this based on the currentAthlete rather than a supplied ID
public async Task<Stats> GetCurrentAthleteStats(int id = USER_ID)
{
var url = $"{BASE_URL}/athletes/{id}/stats";
return await GetData<Stats>(url);
}
public async Task<T> GetData<T>(string url) where T : IData
{
Console.WriteLine($"Sending request to {url}");
T data = await new FlurlClient().WithOAuthBearerToken(ACCESS_TOKEN).WithUrl(url).GetJsonAsync<T>();
return data;
}
}
public class Athlete : AthleteSummary
{
public int? FollowerCount { get; set; }
public int? FriendCount { get; set; }
public int? MutualFriendCount { get; set; }
public AthleteType? AthleteType { get; set; }
public string DatePreference { get; set; }
/// <summary>
/// feet or metres
/// </summary>
public string MeasurementPreference { get; set; }
public string Email { get; set; }
public int? Ftp { get; set; }
/// <summary>
/// Athlete's weight in kilograms
/// </summary>
public double? Weight { get; set; }
// TODO: Add these below once we have created the required classes
// CLUBS
// BIKES
// SHOES
}
public class AthleteSummary : IData
{
public int? Id { get; set; }
public ResourceState? ResourceState { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
/// <summary>
/// URL to a 62x62 pixel profile picture
/// </summary>
public string ProfileMedium { get; set; }
/// <summary>
/// URL to a 126x124 pixel profile picture
/// </summary>
public string Profile { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string Sex { get; set; }
/// <summary>
/// pending, accepted, blocked or null - the authenticated athlete’s following status of this athlete
/// </summary>
public string Friend { get; set; }
/// <summary>
/// pending, accepted, blocked or null - the authenticated athlete’s following status of this athlete
/// </summary>
public string Follower { get; set; }
public bool? Premium { get; set; }
public DateTime? CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
}
public interface IData
{
}
public class Stats : IData
{
public double? Biggest_Ride_Distance { get; set; }
public double? Biggest_Climb_Elevation_Gain { get; set; }
public Totals Recent_Ride_Totals { get; set; }
public Totals Recent_Run_Totals { get; set; }
public Totals Recent_Swim_Totals { get; set; }
public Totals Ytd_Ride_Totals { get; set; }
public Totals Ytd_Run_Totals { get; set; }
public Totals Ytd_Swim_Totals { get; set; }
public Totals All_Ride_Totals { get; set; }
public Totals All_Run_Totals { get; set; }
public Totals All_Swim_Totals { get; set; }
}
public class Totals
{
public int? Count { get; set; }
/// <summary>
/// Distance in metres
/// </summary>
public double? Distance { get; set; }
/// <summary>
/// Moving time in seconds
/// </summary>
public int? Moving_Time { get; set; }
/// <summary>
/// Elapsed time in seconds
/// </summary>
public int? Elapsed_Time { get; set; }
public double? Elevation_Gain { get; set; }
public int? Achievement_Count { get; set; }
}
public enum AthleteType
{
Cyclist = 0,
Runner = 1
}
public enum ResourceState
{
Meta = 1,
Summary = 2,
Detailed = 3
}
}
}
结果:Output