SwashBuckle Customising Operation Tags - asp.net-web-api2

I'm simply looking to remove the word controller from appearing in the swagger documentation. I've looked at using an IOperationFilter or IDocumentFilter to manually edit the tags, but the properties are read-only.
/// <summary>
/// Home
/// </summary>
public class HomeController : ApiController
{
}
I've enabled XmlComments as per documentation on GitHub. I don't have any other problem other than i dont have the ability to change the descriptions for a Controller.
private static string GetXmlCommentsPath()
{
return string.Format(#"{0}\bin\Sample.WebApi.XML", System.AppDomain.CurrentDomain.BaseDirectory);
}
And in the Swaggerconfig :-
c.IncludeXmlComments(GetXmlCommentsPath());

If you mean removing "controller" from the list of action groups (the default of which is the controller name) then in your SwaggerConfig.cs you can use the GroupActionsBy option:
c.GroupActionsBy(apiDesc =>
{
return apiDesc.ActionDescriptor.ControllerDescriptor.ControllerName.Replace("Controller", "");
}

Related

ASP.NET Core MVC Navigation Security Trimming

In ASP.NET Core MVC I would like to hide links in my navigation bar that the user is not authorized to access. Currently the MvcSiteMapProvider that I have used in previous projects does not support ASP.NET Core MVC.
A similar question was asked a couple of years ago and whilst the suggested answer would work it would require repeating the Authorization filter set on a controller/action to ensure links are hidden.
How can this be done and are there any current examples of security trimming in ASP.NET Core MVC?
I have created custom tag helper to handle this.
[HtmlTargetElement(Attributes = "asp-roles")]
public class SecurityTrimmingTagHelper : TagHelper
{
[ViewContext]
public ViewContext Context { get; set; }
[HtmlAttributeName("asp-roles")]
public string Roles { get; set; }
/// <summary>
/// Hides html element if user is not in provided role.
/// If no role is supplied the html element will be render.
/// </summary>
/// <param name="context"></param>
/// <param name="output"></param>
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (!Context.HttpContext.User.Identity.IsAuthenticated)
{
output.SuppressOutput();
}
if (!string.IsNullOrEmpty(Roles))
{
var roles = Roles.Split(',');
foreach (var role in roles)
{
if (!Context.HttpContext.User.IsInRole(role))
{
output.SuppressOutput();
return;
}
}
}
}
}
You can apply this to any html element. If you want to apply it to only specific html element ( lets say <li>) then change the HtmlTargetElement to
[HtmlTargetElement("li",Attributes = "asp-roles")]
then in view you can do
<li asp-roles="Admin">Admin</li>

IdentityUser not serializing Web Api

Not sure what is going on here.
I am exposing the Identity functionality through a Web API project. The CRUD aspect will be exposed to an admin app and the login, registration in a public facing app.
Right now I am just trying to return a list of all users in the database through a Web Api controller action. I am getting nothing output to the response, but I do get back data from the service:
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("")]
public async Task<IHttpActionResult> GetAllUsers()
{
var model = await _userService.GetAllUsers(); //<---Gets List<AppUser> here?
return Ok(model);
}
This action shows nothing on fiddler or Postman?
Any ideas?
public class AppUser : IdentityUser
{
public DateTime Created { get; set; }
}
Is there something special about the IdentityUser class that prevents it from being serialized?
Here is the web api serialization config:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
config.Formatters.Add(new JsonFormatter());
}
public class JsonFormatter : JsonMediaTypeFormatter
{
public JsonFormatter()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
this.SerializerSettings.Formatting = Formatting.Indented;
}
public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
{
base.SetDefaultContentHeaders(type, headers, mediaType);
headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
Found my answer. The IdentityUser class is not really meant to be exposed over an API; lots of sensitive data and all.
However this is will sit behind a firewall and I do not feel like writing a DTO and mapper just to make this work.
The answer is explained here
Basically you just need to override the properties you want exposed and decorate them with a DataMember attribute for serialization.

Web API Help Page with template classes

I have a ASP.NET Web API which returns a template class but I can't get the Web API Help Page to provide documentation for the return type correctly.
Let's say I have the following Model classes:
public class MyType<T>
{
/// <summary>A list of T</summary>
public List<T> MyList { get; set; }
}
public class Foo
{
/// <summary>Bar string</summary>
public string Bar { get; set; }
}
and my API action looks as follows
[ResponseType(typeof(MyType<Foo>))]
public IHttpActionResult Get()
{
return Ok<MyType<Foo>>(new MyType<Foo>());
}
The resulting Web API Help Page then declares that the Get action returns a MyTypeOfFoo and do not provide the XML documentation for MyType, it just lists the parameters it contains. Probably because it don't understand that MyTypeOfFoo is the same as MyType<Foo>.
Are there any known solutions to this problem?
Update
Creating a pseudo-class and returning it instead does not work either. E.g.
/// <summary>My Foo Type</summary>
public class MyFooType : MyType<Foo>
{
}
[ResponseType(typeof(MyFooType)]
public IHttpActionResult Get()
{
return Ok<MyFooType>(new MyFooType());
}
The documentation output for the above code lacks the comments available on the inherited properties.

NinjectWebCommon Bindings in RegisterServices not working for me in WebApi

I create a new ASP.NET Web API project. I then use nuget to pull Ninject.Web.Common, then I download and build Ninject.Web.WebApi from here. Included it in the project. I added a service and the injection via constructor, setup binding (the debugger shows that the code actually hits the bind) but still throws this error:
Error activating IValueService
No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency IValueService into parameter valueService of constructor of type ValuesController
1) Request for ValuesController
Suggestions:
1) Ensure that you have defined a binding for IValueService.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
Libraries in my project:
Ninject.dll
Ninject.Web.Common
Ninject.Web.Webapi
WebActivator
All ninject libraries are marked as version 3.
Here is the Code:
[assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplication3.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(MvcApplication3.App_Start.NinjectWebCommon), "Stop")]
namespace MvcApplication3.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IValueService>().To<ValueService>();
}
}
}
service:
public interface IValueService
{
List<string> GetStrings();
}
public class ValueService : IValueService
{
public List<string> GetStrings()
{
return new List<string>{"test", "test"};
}
}
controller:
public class ValuesController : ApiController
{
private readonly IValueService valueService;
// GET /api/values
public ValuesController(IValueService valueService)
{
this.valueService = valueService;
}
}
The downloaded project sample from github works but not when I create a new project. Am I missing a step?
I just checked that there is no issue with this extension (at least not with the lastest MVC4 preview):
Create a new MVC4 WEB Api project
Nuget Install-Package Ninject.Web.WebAPI
Change controller + add bindings
Your code looks good. There must be a problem somewhere else.
Do not bind directly in RegisterServices but first create a class from
IDependencyResolver en add your bindings in AddBindings
example
See in book Pro ASP.NET MVC5 from Adam Freeman

Temporarily turn off identity column with Fluent AutoMap?

I have begun to test Fluent NHibernate in C#
I have a well normalized object structure with 20 related classes.
I currently use Fluent 1.3 with NHibernate 3.2.
So far I have managed to use the AutoMap feature which suits me fine,
Very convenient!
BUT ...
3 of the tables are "enum tables" that need to have their records set with specific Id value.
I tried to make manual mappings of these tables and let the rest be automapped.
But when the manual table is created it fails because it references a table that is automapped (and not available for manual mapper?)
Is it possible to use AutoMapping but for some very few classes override identity creation on primary key?
I tried to make a custom convention but without success.
public class OverrideIdentityGeneration : Attribute
{
}
public class ConventionIdentity : AttributePropertyConvention<OverrideIdentityGeneration>
{
protected override void Apply(OverrideIdentityGeneration attribute, IPropertyInstance instance)
{
instance.Generated.Never();
}
}
Is there some other way?
It would be sad to be forced back to use manual mapping for all classes ....
class MyIdConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
if (instance.EntityType == ...)
{
instance.GeneratedBy.Assigned();
}
}
}
Update:
for enum-like classes it's often easier to define an enum as id
class ConfigValue
{
public virtual Config Id { get; set; }
}
// the convention is easy
if (instance.EntityType.IsEnum)
{
instance.GeneratedBy.Assigned();
// to save as int and not string
instance.CustomType(typeof(Config));
}
// querying without magic int values
var configValue = Session.Get<ConfigValue>(Config.UIColor);
I used the idea given by Fifo and extended it to use a custom attribute instead.
To make code readable and avoid redundance when using similar idea in other conventions I added an extension method to check for custom attribute.
This is the code I ended up with:
/// <summary>
/// Convention to instruct FluentNHIbernate to NOT generate identity columns
/// when custom attribute is set.
/// </summary>
public class ConventionIdentity : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
if(instance.CustomAttributeIsSet<NoIdentity>())
instance.GeneratedBy.Assigned();
}
}
/// <summary>
/// Custom attribute definition.
/// </summary>
public class NoIdentity : Attribute
{
}
/// <summary>
/// Example on how to set attribute.
/// </summary>
public class Category
{
[NoIdentity]
public int Id { get; set; }
public string Name { get; set; }
}
public static class IInspectorExtender
{
/// <summary>
/// Extender to make convention usage easier.
/// </summary>
public static T GetCustomAttribute<T>(this IInspector instance)
{
var memberInfos = instance.EntityType.GetMember(instance.StringIdentifierForModel);
if(memberInfos.Length > 0)
{
var customAttributes = memberInfos[0].GetCustomAttributes(false);
return customAttributes.OfType<T>().FirstOrDefault();
}
return default(T);
}
}