Как связать коллекцию моделей в функции Azure, запускаемой по протоколу HTTP? - PullRequest
0 голосов
/ 13 марта 2019

У меня есть функция Azure v2. Функция вызывается запросом POST HTTP и получает список учетных записей.

Вот функция Azure:

public sealed class AccountFunction
{
    private readonly ILogger m_logger;

    public AccountFunction(ILoggerFactory loggerFactory)
    {
        m_logger = loggerFactory.CreateLogger<AccountFunction>();
    }

    [FunctionName("AccountFunction")]
    public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/accounts")] List<Account> accounts)
    {
        m_logger.LogInformation("C# HTTP trigger function processed a request.");

        return new OkObjectResult(accounts);
    }
}

Класс Account - это простое POCO:

public class Account
{
    public string Name { get; set; }
    public string Description { get; set; }
}

Я запускаю функцию и затем отправляю следующий запрос POST:

http://localhost:7071/api/v1/accounts

Я устанавливаю коллекцию JSON в теле запроса:

[{ 
    "name":"A",
    "description":"covfefe"
},
 { 
    "name":"B",
    "description":"huuuggge"
}]

Функция Azure возвращает пустую коллекцию, и в трассировках не было ошибок:

[2019-03-13 1:34:13 PM] Executing HTTP request: { 
[2019-03-13 1:34:13 PM]   "requestId": "3ee0483f-7e26-4bbf-a4ea-550dbbd86563", [2019-03-13 1:34:13 PM]   "method": "POST", 
[2019-03-13 1:34:13 PM]   "uri": "/api/v1/accounts" 
[2019-03-13 1:34:13 PM] } 
[2019-03-13 1:34:13 PM] Request successfully matched the route with name 'AccountFunction' and template 'api/v1/accounts' 
[2019-03-13 1:34:14 PM] Executing 'AccountFunction' (Reason='This function was programmatically called via the host APIs.', Id=92b40b1a-8bb1-4699-a590-d057c5eb7353) 
[2019-03-13 1:34:14 PM] C# HTTP trigger function processed a request. 
[2019-03-13 1:34:14 PM] Executed 'AccountFunction' (Succeeded, Id=92b40b1a-8bb1-4699-a590-d057c5eb7353) [2019-03-13 1:34:14 PM] Executed HTTP request: { 
[2019-03-13 1:34:14 PM]   "requestId": "3ee0483f-7e26-4bbf-a4ea-550dbbd86563",
[2019-03-13 1:34:14 PM]   "method": "POST", [2019-03-13 1:34:14 PM]   "uri": "/api/v1/accounts", 
[2019-03-13 1:34:14 PM]   "identities": [ [2019-03-13 1:34:14 PM]     { 
[2019-03-13 1:34:14 PM]       "type": "WebJobsAuthLevel", 
[2019-03-13 1:34:14 PM]       "level": "Admin" 
[2019-03-13 1:34:14 PM] } 
[2019-03-13 1:34:14 PM]   ], 
[2019-03-13 1:34:14 PM]   "status": 200, 
[2019-03-13 1:34:14 PM]   "duration": 517 
[2019-03-13 1:34:14 PM] }

При отладке функции я обнаружил, что параметр с учетными записями не был правильно заполнен:

Accounts is empty

Вопрос

Как связать коллекцию моделей в функции Azure?

Я использую последние инструменты Azure Function Core 2.4.419 и среду выполнения Azure Function 2.0.12332.0.

Приложение функций предназначено для .NET Core 2.1.

Обновление

Я пробовал несколько других вариантов.

Первый должен иметь новый класс Accounts, производный от List<Account>, но результат тот же.

public class Accounts : List<Account>
{        
}

Второй тест - учетные записи, содержащие список учетных записей в качестве свойства:

public class Accounts
{        
    public List<Account> Items {get;set;}
}

Тогда опять тот же результат, за исключением того, что возвращаемый объект теперь:

{
    "items": null
}

1 Ответ

0 голосов
/ 13 марта 2019

Я попытался и воспроизвел эту проблему также локально. Сначала я попытался с одним Account объектом, а затем обновил сигнатуру метода, чтобы принять List<Account>. При быстрой проверке я обнаружил следующую проблему GitHub , описывающую это поведение.

Существует комментарий по этому вопросу, который относится к следующей строке исходного файла , где код явно исключает массивы

var additionalBindingData = parsed.Children<JProperty>()
    .Where(p => p.Value != null && (p.Value.Type != JTokenType.Array))

Кажется, что единственный способ достичь этого - это обходной путь .

Другой подход заключается в использовании метода расширения ReadAsAsync<T>.

namespace StackOverflow.AccountsQuestion
{
    public sealed class AccountFunction
    {
        private readonly ILogger m_logger;

        public AccountFunction(ILoggerFactory loggerFactory)
        {
            m_logger = loggerFactory.CreateLogger<AccountFunction>();
        }

        [FunctionName("AccountFunction")]
        public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/accounts")] HttpRequestMessage request)
        {
            m_logger.LogInformation("C# HTTP trigger function processed a request.");
            var accounts = await request.Content.ReadAsAsync<List<Account>>();
            return new OkObjectResult(accounts);
        }
    }

    public class Account
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }
}
...