问题
Ive been banging my head against this all week. Creating a new Team Project using the REST api. Everywhere i look, the response is the same, and it always involves using command line and xml.
But why?
On the visual studio online pages this can be found:
https://www.visualstudio.com/en-us/integrate/api/tfs/projects
(Specifically looking at the part labelled "Create a team project")
So why does this exist if it cant be used? Or am i missing something?
If anyone knows any examples of using this i would greatly appreciate it.
Ive been using the Microsoft.TeamFoundation.WorkItemTracking.Client namespaces etc... and have been happily creating new work items to projects
and Ive even managed to use the API to pull down lists of projects too. using code from this example (scroll to bottom of page)
https://www.visualstudio.com/en-us/integrate/get-started/rest/basics
but i cannot for the life of me post a new team project.
At this point i am open to any suggestions, i created an account here just to ask (i love this site) :(
As requested, some code:
static async Task<string> PostProjectAsync(HttpClient _client, string _apiUrl, string _apiVersion)
{
var responseBody = string.Empty;
HttpContent hc = new StringContent(@"
{
""name"": ""Testprojectfromconsole"",
""description"": ""Posted from console application using the tfs API""
}
");
//TODO: make a class that matches the json layout that the api is expecting
//then see if you have any better luck with that instead of this horrid horrid mess
ProjectPost newproj = new ProjectPost();
newproj.Name = @"Test Project -From console";
newproj.Description = @"Hopefully this has been posted from the console app, delete it later on if need be.";
newproj.Capabilities.VersionControl.SourceControlType = @"TFS"; //probably wrong
newproj.Capabilities.ProcessTemplate.TemplateTypeId = @"default"; //also probably wrong
string json = JsonConvert.SerializeObject(newproj);
try
{
using (HttpResponseMessage response = _client.PostAsync(_apiUrl + _apiVersion, hc).Result)
{
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return responseBody;
}
At the moment I'm passing a HttpContent called "hc" to the postasync, but if i switch it for the json object, the postasync stops working (because it wants httpcontent not a json)
before this methodis called, the client is set up as so:
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
//Set alternate credentials
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", ALTUSERNAME, ALTPASSWORD))));
Console.WriteLine("<--------------Getting projects from tfs!-------------->");
Console.WriteLine("<----------------Hold on to your butts!---------------->");
responseBody = await GetAsync(client, BASEURL + "projects", APIVERS);
Console.WriteLine(responseBody.ToString());
Console.WriteLine("<----------------Making a new project!----------------->");
Console.WriteLine("<----------------Hold on to your butts!---------------->");
responseBody = await PostProjectAsync(client, BASEURL + "projects", APIVERS);
Console.WriteLine(responseBody.ToString());
oh, and the URL is such:
static string PN1 = @"Test Project -From Web";
static string PN2 = @"Another Test Project -From Web";
static string COL = @"DefaultCollection";
static string BASEURL = "https://{0}.visualstudio.com/DefaultCollection/_apis/";
// Get the alternate credentials that you'll use to access the Visual Studio Online account.
static string ALTUSERNAME = "myusername";
static string ALTPASSWORD = "mypassword!";
//Your visual studio account name
static string ACCOUNT = "ourserver";
//Api version query parameter
static string APIVERS = "?api-version=1.0";
回答1:
Here is the code that I've used. This is made for .net 3.5 but I found solutions with .net 4.5.1:
private const string PROJECT_TEMPLATE_AGILE = "adcc42ab-9882-485e-a3ed-7678f01f66bc";
private const string PROJECT_TEMPLATE_SCRUM = "6b724908-ef14-45cf-84f8-768b5384da45";
private const string PROJECT_TEMPLATE_CMMI = "27450541-8e31-4150-9947-dc59f998fc01";
VsoTeamProject project = new VsoTeamProject(
newFolderName,
comment,
new Capabilities(new VersionControl("Tfvc"), new ProcessTemplate(projectTemplateId)));
CreateTeamProject(project, "POST", false); // this calls PostResponse method
Here is the main method:
private void PostResponse(VsoTeamProject project, string method, bool useProjectName)
{
string projectState = "wellFormed";
if(method.Equals("DELETE"))
{
projectState = "deleting";
}
var requestUriString = ConstructUrl(
useProjectName ? project.TeamProjectId : string.Empty,
string.Empty,
new Dictionary<string, object>());
var httpWebRequest = (HttpWebRequest)WebRequest.Create(requestUriString);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = method;
string autorization = TFSImplementor.LoginName + ":" + TFSImplementor.Password;
byte[] binaryAuthorization = Encoding.UTF8.GetBytes(autorization);
autorization = Convert.ToBase64String(binaryAuthorization);
autorization = "Basic " + autorization;
httpWebRequest.Headers.Add("AUTHORIZATION", autorization);
if(method.Equals("POST"))
{
using(var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = JsonConvert.SerializeObject(project);
streamWriter.Write(json);
}
}
try
{
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
if(httpResponse.StatusCode == HttpStatusCode.Accepted)
{
Task<WebResponse> responseTask = Task.Factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
using(var responseStream = responseTask.Result.GetResponseStream())
{
var reader = new StreamReader(responseStream);
var t = reader.ReadToEnd();
ProjectStatus json = JsonConvert.DeserializeObject<ProjectStatus>(t);
if(json.status.Equals("queued"))
{
while(true)
{
if(CheckTeamProjectState(project.ProjectName, true, projectState))
{
break;
}
}
}
}
}
}
catch(WebException e)
{
using(WebResponse response = e.Response)
{
using(Stream data = response.GetResponseStream())
{
using(var reader = new StreamReader(data))
{
string text = reader.ReadToEnd();
Logger.Error(text);
Logger.Exception(e);
if(method.Equals("DELETE"))
{
throw new Exception("Failed to delete project, check log for more details");
}
throw new Exception("Failed to create project, check log for more details");
}
}
}
}
}
Here are the classes that you can use:
private class VsoTeamProject
{
#region Fields
private readonly string m_name;
private readonly string m_comment;
private readonly string m_teamProjectId;
private readonly Capabilities m_capabilities;
#endregion
#region Constructors
public VsoTeamProject(string teamProjectId, string name)
{
m_teamProjectId = teamProjectId;
m_name = name;
}
public VsoTeamProject(string projectName, string description, Capabilities capabilities)
{
m_name = projectName;
m_comment = description;
m_capabilities = capabilities;
}
#endregion
#region Properties
[JsonProperty("name")]
protected internal string ProjectName
{
get
{
return m_name;
}
}
[JsonProperty("description")]
protected internal string Description
{
get
{
return m_comment;
}
}
protected internal string TeamProjectId
{
get
{
return m_teamProjectId;
}
}
[JsonProperty("capabilities")]
protected internal Capabilities Capabilities
{
get
{
return m_capabilities;
}
}
#endregion
}
private class ProjectStatus
{
public string id { get; set; }
public string status { get; set; }
public string url { get; set; }
public string name { get; set; }
public string state { get; set; }
public string message { get; set; }
}
private class Capabilities
{
public Capabilities(VersionControl versionControl, ProcessTemplate template)
{
VersionControl = versionControl;
ProcessTemplate = template;
}
[JsonProperty("processTemplate")]
public ProcessTemplate ProcessTemplate { get; private set; }
[JsonProperty("versioncontrol")]
public VersionControl VersionControl { get; private set; }
}
private class VersionControl
{
public VersionControl(object type)
{
SourceControlType = type;
}
[JsonProperty("sourceControlType")]
public object SourceControlType { get; private set; }
}
private class ProcessTemplate
{
public ProcessTemplate(string templateTypeId)
{
TemplateTypeId = templateTypeId;
}
[JsonProperty("templateTypeId")]
public string TemplateTypeId { get; private set; }
}
I've this PostResponse method also for deleting the projects from VSO. It works like a charm.
回答2:
I don't know if you are still interested in an answer to this (as it's been 3 years now), but the issue isn't your code: it's the documentation.
When you create a project using the API, the fields listed in the documentation don't tell you all of the required fields.
If you tried the request in Postman, here's what you would get back:
message:"The project information supplied to project create is invalid. You must provide all and only these properties/capabilities: name, description, visibility, capabilities.versioncontrol.sourceControlType, capabilities.processTemplate.templateTypeId."
Where the project template type id = 6b724908-ef14-45cf-84f8-768b5384da45
来源:https://stackoverflow.com/questions/31744123/creating-a-new-team-project-using-the-rest-api