How to use info from rest api response C# - c#

I am trying to validate an API response but I can't seem to understand how to use the content of the response.
This is my response :
"{\"On\": false, \"value\": null,}"
And I would like to test for the value of "On" (if its true then... or false then...).
Here is my code so far :
using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Net.Http.Headers;
namespace APITest
{
class Program
{
static void Main(string[] args)
{
PostRequest("My API");
Console.ReadKey();
}
async static void PostRequest(string url)
{
IEnumerable<KeyValuePair<string, string>> queries = new
List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("Test","1")
};
HttpContent q = new FormUrlEncodedContent(queries);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await
client.PostAsync(url,q))
{
using (HttpContent content = response.Content)
{
string mycontent = await
content.ReadAsStringAsync();
HttpContentHeaders headers = content.Headers;
Console.WriteLine(mycontent);
}
}
}
}
}
}

You create a class which represents that JSON structure and use a JSON serializor to de-serialize your string representation to an object of that class and use it as needed
Here i show you do it with JSON.NET
string mycontent = await content.ReadAsStringAsync();
var result= Newtonsoft.Json.JsonConvert.DeserializeObject<ApiResult>(mycontent);
if (result!= null)
{
if (result.On)
{
//do something, may be read, result.Value
}
else
{
//do something else
}
}
Assuming you have a class like this
public class ApiResult
{
public bool On { set; get; }
public string Value { set; get; }
}

Related

Parameterized query with c # HttpClient

I am sending an httpclient request as below. I want to send a parameter with a get request. How can I do that ? Or how can I use it properly?
For example; http://localhost:3000/users?business_code=123
using System.Net.Http;
using System;
using System.Threading.Tasks;
using MyApplication.Models;
namespace MyApplication.Api
{
public class ModelsRepository
{
public HttpClient _client;
public HttpResponseMessage _response;
public HttpRequestMessage _request;
public ModelsRepository()
{
_client = new HttpClient();
_client.BaseAddress = new Uri("http://localhost:3000/");
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImZ0aG1seW16QGhvdG1haWwuY29tIiwidXNlcklkIjoxLCJpYXQiOjE2MTM5NzI1NjcsImV4cCI6MTYxNDE0NTM2N30.-EVUg2ZmyInOLBx3YGzLcWILeYzNV-svm8xJiN8AIQI");
_client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<UsersModel> GetList()
{
_response = await _client.GetAsync($"users");
var json = await _response.Content.ReadAsStringAsync();
var listCS = UsersModel.FromJson(json);
return listCS;
}
}
}
What exactly do you mean by "you have to send the variable as a parameter"? Does it mean you want to add parameters dynamically by number and name?
In this case you could have e.g. a Dictionary as input and convert it into a query string:
var queryParameters = new Dictionary<string, string>
{
{ "business_code", "123" },
{ "someOtherParam", "456"}
};
var dictFormUrlEncoded = new FormUrlEncodedContent(queryParameters);
var queryString = await dictFormUrlEncoded.ReadAsStringAsync();
var response = await _client.GetAsync($"users?{queryString}")

Consume a Web Service in C#: Remove Task.Wait()

I am trying to consume a web service. It's an XML based service. I mean response in XML format. The code is working fine. However, I do not want to use task.Wait(). Please let me know how I can replace it with async/await.
Below is my code :
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
Program obj = new Program();
var result = obj.GetData().Result;
}
public async Task<string> GetData()
{
string url =
"https://test.net/info.php?akey=abcd&skey=xyz";
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync(url).Result;
var responseValue = string.Empty;
if (response != null)
{
Task task = response.Content.ReadAsStreamAsync().ContinueWith(t =>
{
var stream = t.Result;
using (var reader = new StreamReader(stream))
{
responseValue = reader.ReadToEnd();
}
});
task.Wait(); // How I can replace it and use await
}
return responseValue;
}
}
[XmlRoot(ElementName = "Info")]
public class Test
{
[XmlAttribute(AttributeName = "att")]
public string SomeAttribute{ get; set; }
[XmlText]
public string SomeText{ get; set; }
}
}
You already are in an async context, so just use await:
var stream = await response.Content.ReadAsStreamAsync();
using (var reader = new StreamReader(stream))
{
responseValue = reader.ReadToEnd();
}
That said, you should check all your calls:
HttpResponseMessage response = await client.GetAsync(url);
and make your main async, too and while we are at it make the method static:
public static async Task Main)
{
var result = await GetData();
}
where your method signature is:
public static async Task<string> GetData()
The static isn't required, but you will find parallel and/or asynchronous programming is a lot easier if you have as little side effects as possible.
You can make Main method async as well and await GetData
static async Task Main(string[] args)
{
Program obj = new Program();
var result = await obj.GetData();
}

Best way to setup MSTest for REST service with cookie-authentication?

Background: I am using ASP.NET Core 3.1, and integration testing a REST service that requires cookie authentication.
Candidate solution below.
Note:
The reason I use a vanilla Host instead of TestServer is because of the cookie requirement. When using TestServer, it provides an HttpClient for you, but the client does not pass cookies back to the server.
I also attempted to use a custom HttpClient with TestServer. That consistently generated a System.Net.Sockets.SocketException (No connection could be made because the target machine actively refused it.)
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi; // Contains my Startup.cs
namespace WebApiTest
{
[TestClass]
public class UserTest
{
static IHost HttpHost;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
HttpHost = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.Build();
await HttpHost.StartAsync();
}
[ClassCleanup]
public static async Task ClassCleanup()
{
await HttpHost.StopAsync();
}
public static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = null;
if (content != null)
{
httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
}
return httpContent;
}
public static HttpClient GetCookieHttpClient()
{
SocketsHttpHandler handler = new SocketsHttpHandler
{
AllowAutoRedirect = false,
CookieContainer = new CookieContainer(),
UseCookies = true
};
return new HttpClient(handler);
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (HttpClient client = GetCookieHttpClient())
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
HttpClient is a thin-client; it doesn't do anything unless you explicitly tell it to. In other words, it will never send the cookie for you; you must add a Cookie header to the request with the cookie value for each request. The test server "client" is just an HttpClient instance set up to proxy requests to the test server. You should use the test server, as prescribed, along with its client, and then add the Cookie header the requests you make with that.
Solutions based on Chris Pratt's suggestions
After some further digging, Microsoft provides a solution for this (WebApplicationFactory):
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;
namespace WebApiTest
{
[TestClass]
public class Class2
{
static WebApplicationFactory<Startup> Factory;
static WebApplicationFactoryClientOptions ClientOptions;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
Factory = new WebApplicationFactory<Startup>();
ClientOptions = new WebApplicationFactoryClientOptions();
ClientOptions.AllowAutoRedirect = false;
ClientOptions.HandleCookies = true;
ClientOptions.BaseAddress = new Uri("http://localhost:5000");
}
public static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = null;
if (content != null)
{
httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
}
return httpContent;
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (HttpClient client = Factory.CreateClient(ClientOptions))
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.PostAsync("http://localhost:5000/api/auth/login", GetHttpContent(credentials));
response = await client.GetAsync(String.Format("http://localhost:5000/api/users/{0}", credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}
In case you want to stick with TestServer, here is a manual Cookie-passing implementation:
using Microsoft.AspNetCore.TestHost;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using WebApi;
namespace WebApiTest
{
public class CookieHttpClient : IDisposable
{
private static HttpContent GetHttpContent(object content)
{
HttpContent httpContent = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(content, content.GetType()));
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return httpContent;
}
private static IEnumerable<string> GetCookieStrings(CookieCollection collection)
{
List<string> output = new List<string>(collection.Count);
foreach (Cookie cookie in collection)
{
output.Add(cookie.Name + "=" + cookie.Value);
}
return output;
}
private HttpClient client;
private CookieContainer container;
public CookieHttpClient(HttpClient client)
{
this.client = client;
this.container = new CookieContainer();
}
public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri)
{
return await this.SendAsync(method, uri, null);
}
public async Task<HttpResponseMessage> SendAsync(HttpMethod method, Uri uri, object data)
{
HttpRequestMessage request = new HttpRequestMessage(method, uri);
// Add data
if (data != null)
{
request.Content = GetHttpContent(data);
}
// Add cookies
CookieCollection collection = this.container.GetCookies(uri);
if (collection.Count > 0)
{
request.Headers.Add("Cookie", GetCookieStrings(collection));
}
HttpResponseMessage response = await this.client.SendAsync(request);
// Remember cookies before returning
if (response.Headers.Contains("Set-Cookie"))
{
foreach (string s in response.Headers.GetValues("Set-Cookie"))
{
this.container.SetCookies(uri, s);
}
}
return response;
}
public void Dispose()
{
this.client.Dispose();
}
}
[TestClass]
public class Class1
{
static TestServer TestServer;
[ClassInitialize]
public static async Task ClassStartup(TestContext context)
{
IWebHostBuilder builder = new WebHostBuilder()
.UseStartup<Startup>();
TestServer = new TestServer(builder);
}
[TestMethod]
public async Task GetUserData_ReturnsSuccess()
{
using (CookieHttpClient client = new CookieHttpClient(TestServer.CreateClient()))
{
var credentials = new
{
Email = "test#test.com",
Password = "password123",
};
HttpResponseMessage response = await client.SendAsync(HttpMethod.Post, new Uri("http://localhost:5000/api/auth/login"), credentials);
response = await client.SendAsync(HttpMethod.Get, new Uri("http://localhost:5000/api/users/" + credentials.Email));
Assert.IsTrue(response.StatusCode == HttpStatusCode.OK);
}
}
}
}

Getting HttpClient to POST/PUT Async

I am attempting to PUT and POST to a URL using HttpClient in C#. I need to do so asynchronously for scaling purposes. However, I am only able to get my PUT and POST to work synchronously. Below is the code I am using to PUT the ZoomData objects in JSON format to the URL:
// takes the dataset to PUT and PUTs to server
public async Task<HttpResponseMessage> JsonPUTcall(ZoomData toPut)
{
string jsonString = JsonConvert.SerializeObject(toPut);
return await client.PutAsync(InsiteDatasetPutURL.Replace("sys_id", toPut.sys_id), new StringContent(jsonString, UnicodeEncoding.UTF8, "application/json"));
}
And here is the code I am using to actually pass ZoomData objects in a queue to JsonPUTcall:
public async void JsonPUTqueueCall(Queue<ZoomData> toPut)
{
if (toPut.Count == 0)
return;
foreach (var zoomData in toPut)
{
var result = await this.JsonPUTcall(zoomData);
}
}
However, when I attempt this, it simply hangs. So, as a test, I replaced "var result = await this.JsonPUTcall(zoomData);" with the following:
public async void JsonPUTqueueCall(Queue<ZoomData> toPut)
{
if (toPut.Count == 0)
return;
foreach (var zoomData in toPut)
{
var result = this.JsonPUTcall(zoomData);
result.Wait();
}
}
That works, but since it is synchronous, it defeats the purpose of using async. What am I missing?
this is my API client which is efficiently uses resources and the methods are async
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
//You need to install package Newtonsoft.Json > https://www.nuget.org/packages/Newtonsoft.Json/
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class MyApiClient : IDisposable
{
private readonly TimeSpan _timeout;
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
private readonly string _baseUrl;
private const string ClientUserAgent = "my-api-client-v1";
private const string MediaTypeJson = "application/json";
public MyApiClient(string baseUrl, TimeSpan? timeout = null)
{
_baseUrl = NormalizeBaseUrl(baseUrl);
_timeout = timeout ?? TimeSpan.FromSeconds(90);
}
public async Task<string> PostAsync(string url, object input)
{
EnsureHttpClientCreated();
using (var requestContent = new StringContent(ConvertToJsonString(input), Encoding.UTF8, MediaTypeJson))
{
using (var response = await _httpClient.PostAsync(url, requestContent))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
public async Task<TResult> PostAsync<TResult>(string url, object input) where TResult : class, new()
{
var strResponse = await PostAsync(url, input);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public async Task<TResult> GetAsync<TResult>(string url) where TResult : class, new()
{
var strResponse = await GetAsync(url);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public async Task<string> GetAsync(string url)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.GetAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> PutAsync(string url, object input)
{
return await PutAsync(url, new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, MediaTypeJson));
}
public async Task<string> PutAsync(string url, HttpContent content)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.PutAsync(url, content))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> DeleteAsync(string url)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.DeleteAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public void Dispose()
{
_httpClientHandler?.Dispose();
_httpClient?.Dispose();
}
private void CreateHttpClient()
{
_httpClientHandler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
_httpClient = new HttpClient(_httpClientHandler, false)
{
Timeout = _timeout
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
if (!string.IsNullOrWhiteSpace(_baseUrl))
{
_httpClient.BaseAddress = new Uri(_baseUrl);
}
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
}
private void EnsureHttpClientCreated()
{
if (_httpClient == null)
{
CreateHttpClient();
}
}
private static string ConvertToJsonString(object obj)
{
if (obj == null)
{
return string.Empty;
}
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
private static string NormalizeBaseUrl(string url)
{
return url.EndsWith("/") ? url : url + "/";
}
}
Usage;
using ( var client = new MyApiClient("http://localhost:8080"))
{
var response = client.GetAsync("api/users/findByUsername?username=alper").Result;
var userResponse = client.GetAsync<MyUser>("api/users/findByUsername?username=alper").Result;
}
These 2 lines is to make it synchronous :
var result = this.JsonPUTcall(zoomData);
result.Wait();
To make it async you hate to do that :
var result = await this.JsonPUTcall(zoomData);

Patching to TFS API with JSON gives 400 error (Bad Request)

I'm trying to do a patch http request to change one of the fields in TFS through the TFS REST API. I've tried several approaches, but I always end up with 400 error. Here is what I have right now:
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();
}
}
And the test method that calls this method:
[TestMethod()]
public void setFieldValue()
{
TFSWebAPIImplementation webAPI = new TFSWebAPIImplementation();
webAPI.SetFieldValue("654321", "/fields/Custom.Tracking", 61949);
}
The PatchwebAPIUrl("...") Method is fine, and returns a good URL, when I navigate to it I get the JSON data that I want to edit. I'm not 100% on the path variable but it's used the same as the example provided from Microsoft. The authorization works, just based on the fact that when I mess with it I get a 401 instead.
This is my sample code:
Class for work item:
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;
}
Class for new and updated fields:
public class NewField
{
[JsonProperty("op")]
public string op = "add";
[JsonProperty("path")]
public string path;
[JsonProperty("value")]
public object value;
}
Class for exceptions:
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;
}
Method for update a work item:
private static WorkItemAtrr UpdateWorkItemRest()
{
Dictionary<string, string> _fields = new Dictionary<string, string>();
_fields.Add("REFERENCE_NAME", "VALUE");
var _updatedWi = UpdateWorkItem("ID", _fields).Result;
}
Method for preparing request:
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));
}
Universal method for request:
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);
}
}
Universal method for response:
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;
}
A 400 means that the request was malformed. In other words, the data stream sent by the client to the server didn't follow the rules.
In the case of a REST API with a JSON payload, 400's are typically, used to indicate that the JSON is invalid in some way according to the API specification for the service.
So, the issue is caused by the JSON body.
Just try it like below:
string json = "[{\"op\":\"replace\",\"path\":\"/fields/System.Title\",\"value\":\"Title\"}]";
You can also use below sample to update the fields with PATCH method via the REST API, it works for me:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
namespace UpdateWorkItemFiled0411
{
class Program
{
static void Main(string[] args)
{
string password = "xxxx";
string credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "username", password )));
Object[] patchDocument = new Object[1];
patchDocument[0] = new { op = "replace", path = "/relations/attributes/comment", value = "Adding traceability to dependencies" };
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", credentials);
var patchValue = new StringContent(JsonConvert.SerializeObject(patchDocument), Encoding.UTF8, "application/json-patch+json");
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, "http://server:8080/tfs/DefaultCollection/_apis/wit/workitems/21?api-version=1.0") { Content = patchValue };
var response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
}
}
}
}
}
Also you may use nugate package Microsoft.TeamFoundationServer.Client.
Connect to team project from 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;
}
my code to add comments:
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;
Okay guys, so unfortunately none of your solutions worked, and I think it's because there was always an extra set of curly brackets on the outside that the TFS API didn't like. Here is my solution that solved my problem:
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;
}
}
}

Categories