WebApi controller summary is not showing on Swagger documentation - asp.net-web-api2

When I enable this documentation feature through Swagger I'm able to see all kind of information about my documentation but there is no details about my Controller name detail/description.
How to show controller documentation content like below example?
/// <summary>
/// Represents the alert api controller class.
/// <summary>
public class XYZController : ApiController
{
}
On enabling swagger I'm not able to see this content any where Represents the XYZ api controller class. here
However I able to see my all method description.

1.) Right click the project and Edit projname.csproj
Add the following
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
2.) Add the following to AddSwaggerGen in ConfigureServices
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
For more details goto:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-5.0&tabs=visual-studio

you need to add IncludeXmlComments extension in AddSwaggerGen as below:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1.0", new Info
{
Title = "My APIs",
Version = "v1.0",
Description = "REST APIs "
});
**// Set the comments path for the Swagger JSON and UI.**
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
For more details refer here

This is possible, See #Liversage answer on this page https://github.com/domaindrivendev/Swashbuckle/issues/572
services.AddSwaggerGen(c =>
{
var xmlPath = ...;
c.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
});

Is there following code in the SwaggerConfig class?
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.IncludeXmlComments(string.Format(#"{0}\bin\YourAssemlyName.XML", System.AppDomain.CurrentDomain.BaseDirectory));

As some guys above have reply already, I guess the question was about this:
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
s.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
set includeControllerXmlComments: true will allow to take summary of controllers.

I think this is related to the following issue :
https://github.com/domaindrivendev/Swashbuckle/issues/572
It is currently not possible to display the controller summary, according to the maintainer:
The reason this has been low on the priority list is because it's not something I've run into issues with. You can absolutely describe what every action in your API does using XML comments on your actions.

in my case, I only needed to mark to use the XML document
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>C:...\CertReports.Host.xml</DocumentationFile>

now, in .net core is easy
config.UseControllerSummaryAsTagDescription = true;

Related

Why swashbuckle example value does not work?

Here I added XML doc parameter example="this does not work" for swashbuckle framework.
/// <summary>Gets a user by id.</summary>
/// <param name="id" example="this does not work">The id of user.</param>
/// <returns>got user.</returns>
[HttpGet("Get")]
[ProducesResponseType(typeof(UserDTO), 200)]
[ProducesResponseType(typeof(AppException), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Get(string id)
{
return HandleResult(await _userServices.GetUserAsync(id));
}
But example value does not appear in Swagger UI.
Where I should look for the problem?
Unfortunately out of the box Swashbuckle doesn't support the example tag/attribute. You need to write an operation filter and implement your own. Or you can use the Swashbuckle.AspNetCore.Filters nuget package.
You need to add the following code in your program.cs file.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
});
//For Example tag support
builder.Services.AddSwaggerExamplesFromAssemblies(Assembly.GetEntryAssembly());
And once you run the app you will be able to see examples like this in your swagger documentation.

Dependency Injection Access While Configuring Service Registrations in asp.net Core (3+)

I have cases, where I want to configure services based on objects which are registered in the dependency injection container.
For example I have the following registration for WS Federation:
authenticationBuilder.AddWsFederation((options) =>{
options.MetadataAddress = "...";
options.Wtrealm = "...";
options.[...]=...
});
My goal in the above case is to use a configuration object, which is available via the DI container to configure the WsFederation-middleware.
It looks to me that IPostConfigureOptions<> is the way to go, but until now, I have not found a way to accomplish this.
How can this be done, or is it not possible?
See https://andrewlock.net/simplifying-dependency-injection-for-iconfigureoptions-with-the-configureoptions-helper/ for the I(Post)ConfigureOptions<T> way, but I find that way too cumbersome.
I generally use this pattern:
// Get my custom config section
var fooSettingsSection = configuration.GetSection("Foo");
// Parse it to my custom section's settings class
var fooSettings = fooSettingsSection.Get<FooSettings>()
?? throw new ArgumentException("Foo not configured");
// Register it for services who ask for an IOptions<FooSettings>
services.Configure<FooSettings>(fooSettings);
// Use the settings instance
services.AddSomeOtherService(options => {
ServiceFoo = fooSettings.ServiceFoo;
})
A little more explicit, but you have all your configuration and DI code in one place.
Of course this bypasses the I(Post)ConfigureOptions<T> entirely, so if there's other code that uses those interfaces to modify the FooSettings afterwards, my code won't notice it as it's reading directly from the configuration file. Given I control FooSettings and its users, that's no problem for me.
This should be the approach if you do want to use that interface:
First, register your custom config section that you want to pull the settings from:
var fooSettingsSection = configuration.GetSection("Foo");
services.Configure<FooSettings>(fooSettingsSection);
Then, create an options configurer:
public class ConfigureWSFedFromFooSettingsOptions
: IPostConfigureOptions<Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions>
{
private readonly FooSettings _fooSettings;
public ConfigureWSFedFromFooSettingsOptions(IOptions<FooSettings> fooSettings)
{
_fooSettings = fooSettings.Value;
}
public void Configure(WsFederationOptions options)
{
options.MetadataAddress = _fooSettings.WsFedMetadataAddress;
options.Wtrealm = _fooSettings.WsFedWtRealm;
}
}
And finally link the stuff together:
services.AddTransient<IPostConfigureOptions<WsFederationOptions>, ConfigureWSFedFromFooSettingsOptions>();
The configurer will get your IOptions<FooSettings> injected, instantiated from the appsettings, and then be used to further configure the WsFederationOptions.

Asp.net DirectoryBrowser Feature, how to control sort?

I've got an asp.net core webapp which uses the directory browser feature setup in my Startup.cs like this.
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "youtubeDLs")),
RequestPath = "/Downloads"
});
I surface this as a partial like this.
<div id="dynamicContentContainer"></div>
<script>
setTimeout(function () {
$("#dynamicContentContainer").load("/Downloads")
}, 2800);
</script>
And this is great, I have my file browser which I want, it's lovely.
BUT, I don't have any control on file sorting, and I'd love to sort the files on DateModified or DateCreated.
I've scoured the API Catalog on MSDN but can't find anything. Is this just something I can't control?
You can simply override HtmlDirectoryFormatter
public class SortedHtmlDirectoryFormatter : HtmlDirectoryFormatter
{
public SortedHtmlDirectoryFormatter(HtmlEncoder encoder) : base(encoder) { }
public override Task GenerateContentAsync(HttpContext context, IEnumerable<IFileInfo> contents)
{
var sorted = contents.OrderBy(f => f.LastModified);
return base.GenerateContentAsync(context, sorted);
}
}
and use it in your app:
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = staticPathProvider,
RequestPath = "/Downloads",
Formatter = new SortedHtmlDirectoryFormatter(HtmlEncoder.Default)
});
Actually there is no options to config sort in this middleware, I've raised this issue in github. Asp.net core has no plan to add this feature either. since UseDirectoryBrowser is more like a diagnostic tool. To achieve this you'd better replace DirectoryBrowserOptions.Formatter to customize the view. You can copy HtmlDirectoryFormatter and customize it to your liking.
I made this simple nuget package SortedHtmlDirectoryFormatter according to Elendil's suggestion.
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(rootDirectory),
RequestPath = "/your-request-path",
Formatter = new SortedHtmlDirectoryFormatter()
});
This will sort the file list by LastModified in descending order.

How to dynamically resolve controller with endpoint routing?

Upgrading to asp.net core 2.2 in my hobby project there is a new routing system I want to migrate to. Previously I implemented a custom IRouter to be able to set the controller for the request dynamically. The incoming request path can be anything. I match the request against a database table containing slugs and it looks up the a matching data container class type for the resolved slug. After that I resolve a controller type that can handle the request and set the RouteData values to the current HttpContext and passing it along to the default implementation for IRouter and everything works ok.
Custom implementaion of IRouter:
public async Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
var page = _pIndex.GetPage(requestPath);
if (page != null)
{
var controllerType = _controllerResolver.GetController(page.PageType);
if (controllerType != null)
{
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
newRouteData.Values["pageType"] = page.PageType;
newRouteData.Values["controller"] = controllerType.Name.Replace("Controller", "");
newRouteData.Values["action"] = "Index";
context.RouteData = newRouteData;
await _defaultRouter.RouteAsync(context);
}
}
}
A controller to handle a specific page type.
public class SomePageController : PageController<PageData>
{
public ActionResult Index(PageData currentPage)
{
return View("Index", currentPage);
}
}
However I got stuck when I'm trying to figure out how I can solve it using the new system. I'm not sure where I'm suppose to extend it for this behavior. I don't want to turn off the endpoint routing feature because I see an opportunity to learn something. I would aso appreciate a code sample if possible.
In ASP.NET 3.0 there is an new dynamic controller routing system. You can implement DynamicRouteValueTransformer.
Documentation is on the way, look at the github issue

Rhino AutoMocker and Stubs

I am using Joshua Flanagan article “Auto mocking Explained” as a guide. In the article there is a section called “The same tests, with automocker would look like this”. I used this information to build code to run the automocker.
As you can see below answer is a list returned from the BLL. Answer does have one row in it; however, all fields are null. So the test for boo fails. Any tips and hints would be greatly appreciated.
[Test]
public void GetStaffListAndRolesByTeam_CallBLLWithDALStub()
{
// Build List<> data for stub
List<StaffRoleByTeamCV> stubData = new List<StaffRoleByTeamCV>();
StaffRoleByTeamCV stubRow = new StaffRoleByTeamCV();
stubRow.Role = "boo";
stubRow.StaffId = 12;
stubRow.StaffName = "Way Cool";
stubData.Add(stubRow);
// create the automocker
var autoMocker = new RhinoAutoMocker<PeteTestBLL>();
// get instance of test class (the BLL)
var peteTestBllHdl = autoMocker.ClassUnderTest;
// stub out call to DAL inside of BLL
autoMocker.Get<IPeteTestDAL>().Stub(c => c.GetStaffListAndRolesByTeam("4146")).Return(stubData);
// make call to BLL this should return stubData
List<StaffRoleByTeamCV> answer = peteTestBllHdl.GetStaffListAndRolesByTeam("4146");
// do simple asserts to test stubData present
// this passes
Assert.IsTrue(1 == answer.Count, "Did not find any rows");
// this fails
Assert.IsTrue(answer[0].Role == "boo", "boo was not found");
}
I tried using MockMode.AAA but still no joy
An new version of AutoMocker (1.0.3) is available. The new version supports relay mode as in this example..
[TestMethod]
public void ShouldSupportOrderedTest()
{
//Arrange
var autoMocker = new RhinoAutoMocker<CustomerUpdater>();
var mockRepository = autoMocker.Repository;
using (mockRepository.Ordered())
{
autoMocker.Get<ICustomerDataProvider>().Expect(x => x.GetCustomer(Arg<int>.Is.Anything)).Return(new CustomerItem());
autoMocker.Get<ICustomerDataProvider>().Expect(x => x.UpdateCustomer(Arg<CustomerItem>.Is.Anything));
autoMocker.Get<ILogWriter>().Expect(x => x.Write(Arg<string>.Is.Anything));
autoMocker.Get<ILogWriter>().Expect(x => x.Write(Arg<string>.Is.Anything));
autoMocker.Get<IMailSender>().Expect(x => x.SendMail(Arg<string>.Is.Anything, Arg<string>.Is.Anything));
}
//Act
autoMocker.ClassUnderTest.UpdateCustomerName(1, "Frank", "frank#somecompany.com");
//Assert
ExceptionAssert.Throws<ExpectationViolationException>(mockRepository.VerifyAll,"IMailSender.SendMail(anything, anything); Expected #1, Actual #0.\r\nILogWriter.Write(anything); Expected #1, Actual #0.\r\n");
}
I haven't tried, but this article suggests that by default all the mocks created by automocker are not replayed:
http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/25/arrange-act-assert-with-structuremap-rhinoautomocker.aspx
Yes that was true for the previous version. But was changed to support ordered tests in version 1.0.3.