我试图通过TFS REST API执行补丁http请求来更改TFS中的一个字段。我已经尝试了几种方法,但我总是遇到400错误。这就是我现在所拥有的:
public void SetFieldValue(string value, string path, int id)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(PatchwebAPIUrl("wit/workitems", id.ToString()));
httpWebRequest.ContentType = "application/json-patch+json";
httpWebRequest.Method = "PATCH";
httpWebRequest.Headers["Authorization"] = "Basic" + Base64authorizationToken();
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "[{\"op\":\"replace\"," +
$"\"path\":\"{path}\"," +
$"\"value\":\"{value}\"}}]";
streamWriter.Write(JsonConvert.SerializeObject(json));
streamWriter.Flush();
streamWriter.Close();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
}
}
调用此方法的测试方法:
[TestMethod()]
public void setFieldValue()
{
TFSWebAPIImplementation webAPI = new TFSWebAPIImplementation();
webAPI.SetFieldValue("654321", "/fields/Custom.Tracking", 61949);
}
PatchwebAPIUrl(" ...")方法很好,并且返回一个好的URL,当我导航到它时,我得到了我想要编辑的JSON数据。我不是100%的路径变量,但它的使用方法与Microsoft提供的示例相同。授权是有效的,只是基于这样一个事实:当我弄乱它时,我得到401。
答案 0 :(得分:1)
这是我的示例代码:
工作类别:
public class WorkItemAtrr
{
[JsonProperty("id")]
public int id;
[JsonProperty("rev")]
public int rev;
[JsonProperty("fields")]
public Dictionary<string, string> fields;
[JsonProperty("_links")]
public Dictionary<string, Link> _links;
[JsonProperty("relations")]
public List<Relation> relations;
[JsonProperty("url")]
public string url;
}
public class Link
{
[JsonProperty("href")]
public string href;
}
public class Relation
{
[JsonProperty("rel")]
public string rel;
[JsonProperty("url")]
public string url;
[JsonProperty("attributes")]
public RelationAttribute attributes;
}
public class RelationAttribute
{
[JsonProperty("comment")]
public string comment = "";
[JsonProperty("isLocked")]
public bool isLocked;
}
新字段和更新字段的类:
public class NewField
{
[JsonProperty("op")]
public string op = "add";
[JsonProperty("path")]
public string path;
[JsonProperty("value")]
public object value;
}
例外类:
public class RestApiExceptionContainer
{
[JsonProperty("id")]
public int id;
[JsonProperty("innerException")]
public string innerException;
[JsonProperty("message")]
public string message;
[JsonProperty("typeName")]
public string typeName;
[JsonProperty("typeKey")]
public string typeKey;
[JsonProperty("errorCode")]
public int errorCode;
[JsonProperty("evenId")]
public int eventId;
}
更新工作项的方法:
private static WorkItemAtrr UpdateWorkItemRest()
{
Dictionary<string, string> _fields = new Dictionary<string, string>();
_fields.Add("REFERENCE_NAME", "VALUE");
var _updatedWi = UpdateWorkItem("ID", _fields).Result;
}
准备请求的方法:
public async Task<WorkItemAtrr> UpdatedWorkItem(int pId, Dictionary<String, String> pFields)
{
//PATCH https://{instance}/DefaultCollection/_apis/wit/workitems/{id}?api-version={version}
string _query_url = String.Format("https://YOUR_SERVER/DefaultCollection/_apis/wit/workitems/{id}?api-version=1.0", pId);
List<Object> flds = new List<Object>();
foreach (var _key in pFields.Keys)
flds.Add(new NewField { op = "add", path = "/fields/" + _key, value = pFields[_key] });
HttpResponseMessage _response = await DoRequest(_query_url, JsonConvert.SerializeObject(flds), ClientMethod.PATCH);
return JsonConvert.DeserializeObject<WorkItemAtrr>(await ProcessResponse(_response));
}
请求的通用方法:
private async Task<HttpResponseMessage> DoRequest(string pRequest, string pBody, ClientMethod pClientMethod)
{
try
{
HttpClientHandler _httpclienthndlr = new HttpClientHandler();
//update for your auth
if (UseDefaultCredentials) _httpclienthndlr.Credentials = CredentialCache.DefaultCredentials;
else if (TFSDomain == "") _httpclienthndlr.Credentials = new NetworkCredential(TFSUserName, TFSPassword);
else _httpclienthndlr.Credentials = new NetworkCredential(TFSUserName, TFSPassword, TFSDomain);
using (HttpClient _httpClient = new HttpClient(_httpclienthndlr))
{
switch (pClientMethod)
{
case ClientMethod.GET:
return await _httpClient.GetAsync(pRequest);
case ClientMethod.POST:
return await _httpClient.PostAsync(pRequest, new StringContent(pBody, Encoding.UTF8, "application/json"));
case ClientMethod.PATCH:
var _request = new HttpRequestMessage(new HttpMethod("PATCH"), pRequest);
_request.Content = new StringContent(pBody, Encoding.UTF8, "application/json-patch+json");
return await _httpClient.SendAsync(_request);
default:
return null;
}
}
}
catch (Exception _ex)
{
throw new Exception("Http Request Error", _ex);
}
}
回应的通用方法:
public async Task<string> ProcessResponse(HttpResponseMessage pResponse)
{
string _responseStr = "";
if (pResponse != null)
{
if (pResponse.IsSuccessStatusCode)
_responseStr = await pResponse.Content.ReadAsStringAsync();
else
{
_responseStr = await pResponse.Content.ReadAsStringAsync();
var _error = JsonConvert.DeserializeObject<RestApiExceptionContainer>(_responseStr);
throw new RestApiException(_error);
}
}
return _responseStr;
}
答案 1 :(得分:0)
A 400表示请求格式错误。换句话说,客户端发送到服务器的数据流不符合规则。
对于具有JSON有效负载的REST API,通常使用400来根据服务的API规范以某种方式指示JSON无效。
因此,问题是由JSON主体引起的。
试试如下:
public int NewWaySum(int a, int b) => a + b;
您还可以使用以下示例通过REST API使用PATCH方法更新字段,它适用于我:
string json = "[{\"op\":\"replace\",\"path\":\"/fields/System.Title\",\"value\":\"Title\"}]";
答案 2 :(得分:0)
您也可以使用nugate包Microsoft.TeamFoundationServer.Client。
从here:
连接到团队项目using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Common;
...
//create uri and VssBasicCredential variables
Uri uri = new Uri(url);
VssBasicCredential credentials = new VssBasicCredential("", personalAccessToken);
using (ProjectHttpClient projectHttpClient = new ProjectHttpClient(uri, credentials))
{
IEnumerable<TeamProjectReference> projects = projectHttpClient.GetProjects().Result;
}
我的代码添加评论:
JsonPatchDocument PatchDocument = new JsonPatchDocument();
PatchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.History",
Value = "Changes from script"
}
);
VssCredentials Cred = new VssCredentials(true);
WorkItemTrackingHttpClient WIClient = new WorkItemTrackingHttpClient(new Uri("http://YOUR_SERVER/tfs/DefaultCollection"), Cred);
WorkItem result = WIClient.UpdateWorkItemAsync(PatchDocument, id).Result;
答案 3 :(得分:0)
好的家伙,很遗憾你的解决方案都没有用,我认为这是因为TFS API不喜欢外面总有一组额外的大括号。这是解决我问题的解决方案:
public void SetFieldValue(string value, string path, int id)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Base64authorizationToken());
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
using (JsonWriter writer = new JsonTextWriter(sw))
{
writer.Formatting = Formatting.Indented;
writer.WriteStartArray(); // [
writer.WriteStartObject(); // {
writer.WritePropertyName("op"); // "Product:"
writer.WriteValue("replace");
writer.WritePropertyName("path");
writer.WriteValue(path);
writer.WritePropertyName("value");
writer.WriteValue(value);
writer.WriteEndObject(); //}
writer.WriteEnd(); // ]
}
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, PatchwebAPIUrl("wit/workitems", id.ToString())) { Content = new StringContent(sb.ToString().Trim(new char[] {'{','}'}), Encoding.UTF8, "application/json-patch+json") };
var response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
}
}
}