On the Swagger page of many APIs (web services) there is a documentation file that includes information about API object properties (their type, whether they are required or not, etc.) It seems this data could be used to automatically generate create/read/update/delete code or possibly code for an API service, for interactions with the API endpoints.
I was wondering if this is possible in .NET 6.0 (or other versions), Visual Studio Professional, etc. Thank you in advance.
CodePudding user response:
Yes, it is possible to generate code for an API from Swagger documentation in .NET 6.0 and Visual Studio Professional.
.NET 6.0 includes a tool called OpenAPI Generator which can automatically generate code from a Swagger documentation file. The tool supports multiple programming languages, including C#, and can generate code for different types of projects, such as ASP.NET Core web services, client libraries to consume API, and more.
To use OpenAPI Generator, you must first download and install .NET 6.0 and Visual Studio Professional. Then you can use the command-line tool or integrate it with Visual Studio using a NuGet package.
Here is an example command to generate code for an API from a Swagger documentation file using OpenAPI
dotnet openapi-generator generate -i /directory/path/file.yaml -g csharp -o /output/directory/path/
This command generates C# code from the Swagger documentation file located at /path/to/file.yaml and saves the generated code to the output folder /path/to/output/folder.
You can also use the OpenAPI Generator tool from Visual Studio by installing the OpenApiGenerator.CodeGenerator NuGet package in your project. Once the package is installed, you can use the Visual Studio UI to generate code from a Swagger documentation file.
CodePudding user response:
First of all, it is certain that ASP.net can fully realize the automatic generation of API documents. It can satisfy your test of the backend interface. Proceed as follows:
1. Create a Web API project.
2. Modify the default API routing configuration Why modify it? Because the default route of each Controller is assigned to "api/{controller}/{id}", the default generated controller is similar to the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebAPI.Controllers
{
public class ValuesController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
3. We need a method to provide multiple HTTP access methods, so the action configuration should be added, such as: "api/{controller}/{action}/{id}". Modify the WebApiConfig.cs file. Code show as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API Routing
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
4. Modify the HelpPageConfig.cs file, the code is as follows:
// Uncomment the following to provide samples for PageResult<T>. Must also add the Microsoft.AspNet.WebApi.OData
// package to your project.
#define Handle_PageResultOfT
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http.Headers;
using System.Reflection;
using System.Web;
using System.Web.Http;
using WebAPI.Utils;
namespace WebAPI.Areas.HelpPage
{
/// <summary>
/// Use this class to customize the Help Page.
/// For example you can set a custom <see cref="System.Web.Http.Description.IDocumentationProvider"/> to supply the documentation
/// or you can provide the samples for the requests/responses.
/// </summary>
public static class HelpPageConfig
{
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
MessageId = "WebAPI.Areas.HelpPage.TextSample.#ctor(System.String)",
Justification = "End users may choose to merge this string with existing localized resources.")]
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly",
MessageId = "bsonspec",
Justification = "Part of a URI.")]
public static void Register(HttpConfiguration config)
{
Uncomment the following to use the documentation from XML documentation file.
//config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
#region *************************XML display interface parameters and comments****************** ***********************
// Add the XML configuration of this project or other reference projects: multiple can be added
config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebAPI.XML")));
#endregion
Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type.
Also, the string arrays will be used for IEnumerable<string>. The sample objects will be serialized into different media type
formats by the available formatters.
//config.SetSampleObjects(new Dictionary<Type, object>
//{
// {typeof(string), "sample string"},
// {typeof(IEnumerable<string>), new string[]{"sample 1", "sample 2"}}
//});
// Extend the following to provide factories for types not handled automatically (those lacking parameterless
// constructors) or for which you prefer to use non-default property values. Line below provides a fallback
// since automatic handling will fail and GeneratePageResult handles only a single type.
#if Handle_PageResultOfT
config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult);
#endif
// Extend the following to use a preset object directly as the sample for all actions that support a media
// type, regardless of the body parameter or return type. The lines below avoid display of binary content.
// The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object.
#region *************************Default JSON display interface******************** *********************
//config.SetSampleForMediaType(
// new TextSample("Binary JSON content. See http://bsonspec.org for details."),
// new MediaTypeHeaderValue("application/bson"));
#endregion
Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format
and have IEnumerable<string> as the body parameter or return type.
//config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable<string>));
Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values"
and action named "Put".
//config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put");
Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png"
on the controller named "Values" and action named "Get" with parameter "id".
//config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id");
Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent<string>.
The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter.
//config.SetActualRequestType(typeof(string), "Values", "Get");
Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent<string>.
The sample will be generated as if the controller named "Values" and action named "Post" were returning a string.
//config.SetActualResponseType(typeof(string), "Values", "Post");
}
#if Handle_PageResultOfT
private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type)
{
if (type.IsGenericType)
{
Type openGenericType = type.GetGenericTypeDefinition();
if (openGenericType == typeof(PageResult<>))
{
// Get the T in PageResult<T>
Type[] typeParameters = type.GetGenericArguments();
Debug.Assert(typeParameters.Length == 1);
// Create an enumeration to pass as the first parameter to the PageResult<T> constuctor
Type itemsType = typeof(List<>).MakeGenericType(typeParameters);
object items = sampleGenerator.GetSampleObject(itemsType);
// Fill in the other information needed to invoke the PageResult<T> constuctor
Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), };
object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, };
// Call PageResult(IEnumerable<T> items, Uri nextPageLink, long? count) constructor
ConstructorInfo constructor = type.GetConstructor(parameterTypes);
return constructor.Invoke(parameters);
}
}
return null;
}
#endif
}
}
This modification supports multiple XML document methods, and the extension class is as follows:
using System;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
using WebAPI.Areas.HelpPage;
using WebAPI.Areas.HelpPage.ModelDescriptions;
namespace WebAPI.Utils
{
/// <summary>A custom <see cref='IDocumentationProvider'/> that reads the API documentation from a collection of XML documentation files.</summary>
public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
/*********
** Properties
*********/
/// <summary>The internal documentation providers for specific files.</summary>
private readonly XmlDocumentationProvider[] Providers;
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name='paths'>The physical paths to the XML documents.</param>
public MultiXmlDocumentationProvider(params string[] paths)
{
this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray();
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(MemberInfo subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(Type subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(HttpControllerDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(HttpActionDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(HttpParameterDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetResponseDocumentation(HttpActionDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/*********
** Private methods
*********/
/// <summary>Get the first valid result from the collection of XML documentation providers.</summary>
/// <param name='expr'>The method to invoke.</param>
private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr)
{
return this.Providers
.Select(expr)
.FirstOrDefault(p => !String.IsNullOrWhiteSpace(p));
}
}
}
Note: HelpPageConfig.cs needs to comment out the following code when XML document comments are enabled
#region *************************Default JSON display interface******************** *********************
//config.SetSampleForMediaType(
// new TextSample("Binary JSON content. See http://bsonspec.org for details."),
// new MediaTypeHeaderValue("application/bson"));
#endregion
5. Next you can write some Controller.cs. Then start the project to view the Api link.
6. Configure automatic generation of interface test pages Add a reference to the NuGet installation package:
WebApiTestOnHelpPage
7. Click the "Test API" button to test.