I have a requirement to add an HTTP REST api to an application that will need to run on .net framework 4.7.2 as well as .net core. The core logic is targeting .net standard 2.0 and uses autofac for dependency injection. I’m planning to use OWIN for the framework dependent build and aspnet webApi for the core build.
Ideally I would like to create common API controllers used by both applications. I can certainly inject services into the different controller types but that would get hard to maintain long term and tough to catch mismatched routes etc. I could probably also just use OWIN everywhere and not leverage aspnet core but don’t want to, the framework build will probably have a 2yr requirement before I no longer need to maintain it and would prefer aspnet webApi long term.
Is there a way to add ApiControllers to an aspnet core application or abstract between the two?
Currently I’ve only been able to inject common services with APIs into the two different controller types to accomplish my required results.
Example Injectable Service with Api:
Main library containing services target .Net Standard 2.0
public interface IDebuggingLevelUtility
{
LogEventLevel Level { get; }
void ChangeDebugLevel(LogEventLevel loggingLevel);
}
Example of an Api ControllerBase hosted in a .net Core WebApi Application
[ApiController]
[Route("api/debugging")]
public class DebuggingController : ControllerBase
{
private readonly ILogger<DebuggingController> _logger;
private readonly IDebuggingLevelUtility _debugging;
public DebuggingController(ILogger<DebuggingController> logger, IDebuggingLevelUtility debugging)
{
_logger = logger;
_debugging = debugging;
}
[HttpGet]
public ActionResult<string> GetLoggerLevel()
{
try
{
return _debugging.Level.ToString();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to change logging Level");
return NotFound();
}
}
[HttpPost("{level}")]
[ProducesResponseType(typeof(bool), 200)]
[ProducesResponseType(typeof(BadRequestResult), 500)]
public ActionResult<string> PostLoggerLevel(LogEventLevel level)
{
try
{
_logger.LogWarning("Changing debugging level from {current} => {new}", LoggingLevel.Level.MinimumLevel, level);
_debugging.ChangeDebugLevel(level);
return _debugging.Level.ToString();
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to change logging Level");
return BadRequest();
}
}
}
Example ApiController hosted in a .Net Framework 4.7.2 application using OWIN self hosted
[RoutePrefix("api/debugging")]
public class DebuggingController : ApiController
{
private readonly ILogger _logger;
private readonly IDebuggingLevelUtility _debuggingLevelUtility;
public DebuggingController(ILogger logger, IDebuggingLevelUtility debuggingLevelUtility)
{
_logger = logger;
_debuggingLevelUtility = debuggingLevelUtility;
}
[HttpGet]
[Route("")]
public void GetLoggerLevel()
{
//DONT WANT TO CREATE CONTROLLERS TWICE
}...
}
Ideally I would abstract the two controllers and create my custom Api Route Handlers centrally located. I believe these use source generators so I'm not entirely sure where to be begin. Ideally I have "MySuperCoolApiController" and inject the required services. This ApiController can be mapped to both the OWIN self hosted api and the AspNet core WebApi. Hope this provide some better clarity.
Related
In my Asp.net Core 5 API Project
I have a serviceLayer that the controller uses, to get data from a third layer called dataLayer.
I want to use the service layer as a DLL in different projects.
This ServiceLayer Contain dependency Injections like that :
namespace ServiceLayer
{
public class UserService : IUserService
{
IUserRepository userRepository; // (From DataLayer)
public UserService(IUserRepository repository) : base(repository)
{
this.userRepository = repository;
}
public Users GetAllPersonsById(int id)
{
return userRepository.GetById(id);
}
}
public interface IUserService : IService<Users>
{
Users GetAllPersonsById(int id);
}
How can I use the method GetAllPersonsById with the DLL ServiceLayer
can I use it because the dependency Injections
As soon as you reference the DLL / project you can use all classes the same ways as if they were in the project.
To use a class as a service:
Provide the service
Inject the service
There's a lot of documentation available, so I'll keep this short:
// provide in startup.cs
services.AddTransient<IUserService, UserService>();
// Inject where you need it
MyConstructor(IUserService userService) {}
See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0
Provide Extension Method
If we take a look at other libs, most of them provide a method to setup the services.
Example: Entity framework core
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(...));
}
So you could:
In your lib, create an extension method for IServicesCollection that adds all services of your lib.
In the consuming project, call services.AddMyLibServices().
This could look like so:
public static class ServicesConfiguration
{
public static void AddDataLayer(this IServiceCollection services)
{
services.AddTransient<IUserService, UserService>();
// ... same for all services of your lib
}
}
Here's a tutorial with more details:
https://dotnetcoretutorials.com/2017/01/24/servicecollection-extension-pattern/
Lamar service registries
An optional and alternative approach are service registries. It's very similar to the extension methods but uses a class to do the setup. See https://jasperfx.github.io/lamar/documentation/ioc/registration/registry-dsl/
Composition Root
You may want to read about the composition root pattern, e.g. What is a composition root in the context of dependency injection?
In a simple app, your startup.cs is your composition root. In more complex apps, you could create a separate project to have a single place to configure your apps services.
Create the DLL
There are two ways to create the DLL:
As a project in your solution (so your solution has multiple projects, each will result in a separate DLL)
As a separate solution and as nuget package
I`m using .NET Core 3.1 and I for every request I need to access some of the logged User claims and pass it to business service (another class library) outside the API project.
I`ve tried to create UserService:
public class UsersService
{
private readonly IHttpContextAccessor _context;
public UsersService(IHttpContextAccessor context)
{
_context = context;
}
public UserSession GetUser()
{
return new UserSession()
{
UserDisplayName = _context.HttpContext.User?.Identity?.Name,
};
}
}
and in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
which should be used/injected by other business services or repositories like that:
public class ExampleBusinessService
{
private readonly IUnitOfWork _unitOfWork;
private IUsersService _userService;
public ExampleBusinessService(IUnitOfWork unitOfWork, IUsersService userService)
{
this._unitOfWork = unitOfWork;
this._userService = userService;
}
However it seems I cannot access IHttpContextAccessor outside of the Web project.
Can this be achieved?
Ofcourse I cannot create the UserService in the API and then use it in other libraries cause that mens the libraries should have reference to the web project.
If not is it possible in controller for the startup class to take the needed info from current user and pass it to the services? Or do so in the controller somehow?
As is say i need custom class with information taken from the logged user in business services (seprate class library) and repositories (seprate class library)
this is how the solution looks like i need to access the logged user info (custom UserSession class) in BusinessServices library:
Thanks
The IHttpContextAccessor type is part of the ASP.NET Core framework and as such only available in your web project. If you have your UsersService implementation in a different project, then you won’t be able to reference the context accessor correctly.
A good way to solve this is to split the contract from the implementation: Define a IUsersService within your library project and only reference that for your business logic. Users of the service do not need to know what things an implementation might depend on; they only care that they can use the public interface of this service.
Then in your web project, create an implementation of IUsersService that uses the IHttpContextAccessor to access the user information. Since the implementation lives within the web project, it can properly access the context accessor.
Finally, combine the interface and the implementation by registering your service implementation as part of your web application:
services.AddTransient<IUsersService, UsersService>();
Once your services (outside of the web project) now require the IUsersService, they will get the implementation that uses the context accessor to access the user information and everything should work without you having to add a reference to the ASP.NET Core framework in your (otherwise unrelated) libraries.
Okay, I understand your concern and it seems you're approaching the problem in the wrong way.
To point it out, I believe this is what you want:
You have ExampleBusinessService, which resides in a shared class library
You have UsersService, which deals with services of user sessions etc.
You want to access session info through IHttpContextAccessor
First of all, both ExampleBusinessService and UsersService don't need to be in API project or tied to it whatsoever. You can freely put them in your class library.
Here is how you can approach this:
Firstly, in your class library add Microsoft.AspNetCore.Http package.
Create static class UsersService (in the class library)
Add the following code (to UsersService)
public static class UsersService
{
// extend IHttpContextAccessor
public static UserSession GetUserSession( this IHttpContextAccessor accessor)
{
return new UserSession
{
UserDisplayName = _context.HttpContext.User?.Identity?.Name,
}
}
}
Now, in your ExampleBusinessService inject IHttpContextAccessor
public class ExampleBusinessService
{
private readonly IHttpContextAccessor _contextAccessor;
public ExampleBusinessService(IHttpContextAccessor accessor)
{
_contextAccessor = accessor,
}
// now do whatever you want with the session info
public SomeBusinessAboutSessionInfo()
{
// retrieve the session
var userSession = _contextAccessor.GetUserSession();
//...your logic
}
}
The final piece (this is important)
Add IHttpContextAccessor to your IServiceCollection in the API project
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
Everything should now work out!!!
In case of adding full dotnetcore to a shared solution (external class library project). Just add a reference to Microsoft.AspNetCore.HTTP to get access to IHttpContextAccessor
I have an existing REST API running on ASP.NET Core 3.0. It uses MVC filter to perform an authorization check based on a header value and returns error in case of authorization failure so that the request is not passed to the controller.
Now, I am experimenting with gRPC and trying to port this API to a gRPC service. However, I do not see any obvious solutions that might act as an MVC filter replacement.
Is there some way to achieve similar authorization checking functionality, perhaps using metadata?
For MVC and gRpc, they are different. ActionFilter is not exist under gRpc.
If you want to apply checking request header for all actions, you could try implement your custom middleware before app.UseEndpoints and check the request header.
For another way, you could try Policy like below:
GrpcRequireemnt and GrpcHandler
public class GrpcRequireemnt : IAuthorizationRequirement
{
}
public class GrpcHandler : AuthorizationHandler<GrpcRequireemnt>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public GrpcHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GrpcRequireemnt requirement)
{
var headers = _httpContextAccessor.HttpContext.Request.Headers;
StringValues token;
if (!headers.TryGetValue("token", out token))
{
context.Fail();
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
Register required services
services.AddAuthorization(options =>
{
options.AddPolicy("TokenAuthorize", policy =>
{
policy.AddRequirements(new GrpcRequireemnt());
});
});
services.AddHttpContextAccessor();
services.AddSingleton<IAuthorizationHandler, GrpcHandler>();
UseCase
[Authorize("TokenAuthorize")]
public override Task<BuyTicketsResponse> BuyTickets(BuyTicketsRequest request, ServerCallContext context)
{
var user = context.GetHttpContext().User;
return Task.FromResult(new BuyTicketsResponse
{
Success = _ticketRepository.BuyTickets(user.Identity.Name!, request.Count)
});
}
This will have slightly different answers depending on if you're using Grpc.Core, which is a wrapper around the C GRPC library initially developed at Google, which has been available for a while and supports a variety of .Net targets (including Framework), or if you're using the new Grpc.AspNetCore which launched with .Net Core 3.0 and is built on Kestrel and ASP.NET Core internals.
Grpc.Core
For Grpc.Core you would want to pass your header value as metadata, and then create a server-side Interceptor to handle the metadata and the request. You can also consider using the AsyncAuthInterceptor, however the core Grpc implementation on the client side will not send credentials over insecure (non-TLS) connections.
Grpc.AspNetCore
Grpc.AspNetCore is built on ASP.NET and can use ASP.NET middleware, including the default ASP.NET authentication. If you can convert your filter into a middleware, you would be able to share the authentication between both implementations.
i have a simple controller where i am using the interfaec like this ,
public class HomeController : Controller
{
// GET: Home
private IHotelService hotelService;
public HomeController(IHotelService _hotelService)
{
hotelService = _hotelService;
}
}
its working fine, but when i use same thing with API controller like
public class RoomController : BaseApiController
{
private IHotelService hotelService;
public RoomController(IHotelService _hotelService)
{
hotelService = _hotelService;
}
it gives me error
As pointed out here (and in several other answers on SO), you have most likely not registered your DI container with Web API. Web API is a separate framework than MVC and therefore it has a separate configuration, including dependency injection.
So, you need to set
GlobalConfiguration.Configuration.DependencyResolver = MyDependencyResovler(container);
at application startup. The details of how to do this depend on what container you are actually using and whether you use a stock dependency resolver or roll your own as shown in Dependency Injection in ASP.NET Web API 2.
Adding Structuremap MVC 5 to an ASP.NET MVC project. I would like to have a singleton of my database connection per request - my controllers would share the same database connection. I am implementing the repository pattern here and need each controller to have a copy of its respective repository. I know this is possible but I think I'm missing or mis-interpretting something wrong.
I have a controller, "Bag," that needs a "IBagRepo"
public class BagController : Controller
{
private readonly IBagRepo repo;
public BagController(IBagRepo repo)
{
this.repo = repo;
}
// actions
}
My first attempt was hooking the singleton database connection in the ControllerConvention, as I assume its called once
public class ControllerConvention : IRegistrationConvention {
public void Process(Type type, Registry registry) {
if (type.CanBeCastTo<Controller>() && !type.IsAbstract) {
// Tried something like
registry.For(type).Singleton().Is(new ApplicationDbContext()); // this
registry.For(type).LifecycleIs(new UniquePerRequestLifecycle());
}
}
}
But it came clear that this isn't the right file to make this change. I went into the registry class that was automatically generated upon installing the nuget package and tried fiddling around with this.
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
// httpContext is null if I use the line below
// For<IBagRepo>().Use<BagRepo>().Ctor<ApplicationDbContext>().Is(new ApplicationDbContext());
}
#endregion
}
I haven't seen a problem like this out here yet. Am I passing in the right types within my DefaultRegistry class?
What you're wanting is effectively the default behavior if you had been using the StructureMap.MVC5 nuget: https://www.nuget.org/packages/StructureMap.MVC5/. As long as your DbContext is registered with the default lifecycle, that package is using a nested container per http request which effectively scopes a DbContext to an HTTP request for unit of work scoping.
Different tooling than MVC & EF, but I described similar mechanics for FubuMVC + RavenDb w/ StructureMap in this blog post: http://jeremydmiller.com/2014/11/03/transaction-scoping-in-fubumvc-with-ravendb-and-structuremap/
I ended overriding the default controller factory and not using structuremap