How to personalize Auto API Controllers endpoints in APB Framework - c#

I'm learning APB framework. ABP can automagically configure the application services as API Controllers by convention. The documentation says it is possible to fully customize it.
In the example they provide, I would like to change an action name of one of the following endpoints:
/api/app/book to /api/app/books.
But unfortunately I cannot find how to do it.
I tried to change the ActionName of the corresponding service method:
public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
}
[ActionName("books"), HttpGet]
public override Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
return base.GetListAsync(input);
}
}
But the resulting endpoint is not what I want:
Any idea how to do it ?

in abp.io (ABP VNext) , you can diable or configure ConventionalControllers in HttpModules config file of your Project .
Edit this Method :
private void ConfigureConventionalControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(SystemApplicationModule).Assembly);
});
}

Related

Generic controller inside razor class library

I have a generic controller that's inside a razor class library referenced in a net core web app. Problem: When I launch the web app and try to navigate to /MyFeature/User, it still doesn't recognize that endpoint at all. Note that when I remove the generic type from the controller and its constructor, the endpoint /MyFeature/User works just fine.
Here is the controller:
[Area("MyFeature")]
[Authorize]
public class UserController<TIdentityUser> : Controller
where TIdentityUser : IdentityUser
{
Next, I'm trying to make the web app accept controllers that have generic types :
public void ConfigureServices(IServiceCollection services)
{
......
services.AddControllerFeatureProvider(Configuration);
....
}
Here is the implementation of AddControllerFeatureProvider from the razor class library:
public static void AddControllerFeatureProvider(this IServiceCollection services, IConfiguration config)
{
services.AddMvcCore().ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(new MyControllerFeatureProvider());
});
}
MyControllerFeatureProvider makes the app accept controllers with generic types. I've debugged that part and it works fine. UserController`1 goes into the breakpoint and it returns IsController as true.
But still, the endpoint doesn't work at all.
public class MyControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
var isController = base.IsController(typeInfo);
if (!isController && typeInfo.Name.Contains("Test") || typeInfo.Name.Contains("User"))
{
isController = typeInfo.Name.EndsWith("Controller`1", StringComparison.OrdinalIgnoreCase);
}
return isController;
}
}

Authorize with a specific scheme in ASP.NET MVC 4

Is there a way to require a specific Authorization Scheme when using the [Authorize] Attribute on a Controller in asp.net MVC 4?
I expected something like this (which is totally possible in .net core btw)
[Authorize(AuthenticationSchemes = "Bearer")]
public class MyController : Controller { }
As far as I know, there is nothing out of the box that would allow you to write this.
The standard authorize attribute doesn't support this.
But you could write your own attribute and check the claims of the identity coming in.
I used an backport of ASP.NET Core authorization policies to .NET Full framework: https://github.com/DavidParks8/Owin-Authorization to write such rules.
How to check of you come from which token?
Normally you will see a claim similar to "idp": "oidc"
How to get the claims? ((ClaimsPrinciple)User).Claims ( in Controller code)
As suggested by #Chetan Ranpariya in the comments I ended up implementing a derived attribute (from AuthorizeAttribute). According to the documentation, overriding the AuthroizeCore method is the way to do it.
When overridden, provides an entry point for custom authorization checks.
Here is a working example for future reference
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public string AuthSchemes { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (this.AuthSchemes != null)
{
string scheme = httpContext?.User?.Identity?.AuthenticationType;
if (string.IsNullOrWhiteSpace(scheme))
{
return false;
}
return this.AuthSchemes.Split(',').Contains(scheme);
}
return base.AuthorizeCore(httpContext);
}
}
The attribute can be used like this
[MyAuthorize(AuthSchemes = CookieAuthenticationDefaults.AuthenticationType)]
public class MyController : Controller { }

Expose extended controller using swagger and Swashbuckle

Our team opted to start by defining a yaml file first and generating server stubs using swagger code-gen.
Question 1
Is this the standard way to design asp.net core 2 APIs?
Question 2
How can we just expose extended controller. Say we use swagger generated api controller as base class and override the existing method in a different file so that we can keep generated code untouched. When I try to host this project using swachbuckle I end up getting "System.NotSupportedExceptioL: HTTP method "POST" & path overloaded by actions".
public class BaseApiController : Controller
{
[HttpGet]
[Route("/api/data")]
[SwaggerOperation("GetData")]
[Swashbuckle.AspNetCore.SwaggerGen.SwaggerResponse(200, type: typeof(List<Data>))]
public virtual IActionResult GetData()
{
string exampleJson = null;
var example = exampleJson != null
? JsonConvert.DeserializeObject<List<Data>>(exampleJson)
: default(List<Tag>);
return new ObjectResult(example);
}
}
public class ChildApiController : BaseApiController
{
public override IActionResult GetData()
{
return base.GetData();
}
}

How can versioning be done in ASP.NET Core Web Api

In previous asp.net web api, I implement DefaultHttpControllerSelector to specify how I want the request to locate my controller. I often have different controllers with different names but intended for same processes. The only difference is that one is of higher version than the other.
For example, I could have a controller named BookingV1Controller, which would be meant to handle the version one of the service. I would also have BookingV2Controller, which was designed to handle the version two of the service. A client application would then make a request to the service with this url http://myservice.com/api/v2/booking/someaction?id=12. To handle the request, I would provide a custom implementation of DefaultHttpControllerSelector to select the appropriate version of the controller required based on the requested version.
However, I seems not to have a way to do this in ASP.NET Core. I have searched everywhere to no avail. No documentation that could help either.
I would appreciate if anyone can be of help to me here. Thanks.
UPDATE
I would also like to know what to do if the version is specified in a custom header. E.g X-Version:v1
UPDATE 2
The requirement was that the version of the service should not be exposed in the URL. If no version is present, the service returns with instruction on how to add the version. If a requested controller is not present in the version requested, the system searches through the lower versions. If it finds it in any lower versions, it uses that. The reason for this is to prevent repetition of controllers on all versions. But with ASP.NET Core, this might not be possible.
This is a very old question that I stumbled upon, but there are much better solutions now. There is this package
Microsoft.AspNetCore.Mvc.Versioning
Which has a much more feature rich way of implementing versioning controls. These include being able to use URL query strings, url paths, headers, or custom version readers. Being able to read the version from HTTPContext etc.
In short, you add the following into your ConfigureServices method in startup.cs
services.AddApiVersioning(o => {
o.ReportApiVersions = true;
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});
Then you have to decorate your controllers with an ApiVersion.
[ApiVersion("1.0")]
[Route("api/home")]
public class HomeV1Controller : Controller
{
[HttpGet]
public string Get() => "Version 1";
}
[ApiVersion("2.0")]
[Route("api/home")]
public class HomeV2Controller : Controller
{
[HttpGet]
public string Get() => "Version 2";
}
You can also implement it in the path by putting it in the route.
[ApiVersion("1.0")]
[Route("api/{version:apiVersion}/home")]
public class HomeV1Controller : Controller
{
[HttpGet]
public string Get() => "Version 1";
}
[ApiVersion("2.0")]
[Route("api/{version:apiVersion}/home")]
public class HomeV2Controller : Controller
{
[HttpGet]
public string Get() => "Version 2";
}
When you go down this method of actually having it implemented via the Microsoft package, it also means that you are able to deprecate versions, have version discovery, access the version number from the HttpContext easily etc. None of which you could really do if it's just hardcoded in your route.
For more info (Including using it in a header) :
http://dotnetcoretutorials.com/2017/01/17/api-versioning-asp-net-core/
http://www.hanselman.com/blog/ASPNETCoreRESTfulWebAPIVersioningMadeEasy.aspx
https://github.com/Microsoft/aspnet-api-versioning/wiki
I created a package for this purpose exactly after banging my head on this problem for a few days. It doesn't require attributes.
https://github.com/GoAheadTours/NamespaceVersioning
In summary, you can register an IApplicationModelConvention in your startup file that can iterate through controllers and register routes based on the namespaces. I created a v1 folder, and put my controller inside
The class that implements IApplicationModelConvention implements an Apply method with an ApplicationModel parameter that will have access to the Controllers in your app and their existing routes. If I see a controller does not have a route set up in my class I get the version from the namespace and use a pre-defined URL prefix to generate a route for that version.
public void Apply(ApplicationModel application) {
foreach (var controller in application.Controllers) {
var hasRouteAttribute = controller.Selectors.Any(x => x.AttributeRouteModel != null);
if (hasRouteAttribute) {
continue;
}
var nameSpace = controller.ControllerType.Namespace.Split('.');
var version = nameSpace.FirstOrDefault(x => Regex.IsMatch(x, #"[v][\d*]"));
if (string.IsNullOrEmpty(version)) {
continue;
}
controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel() {
Template = string.Format(urlTemplate, apiPrefix, version, controller.ControllerName)
};
}
}
I have all the code up on github and a link to the package on nuget as well
Use the routing attributes to control versions.
i.e.
[Route("api/v1/[controller]")]
public class BookingV1Controller : Controller
{
....
}
[Route("api/v2/[controller]")]
public class BookingV2Controller : Controller
{
....
}
For more information relating to migrating from standard Web Api and .NET Core ASP.NET have a look at: MSDN: Migrating from ASP.NET Web Api
For that Add service API versioning to your ASP.NET Core applications
public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddApiVersioning();
// remaining other stuff omitted for brevity
}
QUERYSTRING PARAMETER VERSIONING
[ApiVersion( "2.0" )]
[Route( "api/helloworld" )]
public class HelloWorld2Controller : Controller {
[HttpGet]
public string Get() => "Hello world!";
}
So this means to get 2.0 over 1.0 in another Controller with the same route, you'd go here:
/api/helloworld?api-version=2.0
we can have the same controller name with different namespaces
URL PATH SEGMENT VERSIONING
[ApiVersion( "1.0" )]
[Route( "api/v{version:apiVersion}/[controller]" )]
public class HelloWorldController : Controller {
public string Get() => "Hello world!";
}
[ApiVersion( "2.0" )]
[ApiVersion( "3.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : Controller {
[HttpGet]
public string Get() => "Hello world v2!";
[HttpGet, MapToApiVersion( "3.0" )]
public string GetV3() => "Hello world v3!";
}
Header Versioning
public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddApiVersioning(o => o.ApiVersionReader = new HeaderApiVersionReader("api-version"));
}
When you do HeaderApiVersioning you won't be able to just do a GET in your browser, so I'll use Postman to add the header (or I could use Curl, or WGet, or PowerShell, or a Unit Test):
Image
please refer https://www.hanselman.com/blog/ASPNETCoreRESTfulWebAPIVersioningMadeEasy.aspx

The constraint entry 'inboundHttpMethod' must have a string value or be of a type which implements 'IHttpRouteConstraint

I am trying to implement attribute routing, but getting following error.
The constraint entry 'inboundHttpMethod' on the route with route
template 'authenticate' must have a string value or be of a type which
implements 'IHttpRouteConstraint
I already added the line of code in Global.asax,
AttributeRoutingHttpConfig.RegisterRoutes(GlobalConfiguration.Configuration.Routes);
and the below code in AttributeRoutingHttpConfig.cs,
routes.MapHttpAttributeRoutes(cfig =>
{
cfig.UseLowercaseRoutes = true;
cfig.AutoGenerateRouteNames = true;
cfig.AddRoutesFromAssemblyOf<AuthenticateController>();
cfig.InMemory = true;
});
Anyone know about the issue, actually I am new to c#.
You are using the wrong packages.
From what I can see you are trying to implement attribute routing using this NuGet package (old and no longer supported).
This package supports Web API v1 (assembly version 4.*), and not Web API 2 (assembly version 5.*).
Web API 2 support for attribute routing is native. This tutorial may help you implementing such a feature: Attribute Routing in Web API 2
Here it is a small example:
[RoutePrefix("v1/myexample")]
public MyController : ApiController {
[Route("foo")]
public string GetFoo()
{
return "foo";
}
}
This action may be reached at the following endpoint: http://myhost/v1/myexample/foo.
Remember to register them in your WebApiConfig.cs file:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Other Web API configuration
}
}

Categories