Skip to content

Instantly share code, notes, and snippets.

@rheone
Last active August 13, 2023 02:31
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rheone/11092375 to your computer and use it in GitHub Desktop.
Save rheone/11092375 to your computer and use it in GitHub Desktop.
Generate JSON Postman Collections from a WebApi2 project using WebApi HelpPages

Postman is a tool that can be used to easily test restful web services.

If an Asp.Net project is using WebApi in conjunction with WebApi Helppages documentation can be automatically generated for exposed restful web services.

This autogenerated documentation is good, but it could be made better via added accessibility.

Expanding on the blog post "Using ApiExplorer to export API information to PostMan, a Chrome extension for testing Web APIs" it is possible to generate a JSON file that can be imported into Postman for use in testing and documenting.

Included within this Gist is the code needed to generate JSON that may be direcly imported into Postman. Once implemented all you need to do is make a GET request to [application]api/postman and you'll have the latest restful API in a form that is readable by postman.

/// <summary>
/// Based on
/// http://blogs.msdn.com/b/yaohuang1/archive/2012/06/15/using-apiexplorer-to-export-api-information-to-postman-a-chrome-extension-for-testing-web-apis.aspx
/// </summary>
[RoutePrefix("api/postman")]
public class PostmanApiController : ApiController
{
/// <summary>
/// Produce [POSTMAN](http://www.getpostman.com) related responses
/// </summary>
public PostmanApiController()
{
// exists for documentation purposes
}
private readonly Regex _pathVariableRegEx = new Regex("\\{([A-Za-z0-9-_]+)\\}", RegexOptions.ECMAScript | RegexOptions.Compiled);
private readonly Regex _urlParameterVariableRegEx = new Regex("=\\{([A-Za-z0-9-_]+)\\}", RegexOptions.ECMAScript | RegexOptions.Compiled);
/// <summary>
/// Get a postman collection of all visible Api
/// (Get the [POSTMAN](http://www.getpostman.com) chrome extension)
/// </summary>
/// <returns>object describing a POSTMAN collection</returns>
/// <remarks>Get a postman collection of all visible api</remarks>
[HttpGet]
[Route(Name = "GetPostmanCollection")]
[ResponseType(typeof (PostmanCollectionGet))]
public IHttpActionResult GetPostmanCollection()
{
return Ok(this.PostmanCollectionForController());
}
private PostmanCollectionGet PostmanCollectionForController()
{
var requestUri = Request.RequestUri;
var baseUri = requestUri.Scheme + "://" + requestUri.Host + ":" + requestUri.Port
+ HttpContext.Current.Request.ApplicationPath;
var postManCollection = new PostmanCollectionGet
{
Id = Guid.NewGuid(),
Name = "[Name of your API]",
Timestamp = DateTime.Now.Ticks,
Requests = new Collection<PostmanRequestGet>(),
Folders = new Collection<PostmanFolderGet>(),
Synced = false,
Description = "[Description of your API]"
};
var helpPageSampleGenerator = Configuration.GetHelpPageSampleGenerator();
var apiExplorer = Configuration.Services.GetApiExplorer();
var apiDescriptionsByController = apiExplorer.ApiDescriptions.GroupBy(
description =>
description.ActionDescriptor.ActionBinding.ActionDescriptor.ControllerDescriptor.ControllerType);
foreach (var apiDescriptionsByControllerGroup in apiDescriptionsByController)
{
var controllerName = apiDescriptionsByControllerGroup.Key.Name.Replace("Controller", string.Empty);
var postManFolder = new PostmanFolderGet
{
Id = Guid.NewGuid(),
CollectionId = postManCollection.Id,
Name = controllerName,
Description = string.Format("Api Methods for {0}", controllerName),
CollectionName = "api",
Order = new Collection<Guid>()
};
foreach (var apiDescription in apiDescriptionsByControllerGroup
.OrderBy(description => description.HttpMethod, new HttpMethodComparator())
.ThenBy(description => description.RelativePath)
.ThenBy(description => description.Documentation.ToString(CultureInfo.InvariantCulture)))
{
TextSample sampleData = null;
var sampleDictionary = helpPageSampleGenerator.GetSample(apiDescription, SampleDirection.Request);
MediaTypeHeaderValue mediaTypeHeader;
if (MediaTypeHeaderValue.TryParse("application/json", out mediaTypeHeader)
&& sampleDictionary.ContainsKey(mediaTypeHeader))
{
sampleData = sampleDictionary[mediaTypeHeader] as TextSample;
}
// scrub curly braces from url parameter values
var cleanedUrlParameterUrl = this._urlParameterVariableRegEx.Replace(apiDescription.RelativePath, "=$1-value");
// get pat variables from url
var pathVariables = this._pathVariableRegEx.Matches(cleanedUrlParameterUrl)
.Cast<Match>()
.Select(m => m.Value)
.Select(s => s.Substring(1, s.Length - 2))
.ToDictionary(s => s, s => string.Format("{0}-value", s));
// change format of parameters within string to be colon prefixed rather than curly brace wrapped
var postmanReadyUrl = this._pathVariableRegEx.Replace(cleanedUrlParameterUrl, ":$1");
// prefix url with base uri
var url = baseUri.TrimEnd('/') + "/" + postmanReadyUrl;
var request = new PostmanRequestGet
{
CollectionId = postManCollection.Id,
Id = Guid.NewGuid(),
Name = apiDescription.RelativePath,
Description = apiDescription.Documentation,
Url = url,
Method = apiDescription.HttpMethod.Method,
Headers = "Content-Type: application/json",
Data = sampleData == null
? null
: sampleData.Text,
DataMode = "raw",
Time = postManCollection.Timestamp,
Synced = false,
DescriptionFormat = "markdown",
Version = "beta",
Responses = new Collection<string>(),
PathVariables = pathVariables
};
postManFolder.Order.Add(request.Id); // add to the folder
postManCollection.Requests.Add(request);
}
postManCollection.Folders.Add(postManFolder);
}
return postManCollection;
}
}
/// <summary>
/// Quick comparer for ordering http methods for display
/// </summary>
internal class HttpMethodComparator : IComparer<HttpMethod>
{
private readonly string[] _order =
{
"GET",
"POST",
"PUT",
"DELETE"
};
public int Compare(HttpMethod x, HttpMethod y)
{
return Array.IndexOf(this._order, x.ToString()).CompareTo(Array.IndexOf(this._order, y.ToString()));
}
}
/// <summary>
/// [Postman](http://getpostman.com) collection representation
/// </summary>
public class PostmanCollectionGet
{
/// <summary>
/// Id of collection
/// </summary>
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
/// <summary>
/// Name of collection
/// </summary>
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
/// <summary>
/// Collection generation time
/// </summary>
[JsonProperty(PropertyName = "timestamp")]
public long Timestamp { get; set; }
/// <summary>
/// Requests associated with the collection
/// </summary>
[JsonProperty(PropertyName = "requests")]
public ICollection<PostmanRequestGet> Requests { get; set; }
/// <summary>
/// **unused always false**
/// </summary>
[JsonProperty(PropertyName = "synced")]
public bool Synced { get; set; }
/// <summary>
/// folders within the collection
/// </summary>
[JsonProperty(PropertyName = "folders")]
public ICollection<PostmanFolderGet> Folders { get; set; }
/// <summary>
/// Description of collection
/// </summary>
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
}
/// <summary>
/// Object that describes a [Postman](http://getpostman.com) folder
/// </summary>
public class PostmanFolderGet
{
/// <summary>
/// id of the folder
/// </summary>
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
/// <summary>
/// folder name
/// </summary>
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
/// <summary>
/// folder description
/// </summary>
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
/// <summary>
/// ordered list of ids of items in folder
/// </summary>
[JsonProperty(PropertyName = "order")]
public ICollection<Guid> Order { get; set; }
/// <summary>
/// Name of the collection
/// </summary>
[JsonProperty(PropertyName = "collection_name")]
public string CollectionName { get; set; }
/// <summary>
/// id of the collection
/// </summary>
[JsonProperty(PropertyName = "collection_id")]
public Guid CollectionId { get; set; }
}
/// <summary>
/// [Postman](http://getpostman.com) request object
/// </summary>
public class PostmanRequestGet
{
/// <summary>
/// id of request
/// </summary>
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
/// <summary>
/// headers associated with the request
/// </summary>
[JsonProperty(PropertyName = "headers")]
public string Headers { get; set; }
/// <summary>
/// url of the request
/// </summary>
[JsonProperty(PropertyName = "url")]
public string Url { get; set; }
/// <summary>
/// path variables of the request
/// </summary>
[JsonProperty(PropertyName = "pathVariables")]
public Dictionary<string, string> PathVariables { get; set; }
/// <summary>
/// method of request
/// </summary>
[JsonProperty(PropertyName = "method")]
public string Method { get; set; }
/// <summary>
/// data to be sent with the request
/// </summary>
[JsonProperty(PropertyName = "data")]
public string Data { get; set; }
/// <summary>
/// data mode of reqeust
/// </summary>
[JsonProperty(PropertyName = "dataMode")]
public string DataMode { get; set; }
/// <summary>
/// name of request
/// </summary>
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
/// <summary>
/// request description
/// </summary>
[JsonProperty(PropertyName = "description")]
public string Description { get; set; }
/// <summary>
/// format of description
/// </summary>
[JsonProperty(PropertyName = "descriptionFormat")]
public string DescriptionFormat { get; set; }
/// <summary>
/// time that this request object was generated
/// </summary>
[JsonProperty(PropertyName = "time")]
public long Time { get; set; }
/// <summary>
/// version of the request object
/// </summary>
[JsonProperty(PropertyName = "version")]
public string Version { get; set; }
/// <summary>
/// request response
/// </summary>
[JsonProperty(PropertyName = "responses")]
public ICollection<string> Responses { get; set; }
/// <summary>
/// the id of the collection that the request object belongs to
/// </summary>
[JsonProperty(PropertyName = "collection-id")]
public Guid CollectionId { get; set; }
/// <summary>
/// Synching
/// </summary>
[JsonProperty(PropertyName = "synced")]
public bool Synced { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment