在 Azure DevOps API C# .net core 3 中创建工作项时出现问题

Having issue creating work item in Azure DevOps API C# .net core 3

我已通读 API、在线帮助文​​章等,但在创建工作项时遇到 400 错误。

我有一个合适的令牌等。我不确定我是不是一次做太多事情。我需要创建一个父问题项和 n 个链接到它的子子任务。我正在为我的经理创建一个 Azure DevOps 帮助应用程序,以便更快地从我们的票务系统创建任务。对于用户,我传递电子邮件地址(DevOps 中的用户),对于 sprint 和 projectID,我传递它们的名称。

我在创建第一个父项时得到的错误是:

{
   "$id":"1",
   "innerException":null,
   "message":"You must pass a valid patch document in the body of the request.",
   "typeName":"Microsoft.VisualStudio.Services.Common.VssPropertyValidationException, Microsoft.VisualStudio.Services.Common",
   "typeKey":"VssPropertyValidationException",
   "errorCode":0,
   "eventId":3000
}

我不知道我在这个补丁调用中传递了什么错误。

来自appSetttings.json

 "AzureDevOpsConfig": {
    "ApiURL": "https://dev.azure.com",
    "Token": "........" }

这是我的代码: ...

    [HttpPost("create")]
    public async Task<string> CreateStory(AzureStoryCreationInfo model)
    {
        var workItemData = new List<dynamic>()
        {
            new
            {
                op = "add",
                path = "/fields/System.Title",
                value = model.Title
            },
            new
            {
                op = "add",
                path = "/fields/System.IterationPath",
                value = $"{model.ProjectID}\{model.Sprint}"
            },
            new
            {
                op = "add",
                path = "/fields/System.Description",
                value = model.Description ?? string.Empty
            }
        };

        if (!string.IsNullOrWhiteSpace(model.User))
        {
            workItemData.Add(new
            {
                op = "add",
                path = "/fields/System.AssignedTo",
                value = model.User
            });
        }

        var parentResponse = await this.CreateWorkItem(model.Organization, model.ProjectID, model.WorkItemType, workItemData);
        var parentResponseData = JsonConvert.DeserializeObject<AzureWorkItemCreateResponse>(parentResponse);

        if (parentResponseData != null && parentResponseData.Id > 0 && model.Tasks != null && model.Tasks.Any())
        {
            foreach (var task in model.Tasks)
            {
                var workItemTaskData = new List<dynamic>()
                {
                    new
                    {
                        op = "add",
                        path = "/fields/System.Parent",
                        value = parentResponseData.Id
                    },
                    new
                    {
                        op = "add",
                        path = "/fields/System.Title",
                        value = task.Title
                    },
                    new
                    {
                        op = "add",
                        path = "/fields/System.IterationPath",
                        value = $"{model.ProjectID}\{model.Sprint}"
                    }//,
                    //new
                    //{
                    //    op = "add",
                    //    path = "/fields/System.Description",
                    //    value = string.Empty
                    //}
                };

                if (!string.IsNullOrWhiteSpace(task.User))
                {
                    workItemTaskData.Add(new
                    {
                        op = "add",
                        path = "/fields/System.AssignedTo",
                        value = task.User
                    });
                }

                var taskResponse = await CreateWorkItem(model.Organization, model.ProjectID, "Task", workItemTaskData);

                //Maybe Check if tasks fail
                var taskResponseData = JsonConvert.DeserializeObject<AzureWorkItemCreateResponse>(taskResponse);
            }

        }

        return await Task.FromResult(parentResponse);
    }

private async Task<string> CreateWorkItem(
            string organization,
            string projectIDorName,
            string workItemType,
            List<dynamic> values)
        {
            var workItemValue = new StringContent(JsonConvert.SerializeObject(values), Encoding.UTF8, "application/json-patch+json");

            return await PostData($"{organization}/{projectIDorName}/_apis/wit/workitems/${workItemType}?api-version=5.1&validateOnly=true", workItemValue);

            //var result = await PostData($"{organization}/{projectIDorName}/_apis/wit/workitems/${workItemType}?api-version=5.1", workItemValue);
        }

private async Task<string> PostData(string subUrl, StringContent data, string baseApi = null)
{
    if (client == null)
        client = new HttpClient();

    if (baseApi == null)
        baseApi = this.config.Value.ApiURL;

    client.DefaultRequestHeaders.Clear();

    client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json-patch+json"));

    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
        Convert.ToBase64String(
            Encoding.ASCII.GetBytes(
                string.Format("{0}:{1}", "", this.config.Value.Token))));

    using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Patch, $"{baseApi}/{subUrl}"))
    using (HttpResponseMessage response = await client.SendAsync(request))
    {
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

...

我什至也尝试过这个:

JsonPatchDocument test = new JsonPatchDocument(
                values.Select(val => new Microsoft.AspNetCore.JsonPatch.Operations.Operation(val.op, val.path, null, val.value)).ToList(),
                new Newtonsoft.Json.Serialization.DefaultContractResolver());
            var workItemValue = new StringContent(JsonConvert.SerializeObject(test), Encoding.UTF8, "application/json-patch+json");

在您的示例中,您使用了 PATCH 方法。但是,您必须使用 POST: Work Items - Create。此外,您不传递任何内容,PostData 方法不使用 StringContent data。以下是根据您的代码创建任务的示例:

        string pat = "<your_pat>";

        var workItemTaskData = new List<dynamic>()
        {
             new
                {
                    op = "add",
                    path = "/fields/System.Title",
                    value = "MyTitle"
                }
        };

        var workItemValue = new StringContent(JsonConvert.SerializeObject(workItemTaskData), Encoding.UTF8, "application/json-patch+json");

        var client = new HttpClient();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json-patch+json"));
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "", pat))));


        using (HttpResponseMessage response = client.PostAsync("https://dev.azure.com/<org_name>/<team_project_name>/_apis/wit/workitems/$Task?api-version=5.1", workItemValue).Result)
        {
            response.EnsureSuccessStatusCode();
            var result = response.Content.ReadAsStringAsync().Result;
        }