我一直在尝试学习如何使用Account API,并决定下载和使用文档中提供的XeroOAuth2Sample。(https://github.com/XeroAPI/xero-netstandard-oauth2-samples)
因此,在HomeController.cs中,有一个示例HTTPGet方法用于从Xero文档提供的API检索发票总数。代码如下:
[HttpGet]
[Authorize]
public async Task<IActionResult> OutstandingInvoices()
{
var token = await _tokenStore.GetAccessTokenAsync(User.XeroUserId());
var connections = await _xeroClient.GetConnectionsAsync(token);
if (!connections.Any())
{
return RedirectToAction("NoTenants");
}
var data = new Dictionary<string, int>();
foreach (var connection in connections)
{
var accessToken = token.AccessToken;
var tenantId = connection.TenantId.ToString();
var organisations = await _accountingApi.GetOrganisationsAsync(accessToken, tenantId);
var organisationName = organisations._Organisations[0].Name;
var outstandingInvoices = await _accountingApi.GetInvoicesAsync(accessToken, tenantId, statuses: new List<string>{"AUTHORISED"}, where: "Type == "ACCREC"");
data[organisationName] = outstandingInvoices._Invoices.Count;
}
var model = new OutstandingInvoicesViewModel
{
Name = $"{User.FindFirstValue(ClaimTypes.GivenName)} {User.FindFirstValue(ClaimTypes.Surname)}",
Data = data
};
return View(model);
}
因此,我一直在尝试通过制作一个页面来练习和探索API,该页面最终将从API端点调用联系人。我创建了一个Contact.cs Model类,看起来像这样:
public class Contact
{
public string ContactID { get; set; }
public string ContactStatus { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string SkypeUserName { get; set; }
public string BankAccountDetails { get; set; }
public string TaxNumber { get; set; }
public string AccountsReceivableTaxType { get; set; }
public string AccountsPayableTaxType { get; set; }
public List<Address> Addresses { get; set; }
public List<Phone> Phones { get; set; }
public DateTime UpdatedDateUTC { get; set; }
public bool IsSupplier { get; set; }
public bool IsCustomer { get; set; }
public string DefaultCurrency { get; set; }
}
然后,我创建了一个ContactViewModel.cs,它具有我希望稍后在Razor View页面上显示的属性,代码如下:
public class ContactViewModel
{
public string ContactID { get; set; }
public string ContactStatus { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsSupplier { get; set; }
public bool IsCustomer { get; set; }
public string DefaultCurrency { get; set; }
}
然后,我有我的ContactsViewModel,它是ContactViewModel的联系人列表:
public class ContactsViewModel
{
public List<ContactViewModel> Contacts { get; set; }
}
因此,当我尝试为我的联系人发出/调用HTTPGet请求时,我的问题出现了,代码如下:
[HttpGet]
[Authorize]
public async Task<IActionResult> Contacts()
{
var token = await _tokenStore.GetAccessTokenAsync(User.XeroUserId());
var connections = await _xeroClient.GetConnectionsAsync(token);
if (!connections.Any())
{
return RedirectToAction("NoContacts");
}
foreach (var connection in connections)
{
var accessToken = token.AccessToken;
var tenantId = connection.TenantId.ToString();
var contactList = await _accountingApi.GetContactsAsync(accessToken, tenantId);
List<ContactsViewModel> contacts = new List<ContactsViewModel>();
foreach (var contact in contactList)
{
contacts.Add(new ContactViewModel
{
ContactID = contact.ContactID,
ContactStatus = contact.ContactStatus,
Name = contact.Name,
FirstName = contact.FirstName,
LastName = contact.LastName,
IsSupplier = contact.IsSupplier,
IsCustomer = contact.IsCustomer
});
}
contacts.AddRange(contactList);
}
var model = new ContactsViewModel()
{
// Contacts = contacts
};
return View(model);
}
因此,第一个错误如下:
ApiException: Error calling GetContacts: {"title":"Unauthorized","status":401,"detail":"AuthorizationUnsuccessful","instance":"354ff497-d29f-468b-9e1c-4345e9ce8123"}
从"GetContactsAsync"方法返回:
var contactList = await _accountingApi.GetContactsAsync(accessToken, tenantId);
我不确定是否有我遗漏的特定值,我也需要传递这些值,从而导致这个错误?我在Xero文档中找不到任何与此相关的信息。尽管将鼠标悬停在GetcontactsAsync上会显示此信息以获取更多信息:
(awaitable) Task<Xero.NetStandard.OAuth2.Model.Contacts> IAccountingApiAsync.GetContactsAsync(string accessToken, stringXeroTenantId, [System.DateTime? ifModifiedSince = null], [string where = null], [string order = null], [List<System.Guid> iDs = null], [int? page = null], [bool? includeArchived = null])
最后,contactList似乎抛出了标题所建议的关于"GetEnumerator"的错误,当使用AddRange将联系人添加到contactsList时,此错误显示为
cannot convert from 'Xero.NetStandard.OAuth2.Model.Contacts' to 'System.Collections.Generic.IEnumerable<XeroOAuth2Sample.Models.ContactsViewModel>'
GetContactsAsync中是否有我明显遗漏的内容?提前感谢您的阅读和帮助。
联系人端点需要一个额外的OAuth2.0作用域,而不是示例中要求的默认作用域。您可以在此处看到示例使用的范围集:https://github.com/XeroAPI/xero-netstandard-oauth2-samples/blob/master/XeroOAuth2Sample/XeroOAuth2Sample/Startup.cs#L105L106
您可以在我们的文档中看到可以请求的全套作用域:https://developer.xero.com/documentation/oauth2/scopes
对于您的情况,如果您最终想要更新/创建联系人,则需要使用accounting.contacts或accounting.contacts.read范围来读取联系人或accounting.contacts范围
编辑:如果你只想阅读联系人,你需要请求并让用户同意该帐户。contacts.read scope。如果你想更新/创建联系人,你需要请求并让用户同意accounting.contacts范围。accounting.contacts范围将允许读取和写入
因此,在向startup.cs:添加更多选项后
options.Scope.Add("accounting.contacts");
options.Scope.Add("accounting.contacts.read");
我按照建议再次完成了静电注册过程,以重新发送新的范围。我认为在HomeController中更正了Contacts((HTTPGet方法的语法和一些轻微的逻辑。
[HttpGet]
[Authorize]
public async Task<IActionResult> Contacts()
{
var token = await _tokenStore.GetAccessTokenAsync(User.XeroUserId());
var connections = await _xeroClient.GetConnectionsAsync(token);
ContactsViewModel contacts = new ContactsViewModel();
contacts.Contacts = new List<ContactViewModel>();
if (!connections.Any())
{
return RedirectToAction("NoContacts");
}
foreach (var connection in connections)
{
var accessToken = token.AccessToken;
var tenantId = connection.TenantId.ToString();
Contacts contactList = await _accountingApi.GetContactsAsync(accessToken, tenantId);
contacts.Contacts.AddRange(contactList._Contacts.Select(contact => new ContactViewModel()
{
ContactID = contact.ContactID.ToString(),
ContactStatus = contact.ContactStatus.ToString(),
Name = contact.Name,
FirstName = contact.FirstName,
LastName = contact.LastName,
IsSupplier = contact.IsSupplier.Value,
IsCustomer = contact.IsCustomer.Value
}).ToList());
}
return View(contacts);
}