Generate model description in AspNet WebApi help pages - c#

How do I generate a description for my model in Asp.Net Web Api help pages?
Example:
As you can see from the example, I can already generate Name, Type and Additional Information. But how do I generate Description?
I've tried nothing and I'm all out of ideas.
No, that's not true. I've tried adding comments to my TransactionDto class, but it does not work.
/// <summary>
/// A DTO (Data Transfer Object) for Transaction objects.
/// </summary>
public class TransactionDto
{
/// <summary>
/// The manager who registered the transaction.
/// </summary>
public string FromId { get; set; }
/// <summary>
/// The receiving manager.
/// </summary>
[Required]
public string ToId { get; set; }
/// <summary>
/// Optional expiration date.
/// </summary>
public DateTime? Expires { get; set; }
/// <summary>
/// Date the transaction was created.
/// </summary>
public DateTime Created { get; set; }
}
I have configured HelpPageConfig.cs to use an XmlDocumentationProvider like so:
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
So how do I generate these descriptions for models?

I believe you have models in the separate project other than the Web API project?
If that is the case, the web API project is not aware of the help XML file generated for the models. you need to set the XML output path for both web API project and the models project and then combine both XML files in the register method of HelpPageConfig.cs file.
public static void Register(HttpConfiguration config)
{
XmlDocument apiDoc = new XmlDocument();
apiDoc.Load(HttpContext.Current.Server.MapPath("~/App_Data/WebApi.Orders.xml"));
XmlDocument contractsDoc = new XmlDocument();
contractsDoc.Load(HttpContext.Current.Server.MapPath("~/App_Data/Contracts.xml"));
if (contractsDoc.DocumentElement != null && apiDoc.DocumentElement!=null)
{
XmlNodeList nodes = contractsDoc.DocumentElement.ChildNodes;
foreach (XmlNode node in nodes)
{
XmlNode copiedNode = apiDoc.ImportNode(node, true);
apiDoc.DocumentElement.AppendChild(copiedNode);
}
apiDoc.Save(HttpContext.Current.Server.MapPath("~/App_Data/WebApi.Orders.xml"));
}
config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/WebApi.Orders.xml")));
......
}

Related

Swagger - How to add definitions on child objects in c#

In C# (Asp.Net Core) )I have the following documentation over a child object of type NameValue.
I wish to have the documentation I added here over FromWarehouse, but when Swagger renders, it uses the definition for NameValue, not FromWarehouse.
/// <summary>
/// The warehouse this transfer is coming from
/// </summary>
/// <remarks>GLW, WDI, PSO, SMA, SHW</remarks>
public NameValue FromWarehouse { get; set; }
You need to do 3 things:
The summary should be moved from class property to class.
not here
public NameValue FromWarehouse { get; set; }
but to
/// <summary>
/// The warehouse this transfer is coming from
/// </summary>
/// <remarks>GLW, WDI, PSO, SMA, SHW</remarks>
public class NameValue{
....
You need to add document generation, with Include Xml Comments
services.AddSwaggerGen(c =>
{
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
Add this to your .csproj file
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
This is the example I used
/// <summary>
/// The warehouse this transfer is coming from
/// </summary>
/// <remarks>GLW, WDI, PSO, SMA, SHW</remarks>
public class NameValue
{
/// <summary>
/// The Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// The Value
/// </summary>
public string Value { get; set; }
}
Here is the result:
The remarks, I was not able to include the, but as far as I understand, remarks are used to provide payload example, and it is used on top of the resource method.
All this I use with dotnet 6 and Swashbuckle.AspNetCore Version 6.2.3.
I hope it helps.
Reference to Microsoft documentation.

C# Net core XML deserialization or how to get indexes of rows in file

Please tell me if it is possible after the deserialization of the XML file to get line numbers, from which values for specific properties were taken. Or are there other ways? I need to get the line numbers in the file where errors occurred (there are paths to tags with errors, for example yml_catalog/offers/offer[ 1 ]/price)
I have here such a model, in which there are values from tags:
[XmlRoot("yml_catalog")]
public class YMLCatalog
{
/// <summary>
/// Time of generation
/// </summary>
[Required]
[XmlAttribute("date")]
public string Date { get; set; }
/// <summary>
/// Shop
/// </summary>
[Required]
[ValidateObject]
[XmlElement(ElementName = "shop")]
public Shop Shop { get; set; }
}
/// <summary>
/// Sale point
/// </summary>
public class Shop
{
/// <summary>
/// Shop name
/// </summary>
[Required]
[XmlElement("name")]
public string Name { get; set; }
/// <summary>
/// Company name
/// </summary>
[Required]
[XmlElement("company")]
public string Company { get; set; }
... etc
}
ValidationObject - This is an attribute for recursive validation via Validator.TryValidateObject(context,[my empty model object],[list of ValidationResult - error list], true);
Finding line numbers with regular expressions is not suitable for me, since the files can be very large and have a huge number of errors, and I only have error messages and paths to them
Example of file with an error in the first tag 'offer' (tag price is empty)
UPD - This solution is not suitable. There is no support for simple elements and attributes!
This can be done via LINQ. If you want to get information about a node with a number and a position, you should use the XmlDocument method instead of the SelectNode method, use XPathSelectElement, and bring it to the interface IXmlLineInfo
XDocument xdoc = XDocument.Load(file, LoadOptions.SetLineInfo);
var node = xdoc.XPathSelectElement("//shop/offers/offer[1]/price");
var lineNumber = ((IXmlLineInfo)node).LineNumber;

How to describe input model as much as possible using Swagger

I have an example input model as follows:
public class CarInputModel {
public string Name { get; set; }
public string ModelName { get; set; }
}
This values will come from UI, what kind of annotations can I use with swagger to describe this API model as much as possible?
You can't use many annotations at all to describe the model. You mostly describe the API itself
[HttpGet] and [HttpPost] for the http attributes
[Produces(typeof(CarInputModel)] for the return type of an action and [ProducesResponseType(typeof(CarInputModel), (int)HttpStatusCode.OK)] for result types based on http code (i.e. return different model on error)
[Route] for the route itself
Additionally you can use Xml Docs to describe the classes and their parameters.
/// <summary>
/// Adds a new car model.
/// </summary>
/// <param name="model">The model to add</param>
/// <response code="201">Returns when the car was added successfully and returns the location to the new resource</response>
/// <response code="400">Invalid Request data.</response>
/// <response code="409">Car mode already exists.</response>
/// <returns>The newly added model on success and a list of errors on failure.</returns>
[HttpPost]
[ProducesResponseType(typeof(CarInputModel), (int)HttpStatusCode.Created)]
[ProducesResponseType(typeof(SerializableError), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(void), (int)HttpStatusCode.Conflict)]
public IActionResult AddCar(CarInputModel model)
{
}
/// <summary>
/// Represents a car
/// </summary>
public class CarInputModel {
/// <summary>
/// Name of the car
/// </summary>
public string Name { get; set; }
/// <summary>
/// Model of the car
/// </summary>
public string ModelName { get; set; }
}
In order to use XmlDocs you need to enable compilation of the xml docs in your project settings (and the of your models) and then add this to your Startup.cs
services.AddSwaggerGen(options =>
{
var appEnv = PlatformServices.Default.Application;
options.IncludeXmlComments(Path.Combine(appEnv.ApplicationBasePath, $"{appEnv.ApplicationName}.xml"));
});

Duplicate parameters output through Swashbuckle/Swagger

Code is further down in the post.
Question: I would like Swashbuckle to generate the following two "GET" requests:
1. mydomain.com/api/books?apikey=12345567891011121314151617181920
2. mydomain.com/api/books/1234?apikey=12345567891011121314151617181920
Swashbuckle does fine on #1 and it works great from the Swagger UI. But for #2, it ends up generating (and calling):
mydomain.com/api/books/{Id}?id=1234&apikey==12345567891011121314151617181920
Needless to say, that GET fails. Swashbuckle is picking up the literal "Id" from the route attribute as well as the BookDetail object. In fact, it shows both IDs in the UI, but I was able to solve that by de-duping by hooking up a custom IOperationFilter, but that obviously didn't help with correcting the actual GET request path.
I already looked at Duplicate parameter output in Swagger 2 but that answer does not work for me. I am looking for having Swashbuckle use the "Id" that's part of the BookDetail object so that Swagger UI shows the description (from the XML comment) while supporting route based ID rather than query string based: mydomain.com/api/books/1234.... What am I doing wrong? (I am on Swashbuckle 5.2.2).
Code:
/// <summary>
/// Books controller
/// </summary>
[RoutePrefix("api")]
public class BooksController : ApiController
{
/// <summary>
/// Returns books matching the search query
/// </summary>
/// <param name="searchRequest"></param>
/// <returns></returns>
[Route("books")]
public IHttpActionResult Get(BookSearch searchRequest)
{
//Do stuff with request
return Ok();
}
/// <summary>
/// Retruns a single book for the given ID
/// </summary>
/// <param name="detailRequest"></param>
/// <returns></returns>
[Route("books/{id:int}")]
public IHttpActionResult Get(BookDetail detailRequest)
{
//Do stuff with request
return Ok();
}
/// <summary>
/// Book search
/// </summary>
public class BookSearch
{
/// <summary>
/// API key
/// </summary>
[Required]
public string ApiKey { get; set; }
/// <summary>
/// Search terms
/// </summary>
public string Query { get; set; }
}
/// <summary>
/// Book detail
/// </summary>
public class BookDetail
{
/// <summary>
/// API key
/// </summary>
[Required]
public string ApiKey { get; set; }
/// <summary>
/// Book of the ID you want to retrieve
/// </summary>
[Required]
public int Id { get; set; }
/// <summary>
/// Boolean indicating whether to include photos or not
/// </summary>
public bool IncludePhotos { get; set; }
}
}

Documentation of a class property is returned as null when accessed from other projects

I am trying to create Web API documentation (which is a part of web-service) for our project. When I try to read the ResourceModal of an Object (http://localhost:56258/Help/ResourceModel?modelName=LoginInfo) that resides in the same project it gives me the Property Documentation correctly.
namespace SmartTouch.CRM.WebService.Models
{
/// <summary>
/// LoginInfo
/// </summary>
public class LoginInfo
{
/// <summary>
/// Email that is used to login to SmartTouch Web Application
/// </summary>
public string UserName { get; set; }
/// <summary>
/// Password that is used to login to SmartTouch Web Application
/// </summary>
public string Password { get; set; }
/// <summary>
/// Client ID that is provided by SmartTouch Administrator
/// </summary>
public string ApiKey { get; set; }
}
}
But, when i try to read the ResourceModal of another Object which resides in another project then the Description is being returned as null though I have generated the XMLDocumentation file. Any help?

Categories