Skip to content

Getting Started

fonlow edited this page Apr 17, 2022 · 28 revisions

Before Getting Started

All custom complex types exposed in the API should be decorated by DataContractAttribute, JsonObjectAttribute or SerializableAttribute, unless you want opt-out approaches of cherry-picking.

For .NET Client API

The service CLR namespaces will be translated to client namespaces through appending ".Client" as suffix by default. For example, namespace My.Name.space will be translated to {"My.Name.space.Client"}.

For TypeScript Client API

While ASP.NET MVC and Web API use NewtonSoft.Json for JSON applications, NewtonSoft.Json can handle well POCO classes decorated by DataContractAttribute.

The CLR namespaces will be translated to TypeScript namespaces through replacing dot with underscore and adding "Client" as suffix by default. For example, namespace My.Name.space will be translated to {"My_Name_space_Client"}.

Enable Doc Comments of Web API

In C:\YourWebSlnPath\Your.WebApi\Areas\HelpPage\App_Start\HelpPageConfig.cs, there is such line:

//config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

Uncomment it and make it be like this:

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/Your.WebApi.xml")));

In the Build tab of the project Properties page, check Output/XML Document File and set "bin\Your.WebApi.xml", while the the output path is "bin" by default.

If you have other assemblies for data models, you may do the same to ensure doc comments to be generated and copied over to the client API.

Remarks

In the .NET Core build of WebApiClientGen, this step is irrelevant. The toolkit will import from the XML document of the .NET Core Web API assembly directly.

Getting Started

Step 0: Install NuGet package WebApiClientGen to the Web MVC/API project.

The installation will also install dependent NuGet packages Fonlow.TypeScriptCodeDOM and Fonlow.Poco2Ts to the project references.

The NuGet package will add 2 TS files to the ~/Scripts/ClientApi folder of the project, one is HttpClient.ts, and the other is WebApiClientAuto.ts which will be replaced everytime the CodeGen is triggered.

Additionally, CodeGenController.cs is added to the project's Controllers folder.

#if DEBUG  //This controller is not needed in production release, since the client API should be generated during development of the Web Api.
...

namespace Fonlow.WebApiClientGen
{
	[System.Web.Http.Description.ApiExplorerSettings(IgnoreApi = true)]//this controller is a dev backdoor during development, no need to be visible in ApiExplorer.
	public class CodeGenController : ApiController
	{
		/// <summary>
		/// Trigger the API to generate WebApiClientAuto.cs for an established client API project.
		/// </summary>
		/// <param name="settings"></param>
		/// <returns>OK if OK</returns>
		[HttpPost]
		public IHttpActionResult TriggerCodeGen(CodeGenSettings settings)
		{
...
        }
    }

The CodeGenController should be available only during development in the debug build, since the client API should be generated once for each version of the Web API.

And in the service startup codes, ensure the following:

services.AddControllers(
	options =>
	{
#if DEBUG
		options.Conventions.Add(new Fonlow.CodeDom.Web.ApiExplorerVisibilityEnabledConvention());

#endif
	}
)

Hints

Step 1: Create the .NET client API project (optional)

This step is not needed if you don't expect any client program coded on .NET Framework.

For .NET Framework:

  1. Microsoft ASP.NET Web API 2.2 Client Libraries
  2. Json.NET which is introduced through the first one by default.
  3. System.Runtime.Serialization
  4. System.ServiceModel
  5. System.ComponentModel.DataAnnotations

For .NET Core:

  1. System.Net.Http
  2. Newtonsoft.Json

Step 2: Run the DEBUG build of the Web API project to trigger the generation of client API codes

Run the Web project in IDE with IIS Express. You have options of generating TypeScript client API codes, or C# client API codes, or both.

You then use Curl or Poster or any of your favorite client tools to POST to http://localhost:10965/api/CodeGen, with content-type=application/json

{
	"ApiSelections": {
		"ExcludedControllerNames": [
			"DemoWebApi.Controllers.Account",
			"DemoWebApi.Controllers.FileUpload"
		],

		"DataModelAssemblyNames": [
			"DemoWebApi.DemoData",
			"DemoWebApi"
		],
		"CherryPickingMethods": 3
	},

	"ClientApiOutputs": {
		"ClientLibraryProjectFolderName": "..\\DemoWebApi.ClientApi",
		"GenerateBothAsyncAndSync": true,
		"CSClientNamespaceSuffix": ".Client" // if undefined, it is .Client anyway.

		"Plugins": [
			{
				"AssemblyName": "Fonlow.WebApiClientGen.jQuery",
				"TargetDir": "Scripts\\ClientApi",
				"TSFile": "WebApiJQClientAuto.ts",
				"AsModule": false,
				"ContentType": "application/json;charset=UTF-8",
				"CamelCase": true
			},

			{
				"AssemblyName": "Fonlow.WebApiClientGen.NG2",
				"TargetDir": "..\\DemoNGCli\\NGSource\\src\\ClientApi",
				"TSFile": "WebApiNG2ClientAuto.ts",
				"AsModule": true,
				"ContentType": "application/json;charset=UTF-8",
				"CamelCase": true,
				"ClientNamespaceSuffix": ".Client" // if undefined, it is .Client anyway which will become _Client
			},

			{
				"AssemblyName": "Fonlow.WebApiClientGen.Axios",
				"TargetDir": "..\\axios\\src\\clientapi",
				"TSFile": "WebApiAxiosClientAuto.ts",
				"AsModule": true,
				"ContentType": "application/json;charset=UTF-8",
				"CamelCase": true
			},

			{
				"AssemblyName": "Fonlow.WebApiClientGen.Aurelia",
				"TargetDir": "..\\aurelia\\src\\clientapi",
				"TSFile": "WebApiAureliaClientAuto.ts",
				"AsModule": true,
				"ContentType": "application/json;charset=UTF-8",
				"CamelCase": true
			}

		]


	}
}

If ClientLibraryProjectFolderName is not defined, no C# client API codes will be generated. And if TypeScriptFolder is not defined, no TypeScript client API codes will be generated.

Hints: By default, WebApiClientAuto.ts is generated under "TypeScriptJQFolder" for jQuery, and WebApiNG2ClientAuto.ts is generated under "TypeScriptNG2Folder" for Angular 2. Sometimes you may want to customize the file names, then you may add property "TypeScriptJQFile": YourClientApi.ts, or "TypeScriptNG2File": YourNG2ClientApi.ts, or "TypeScriptAxiosFile": YourAxiosClientApi.ts, or "TypeScriptAureliaFile": YourAureliaClientApi.ts into the JSON post.

NGVersion is a new setting introduced in WebApiClientGen 2.4. By default, WebApiClientGen 2.4 and greater will by default declare the import as import { Observable } from 'rxjs'; . If you are still using Angular 5.x, you need to declare "NGVersion" : 5 in the JSON config, so the import in the generated codes will be import { Observable } from 'rxjs/Observable'; .

StringAsString is an option for .NET Core Web API which will return text/plain string by default, rather than application/json JSON object. In contrast, ASP.NET Web API always return a string as application/json JSON object, unless you provide a custom made formatter that returns string as plain text in the response body. "ApiSelections" in CodeGen.json is mapped to the following:

	/// <summary>
	/// For cherry picking APIs and data models 
	/// </summary>
	public class CodeGenConfig
	{
		public string[] ExcludedControllerNames { get; set; }

		/// <summary>
		/// Assembly names without file extension
		/// </summary>
		public string[] DataModelAssemblyNames { get; set; }

		/// <summary>
		/// Similar to DataModelAssemblyNames however, each assembly could have a CherryPickingMethods. An assembly should appear in either DataModelAssemblyNames or DataModels.
		/// </summary>
		public DataModel[] DataModels { get; set; }

		/// <summary>
		/// Cherry picking methods of POCO classes
		/// </summary>
		public int? CherryPickingMethods { get; set; }

	}

	public class DataModel
	{
		public string AssemblyName { get; set; }

		public int? CherryPickingMethods { get; set; }

		/// <summary>
		/// System.ComponentModel.DataAnnotations attributes are translated into Doc Comments, 
		/// including Required, Range, MaxLength, MinLength, StringLength, DataType and RegularExpression.
		/// If defined, overwrite the global setting in ModelGenOutputs; if not defined, follow the global setting.
		/// </summary>
		public bool? DataAnnotationsToComments { get; set; }
	}

You should define either DataModelAssemblyNames or DataModels, not both.

"ClientApiOutputs" in CodeGen.json is mapped to the following:

	public class ModelGenOutputs
	{
		/// <summary>
		/// The naming of namespace is after the controller's namespace. To distinguish from the server side namespace, it is better to add a suffix like ".Client". The default is ".Client".
		/// </summary>
		public string CSClientNamespaceSuffix { get; set; } = ".Client";

		/// <summary>
		/// System.ComponentModel.DataAnnotations attributes are to be copied over, including Required, Range, MaxLength, MinLength and StringLength.
		/// </summary>
		public bool DataAnnotationsEnabled { get; set; }

		/// <summary>
		/// System.ComponentModel.DataAnnotations attributes are translated into Doc Comments, 
		/// including Required, Range, MaxLength, MinLength, StringLength, DataType and RegularExpression.
		/// </summary>
		public bool DataAnnotationsToComments { get; set; }

		/// <summary>
		/// Generated data types will be decorated with DataContractAttribute and DataMemberAttribute.
		/// </summary>
		public bool DecorateDataModelWithDataContract { get; set; }

		/// <summary>
		/// When DecorateDataModelWithDataContract is true, this is the namespace of DataContractAttribute. For example, "http://mybusiness.com/09/2019
		/// </summary>
		public string DataContractNamespace { get; set; }

		public bool DecorateDataModelWithSerializable { get; set; }
	}

	/// <summary>
	/// Client APIs as output for C#. Mapped to "ClientApiOutputs" in CodeGen.json.
	/// </summary>
	public class CodeGenOutputs : ModelGenOutputs
	{
		/// <summary>
		/// Assuming the client API project is the sibling of Web API project. Relative path to the running instance of the WebApi project should be fine.
		/// </summary>
		public string ClientLibraryProjectFolderName { get; set; }

		/// <summary>
		/// File to be generated under ClientLibraryProjectFolder. The default is WebApiClientAuto.cs.
		/// </summary>
		public string FileName { get; set; } = "WebApiClientAuto.cs";

		/// <summary>
		/// For .NET client, generate both async and sync functions for each Web API function
		/// </summary>
		public bool GenerateBothAsyncAndSync { get; set; }

		/// <summary>
		/// Whether the Web API return string as string, rather than JSON object which is a double quoted string.
		/// </summary>
		public bool StringAsString { get; set; }

		/// <summary>
		/// Whether to conform to the camel casing convention of javascript and JSON.
		/// If not defined, WebApiClientGen will check if GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver is Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver;
		/// If CamelCasePropertyNamesContractResolver is presented, camelCasing will be used. If not, no camelCasing transformation will be used.
		/// The default is ".Client".
		/// </summary>
		public bool? CamelCase { get; set; }

		/// <summary>
		/// Each controller is mapped into a container class to contain client API functions matching controller operations.
		/// By default the container is named after the controller name, for example, service class ValuesController will result in client container class Values.
		/// You may define a container name suffix such as "Client", so the generated container class name may become ValuesClient.
		/// </summary>
		public string ContainerNameSuffix { get; set; }

		/// <summary>
		/// Replace EnsureSuccessStatusCode with EnsureSuccessStatusCodeEx for specific unsuccessful HTTP status handling, which throws YourClientWebApiRequestException.
		/// </summary>
		public bool UseEnsureSuccessStatusCodeEx { get; set; }

		/// <summary>
		/// Default  is true so the code block is included in the generated codes.
		/// Defined if UseEnsureSuccessStatusCodeEx is true. Respective code block will be included the code gen output. However, if you have a few client APIs generated to be used in the same application,
		/// and you may want these client APIs share the same code block, then put the WebApiRequestException code block to an assembly or a standalone CS file.
		/// </summary>
		public bool IncludeEnsureSuccessStatusCodeExBlock { get; set; } = true;

		/// <summary>
		/// Function parameters contain a callback to handle HTTP request headers
		/// </summary>
		public bool HandleHttpRequestHeaders { get; set; }

		public JSPlugin[] Plugins { get; set; }
	}

Remarks

Please check Settings Explained for more details and contexts.

For various JavaScript libraries or frameworks, each member of Plugins[] in CodeGen.json is mapped to the following:

	public class JSPlugin
	{
		public string AssemblyName { get; set; }

		public string TargetDir { get; set; }

		/// <summary>
		/// Name of TypeScript file to be geneated under TargetDir.
		/// </summary>
		public string TSFile { get; set; }

		/// <summary>
		/// HTTP content type used in POST of HTTP of NG2. so text/plain could be used to avoid preflight in CORS.
		/// </summary>
		public string ContentType { get; set; }

		/// <summary>
		/// True to have "export namespace"; false to have "namespace". jQuery wants "namespace".
		/// </summary>
		public bool AsModule { get; set; }

		public string ClientNamespaceSuffix { get; set; } = ".Client";

		public string ContainerNameSuffix { get; set; }

		/// <summary>
		/// System.ComponentModel.DataAnnotations attributes are translated into Doc Comments, including Required, Range, MaxLength, MinLength, StringLength, DataType and RegularExpression.
		/// </summary>
		public bool DataAnnotationsToComments { get; set; }
	}

TargetDir is an absolute path or relative path to the running instance of the Web API project.

CherryPickingMethods:

    /// <summary>
    /// Flagged options for cherry picking in various development processes.
    /// </summary>
    [Flags]
    public enum CherryPickingMethods
    {
        /// <summary>
        /// Include all public classes, properties and properties.
        /// </summary>
        All = 0,

        /// <summary>
        /// Include all public classes decorated by DataContractAttribute, and public properties or fields decorated by DataMemberAttribute. 
        /// And use DataMemberAttribute.IsRequired
        /// </summary>
        DataContract =1,

        /// <summary>
        /// Include all public classes decorated by JsonObjectAttribute, and public properties or fields decorated by JsonPropertyAttribute.  
        /// And use JsonPropertyAttribute.Required
        /// </summary>
        NewtonsoftJson = 2,

        /// <summary>
        /// Include all public classes decorated by SerializableAttribute, and all public properties or fields but excluding those decorated by NonSerializedAttribute.
        /// And use System.ComponentModel.DataAnnotations.RequiredAttribute.
        /// </summary>
        Serializable = 4,

        /// <summary>
        /// Include all public classes, properties and properties. 
        /// And use System.ComponentModel.DataAnnotations.RequiredAttribute.
        /// </summary>
        AspNet = 8,
    }

The default one is DataContract. And you may use any or combinations of methods.

Publish client API libraries

Build the client API project (Optional)

This step is not needed if you don't expect any client program coded on .NET Framework, or you would prefer to distribute the generated CS file.

Internal usages

The TypeScript file WebApiClientAuto.ts generated in ~Scripts/ClientApi is linked to your MVC/Web API project by default, thus it will be usable immediately in Visual Studio IDE at design time, and will be compiled into Javascript at compile time.

External usages

If you intend to let developers outside use the Web API, you may publish the client API source codes or the assembly in your Website every time you introduce breaking changes to the interfaces of the Web API.

Major Web services vendors like Google and Amazon typically publish both the API documents and the client API libraries for major programming languages.

References:

Remarks

Since Javascript supports only single dimensional array, this article "Tricky Array" may help you to deal with multidimensional array in Web API.

Hints

Help Pages and Strongly Typed Client API describes how to make doc comments of API controller available to the client API codes generated.

Crafting some batch files may automate the steps above at some degree. For more details, you may download the whole VS solution of this open source project and inspect those batch files included in VS projects. And it is recommended to make the client API library be strong named, if you decide to publish the assembly only, since some client programmers may have their client programs strong named.

And it shouldn't be hard to write scripts to automate some steps altogether for Continuous Integration.

Clone this wiki locally