I' using ASP.net Core and swagger, I have created the following method and verbose documentation:
/// <summary>Creates a package with the requested information.</summary>
/// <param name="createPackage">The package information to create.</param>
/// <response code="201" cref="PackageCreatedExample">Created</response>
/// <returns><see cref="PackageCreated"/> package created.</returns>
[HttpPost]
[SwaggerResponse(201, "Package created", typeof(PackageCreated))]
[SwaggerResponse(400, "Validation failure", typeof(ApiErrorResult))]
[SwaggerResponseExample(201, typeof(PackageCreatedExample))]
public async Task<IActionResult> CreatePackage(PackageCreate createPackage)
{
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResult(ModelState));
}
var createdPackageInfo = new PackageCreated();
// Created item and location returned.
return CreatedAtAction(nameof(GetPackage), new { createdPackageInfo.Id, Version = "2" }, createdPackageInfo);
}
I am trying to get an example response to appear in swagger but it always defaults the response sample as follows:
As you can see from the code above, I created a "PackageCreatedExample" class that I want to be picked up and used in swagger but it's just being ignored. I tried using comments response code="201" cref="PackageCreatedExample" and wrote the SwaggerResponseExample attribute, but neither get picked up. Here's my example code:
public class PackageCreatedExample : IExamplesProvider<PackageCreated>
{
public PackageCreated GetExamples()
{
return new PackageCreated {
Outcome = "PackageCreated",
Reference = "6178",
ShippingDocumentation = new List<Documentation> {
new Documentation {
Document = "JVBERi0xLjMNCjEgMCBvYmoNCjw8DQovVHlwZSAvQ2F...",
Format = "Pdf",
Type = DocumentationType.TYPE3
}
},
ReturnsDocumentation = new List<Documentation> {
new Documentation {
Document = "YmoNCjw8DQovVHlwZSAvQ2F0YWxvZw0KL1BhZ2VzIDQgMJVBERi0xLjMNCjEgMCBv...",
Format = "doc",
Type = DocumentationType.TYPE4
}
}
};
}
}
What am I doing wrong?
Thanks in advance for any pointers!
According to the author, this method should not be used anymore (link). This section of the official readme describes the way how the descriptions and examples should be handled.
In short, you have to
Remove all the annotations and use the followings instead:
/// <summary>Creates a package with the requested information.</summary>
/// <param name="createPackage">The package information to create.</param>
/// <returns>package created.</returns>
/// <response code="201">Created</response>
/// <response code="400">Validation failure</response>
[HttpPost]
[ProducesResponseType(typeof(PackageCreated), 201)]
[ProducesResponseType(typeof(ApiErrorResult), 400)]
public async Task<IActionResult> CreatePackage(PackageCreate createPackage)
Enable generating XML documentation under Project -> Properties -> Build tab -> Check XML documentation file in the Output section
Configure swagger to use the generated XML file
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1",
new OpenApiInfo
{
Title = "My API - V1",
Version = "v1"
}
);
var filePath = Path.Combine(System.AppContext.BaseDirectory, "MyApi.xml");
c.IncludeXmlComments(filePath);
}
Add example comments to the properties of your input model classes:
PackageCreate.cs
public PackageCreate
{
// Built-in type
/// <summary>
/// Outcome value
/// </summary>
/// <example>PackageCreated</example>
public string Outcome { get; set; }
// Custom class -> comment its properties in Documentation.cs
public Documentation { get; set; }
// Array type -> comment the properties in Documentation.cs
public IList<Documentation> { get; set; }
}
Documentation.cs:
public Documentation
{
/// <summary>
/// Document name
/// </summary>
/// <example>YmoNCjw8DQovVHlwZSAvQ2F0YWxvZw0KL1BhZ2VzIDQgMJVBERi0xLjMNCjEgMCBv</example>
public string Document { get; set; }
/// <summary>
/// Document format
/// </summary>
/// <example>doc</example>
public string Format { get; set; }
}
Related
Im currently trying to call a field on graphql-query from code, without using the http layer. In a test case I had success using this snippet inside of a field resolver. The breakpoint hits.
var newContext = new ResolveFieldContext(context);
var query = context.ParentType;
var ticketQueryField = query.GetField("getTickets");
await (Task) ticketQueryField.Resolver.Resolve(context);
So I think its possible to fill the copied ResolveFieldContext with my real needed fields/arguments and call it like this. But its very ... complicated to fill the ResolveFieldContext by hand. So maybe there is a easier way to create the context. Like:
var newContext = new ResolveFieldContext("query test { getTickets(id: 1) { number, title } }");
That would be really awesome and in my real scenario there a more then just field which I want to access with the generated query.
Why I want to use the Graph like this? The Batch-Loader which we are using inside the GraphQL-Types are perfect for our needs.
You can execute a GraphQL query without http by using the DocumentExecutor directly, and providing your own DocumentWriter if you want the data in a specific format. There is an extension method which returns JSON, but you can write your own.
https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL.NewtonsoftJson/DocumentWriter.cs
This is an example test base class for testing queries:
https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL.Tests/BasicQueryTestBase.cs
This is a console example that returns JSON, not using http.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Authorization;
using GraphQL.SystemTextJson;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.Extensions.DependencyInjection;
namespace BasicSample
{
internal class Program
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "main")]
private static async Task Main()
{
using var serviceProvider = new ServiceCollection()
.AddSingleton<IAuthorizationEvaluator, AuthorizationEvaluator>()
.AddTransient<IValidationRule, AuthorizationValidationRule>()
.AddTransient(s =>
{
var authSettings = new AuthorizationSettings();
authSettings.AddPolicy("AdminPolicy", p => p.RequireClaim("role", "Admin"));
return authSettings;
})
.BuildServiceProvider();
string definitions = #"
type User {
id: ID
name: String
}
type Query {
viewer: User
users: [User]
}
";
var schema = Schema.For(definitions, builder => builder.Types.Include<Query>());
// remove claims to see the failure
var authorizedUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("role", "Admin") }));
string json = await schema.ExecuteAsync(_ =>
{
_.Query = "{ viewer { id name } }";
_.ValidationRules = serviceProvider
.GetServices<IValidationRule>()
.Concat(DocumentValidator.CoreRules);
_.RequestServices = serviceProvider;
_.UserContext = new GraphQLUserContext { User = authorizedUser };
});
Console.WriteLine(json);
}
}
/// <summary>
/// Custom context class that implements <see cref="IProvideClaimsPrincipal"/>.
/// </summary>
public class GraphQLUserContext : Dictionary<string, object>, IProvideClaimsPrincipal
{
/// <inheritdoc />
public ClaimsPrincipal User { get; set; }
}
/// <summary>
/// CLR type to map to the 'Query' graph type.
/// </summary>
public class Query
{
/// <summary>
/// Resolver for 'Query.viewer' field.
/// </summary>
[GraphQLAuthorize("AdminPolicy")]
public User Viewer() => new User { Id = Guid.NewGuid().ToString(), Name = "Quinn" };
/// <summary>
/// Resolver for 'Query.users' field.
/// </summary>
public List<User> Users() => new List<User> { new User { Id = Guid.NewGuid().ToString(), Name = "Quinn" } };
}
/// <summary>
/// CLR type to map to the 'User' graph type.
/// </summary>
public class User
{
/// <summary>
/// Resolver for 'User.id' field. Just a simple property.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Resolver for 'User.name' field. Just a simple property.
/// </summary>
public string Name { get; set; }
}
}
I am working with my first Blazor app and running into a strange problem. I have a Web API that returns a fairly basic JSON response:
{
"entityId": 26,
"notifications": [
{
"type": "Success",
"message": "The operation completed successfully."
}
],
"hasErrors": false,
"hasWarnings": false
}
There is a standard POCO class that matches the above properties. From the Blazor app, when I try to get the response from an Http.PutAsJsonAsync<T>, an instance of the POCO class is created (so it's not null and doesn't throw an error), but none of the values above are actually present. The EntityId property is null and Notifications is instantiated but empty. The way I'm trying to access the response is:
var result = await Http.PutAsJsonAsync<ManufacturerModel>($"manufacturers", (ManufacturerModel)context.Model);
if (result.IsSuccessStatusCode)
{
var response = await JsonSerializer.DeserializeAsync<EntityResponseModel>(result.Content.ReadAsStream());
//response isn't null - it's just a newly created object with nothing mapped
}
Via the console in Chrome, I've confirmed the proper JSON is returned so it's really confusing why it'd create a new instance of that class, but not map any of the values.
Any ideas?
**** EDIT - including POCO definition ****
public class EntityResponseModel : BaseModel
{
/// <summary>
/// Gets or sets the Id of the affected entity
/// </summary>
public long? EntityId { get; set; }
}
public class BaseModel
{
public BaseModel()
{
this.Notifications = new EntityNotificationCollection();
}
#region Validation
/// <summary>
/// Adds a notification to this model.
/// </summary>
/// <param name="type">The type of notification</param>
/// <param name="message">The message to display</param>
public void AddNotification(EntityNotificationTypes type, string message)
{
this.Notifications.Add(new EntityNotification { Type = type, Message = message });
}
/// <summary>
/// Gets or sets the collection of notifications
/// </summary>
public EntityNotificationCollection Notifications { get; private set; }
/// <summary>
/// Gets whether errors exist on this model.
/// </summary>
//[JsonIgnore]
public bool HasErrors { get => this.Notifications.HasErrors; }
/// <summary>
/// Gets whether warnings exist on this model
/// </summary>
//[JsonIgnore]
public bool HasWarnings { get => this.Notifications.HasWarnings; }
#endregion
}
You can specify your serialization settings and define it as being case sensitive or insensitive.
CharlieFace provided this answer above.
Looks like you need to add your JsonAttribute to managing your case sensitivity.
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
This is my full solution:
var retList = new List<PocoSomeClassData> ();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
using (var reponseStream = await response.Content.ReadAsStreamAsync())
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
retList = await JsonSerializer.DeserializeAsync < List<PocoSomeClassData> > (reponseStream,options);
}
}
I'm developing API via c# that will send notification to specific user ( android user) , then when user open the notification I want to redirect him to specific activity.
So I needed to send data along with notification message. I've tested it using Firebase Console and it's working fine , The notification is received and my launcher activity receive the extra from data has been sent
I've also tested it from my backend and the notification is received except that my launcher intent doesn't receive any extra.
I've been struggling for hours now , Any idea would help !
this is my code from c#
public String getNotification ()
{
string serverKey = "xxxx";
var result = "-1";
try
{
var webAddr = "https://fcm.googleapis.com/fcm/send";
var regID = "xxxx";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("Authorization:key=" + serverKey);
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"to\": \"" + regID +
"\",\"notification\": {\"title\": \"Testing\",\"body\": \"Hi Testing\"}" +
"," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return "Can't Send";
}
}
}
And this is my launcher activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("test" , "in main");
if (getIntent().getStringExtra("mymsg") != null) {
Log.d("test" , "has extra");
Intent intent = new Intent(this, Main2Activity.class);
startActivity(intent);
finish();
} else {
Log.d("test" , "no extra");
}
It looks like you have the wrong JSON:
," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }} will be:
"data:" {
"mymsg":"h"
}
just correct your JSON. But I recommend using c# classes and serialization. Look at this simple example:
var payload = new {
to = "XXXX",
notification = new
{
body = "Test",
title = "Test"
},
data = new {
mymsg = "h"
}
}
// Using Newtonsoft.Json
string postbody = JsonConvert.SerializeObject(payload).ToString();
But its just example. You should create classes instead of anonym objects and using JsonProperty or another way to serialize the object. Something like that:
/// <summary>
/// Data for sending push-messages.
/// </summary>
public class PushData
{
/// <summary>
/// [IOS] Displaying message
/// </summary>
[JsonProperty("alert")]
public Alert Alert { get; set; }
/// <summary>
/// [IOS] badge value (can accept string representaion of number or "Increment")
/// </summary>
[JsonProperty("badge")]
public Int32? Badge { get; set; }
/// <summary>
/// [IOS] The name of sound to play
/// </summary>
[JsonProperty("sound")]
public String Sound { get; set; }
/// <summary>
/// [IOS>=7] Content to download in background
/// </summary>
/// <remarks>
/// Set 1 for silent mode
/// </remarks>
[JsonProperty("content-available")]
public Int32? ContentAvailable { get; set; }
/// <summary>
/// [IOS>=8] Category of interactive push with additional actions
/// </summary>
[JsonProperty("category")]
public String Category { get; set; }
/// <summary>
/// [Android] Used for collapsing some messages with same collapse_key
/// </summary>
[JsonProperty(PropertyName = "collapse_key")]
public String CollapseKey { get; set; }
/// <summary>
/// [Android] This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline.
/// The maximum time to live supported is 4 weeks, and the default value is 4 weeks.
/// </summary>
/// <value>
/// Time_to_live value of 0 means messages that can't be delivered immediately will be discarded
/// </value>
[JsonProperty("time_to_live")]
public Int32 TimeToLive { get; set; }
/// <summary>
/// [Android] Uri of activity to open when push activated by user
/// </summary>
[JsonProperty("url")]
public String Url { get; set; }
/// <summary>
/// Payload for push
/// </summary>
[JsonProperty("data")]
public Payload Payload { get; set; }
}
with message builder which serialize your message body to correct json string.
I used https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-2.1&tabs=visual-studio#xml-comments to show my classes summaries description in SwaggerUI, it's OK but not show enum summary description !
My startup.cs
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "My App-Service",
Description = "My Description",
});
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"));
c.DescribeAllEnumsAsStrings();
});
My enum:
public enum GenderEnum
{
/// <summary>
/// Man Description
/// </summary>
Man = 1,
/// <summary>
/// Woman Description
/// </summary>
Woman = 2
}
It shows something like following:
I want to show Man Description and Woman Description in SwaggerUI
like this:
Man = 1, Man Description
Woman = 2, Woman Description
I'm using Swashbuckle.AspNetCore v4.0.1 package
As of June/2021 OpenApi now supports this and you can extend Swagger to show the details. Here is my code for C# on .NET 5.0.
First define the schema filter in a file (call it DescribeEnumMembers.cs and be sure to change YourNamespace to the name of your namespace):
using System;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace YourNamespace
{
/// <summary>
/// Swagger schema filter to modify description of enum types so they
/// show the XML docs attached to each member of the enum.
/// </summary>
public class DescribeEnumMembers: ISchemaFilter
{
private readonly XDocument mXmlComments;
/// <summary>
/// Initialize schema filter.
/// </summary>
/// <param name="argXmlComments">Document containing XML docs for enum members.</param>
public DescribeEnumMembers(XDocument argXmlComments)
=> mXmlComments = argXmlComments;
/// <summary>
/// Apply this schema filter.
/// </summary>
/// <param name="argSchema">Target schema object.</param>
/// <param name="argContext">Schema filter context.</param>
public void Apply(OpenApiSchema argSchema, SchemaFilterContext argContext) {
var EnumType = argContext.Type;
if(!EnumType.IsEnum) return;
var sb = new StringBuilder(argSchema.Description);
sb.AppendLine("<p>Possible values:</p>");
sb.AppendLine("<ul>");
foreach(var EnumMemberName in Enum.GetNames(EnumType)) {
var FullEnumMemberName = $"F:{EnumType.FullName}.{EnumMemberName}";
var EnumMemberDescription = mXmlComments.XPathEvaluate(
$"normalize-space(//member[#name = '{FullEnumMemberName}']/summary/text())"
) as string;
if(string.IsNullOrEmpty(EnumMemberDescription)) continue;
sb.AppendLine($"<li><b>{EnumMemberName}</b>: {EnumMemberDescription}</li>");
}
sb.AppendLine("</ul>");
argSchema.Description = sb.ToString();
}
}
}
Then enable it in your ASP.NET ConfigureServices() method. Here is my code after snipping out the parts that don't matter for this exercise:
public void ConfigureServices(IServiceCollection argServices) {
// ...<snip other code>
argServices.AddSwaggerGen(SetSwaggerGenOptions);
// ...<snip other code>
return;
// ...<snip other code>
void SetSwaggerGenOptions(SwaggerGenOptions argOptions) {
// ...<snip other code>
AddXmlDocs();
return;
void AddXmlDocs() {
// generate paths for the XML doc files in the assembly's directory.
var XmlDocPaths = Directory.GetFiles(
path: AppDomain.CurrentDomain.BaseDirectory,
searchPattern: "YourAssemblyNameHere*.xml"
);
// load the XML docs for processing.
var XmlDocs = (
from DocPath in XmlDocPaths select XDocument.Load(DocPath)
).ToList();
// ...<snip other code>
// add pre-processed XML docs to Swagger.
foreach(var doc in XmlDocs) {
argOptions.IncludeXmlComments(() => new XPathDocument(doc.CreateReader()), true);
// apply schema filter to add description of enum members.
argOptions.SchemaFilter<DescribeEnumMembers>(doc);
}
}
}
}
Remember to change "YourAssemblyNameHere*.xml" to match your assembly name. The important line that enables the schema filter is:
argOptions.SchemaFilter<DescribeEnumMembers>(doc);
...which MUST be called AFTER the following line:
argOptions.IncludeXmlComments(() => new XPathDocument(doc.CreateReader()), true);
Using the above code, if you have an enum type defined like this for example:
/// <summary>
/// Setting to control when a no-match report is returned when searching.
/// </summary>
public enum NoMatchReportSetting
{
/// <summary>
/// Return no-match report only if the search query has no match.
/// </summary>
IfNoMatch = 0,
/// <summary>
/// Always return no-match report even if the search query has a match.
/// </summary>
Always = 1,
/// <summary>
/// Never return no-match report even if search query has no match.
/// </summary>
No = 99
}
The Swagger documentation will end up showing a description of each enum member as part of the description of the enum type itself:
This solution allows for
Show underlying value as well as name/description
Handle multiple xml documentation files, but only process docs once.
Customization of the layout without code change
Here's the class...
using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace YourNamespace
{
/// <summary>
/// Swagger schema filter to modify description of enum types so they
/// show the XML docs attached to each member of the enum.
/// </summary>
public class DescribeEnumMembers : ISchemaFilter
{
private readonly XDocument xmlComments;
private readonly string assemblyName;
/// <summary>
/// Initialize schema filter.
/// </summary>
/// <param name="xmlComments">Document containing XML docs for enum members.</param>
public DescribeEnumMembers(XDocument xmlComments)
{
this.xmlComments = xmlComments;
this.assemblyName = DetermineAssembly(xmlComments);
}
/// <summary>
/// Pre-amble to use before the enum items
/// </summary>
public static string Prefix { get; set; } = "<p>Possible values:</p>";
/// <summary>
/// Format to use, 0 : value, 1: Name, 2: Description
/// </summary>
public static string Format { get; set; } = "<b>{0} - {1}</b>: {2}";
/// <summary>
/// Apply this schema filter.
/// </summary>
/// <param name="schema">Target schema object.</param>
/// <param name="context">Schema filter context.</param>
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var type = context.Type;
// Only process enums and...
if (!type.IsEnum)
{
return;
}
// ...only the comments defined in their origin assembly
if (type.Assembly.GetName().Name != assemblyName)
{
return;
}
var sb = new StringBuilder(schema.Description);
if (!string.IsNullOrEmpty(Prefix))
{
sb.AppendLine(Prefix);
}
sb.AppendLine("<ul>");
// TODO: Handle flags better e.g. Hex formatting
foreach (var name in Enum.GetValues(type))
{
// Allows for large enums
var value = Convert.ToInt64(name);
var fullName = $"F:{type.FullName}.{name}";
var description = xmlComments.XPathEvaluate(
$"normalize-space(//member[#name = '{fullName}']/summary/text())"
) as string;
sb.AppendLine(string.Format("<li>" + Format + "</li>", value, name, description));
}
sb.AppendLine("</ul>");
schema.Description = sb.ToString();
}
private string DetermineAssembly(XDocument doc)
{
var name = ((IEnumerable)doc.XPathEvaluate("/doc/assembly")).Cast<XElement>().ToList().FirstOrDefault();
return name?.Value;
}
}
}
and utilization...
services.AddSwaggerGen(c =>
{
...
// See https://github.com/domaindrivendev/Swashbuckle/issues/86
var dir = new DirectoryInfo(AppContext.BaseDirectory);
foreach (var fi in dir.EnumerateFiles("*.xml"))
{
var doc = XDocument.Load(fi.FullName);
c.IncludeXmlComments(() => new XPathDocument(doc.CreateReader()), true);
c.SchemaFilter<DescribeEnumMembers>(doc);
}
});
This then reports as
Unfortunately this does not seem to be supported in the OpenAPI specification. There is an open Github issue for this.
There is also an open Github issue for Swashbuckle.
However, in this issue there is a workaround to create a schema filter, which at least shows the enum value comments in the enum type description.
I solved this using a description attribute. Here is an example usage:
public enum GenderEnum
{
[Description("Man Description")]
Man = 1,
[Description("Woman Description")]
Woman = 2
}
Is there a way to add model information, like valid values, default values, summary, and other remarks into the swagger output?
For instance in c# how would I add the following comments and attributes into swagger?
/// <summary>
/// A clear summary
/// </summary>
/// <remarks>
/// Some remarks
/// </remarks>
public class A
{
public A()
{
_Field_A = 0;
_Field_B = string.Empty;
}
private int _Field_A { get; set; }
[Range(0, 150)]
public int Field_A
{
get
{
return _Field_A;
}
set
{
if (value != null) { _Field_A = value; }
}
}
private string _Field_B { get; set; }
/// <summary>
/// Field_B summary
/// </summary>
public string Field_B
{
get
{
return _Field_B;
}
set
{
if (value != null) { _Field_B = value; }
}
}
}
You will need to enable XML documentation file creation in your project properties:
Project Properties > Build > Check the XML Documentation File box
Then you can uncomment or add the following line to your SwaggerConfig.cs file:
c.IncludeXmlComments(GetXmlCommentsPath());
According to the Swashbuckle github, you can enable XML comments which will allow you to add the metadata accordingly.
httpConfiguration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "A title for your API");
c.IncludeXmlComments(GetXmlCommentsPathForControllers());
c.IncludeXmlComments(GetXmlCommentsPathForModels());
});