在 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;
}
我已通读 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;
}