How create a middleware with api endpoints in .NET Core - api

I have created the web application with the web api. The application contains some Controllers for example TodoController:
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
}
[HttpGet]
public IEnumerable<TodoItem> GetAll()
{
return _context.TodoItems.ToList();
}
}
}
If I create the GET request - /api/todo - I get the list of Todos from database.
I have a list of controllers and api endpoints like above.
I would like distribute this api to another application ideally like middleware - my idea is register in Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddTodoApi();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseTodoApi();
}
This will be awesome use case for my api but I don't know how this controllers api endpoints rewrite like middleware and return same JSON data same approache like using classic Controllers.
How can I write the middleware in .NET Core for creating API endpoints?

Instead of the separate middleware, you may configure the MVC middleware to discovery controllers from another assembly:
// using System.Reflection;
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddMvc()
.AddApplicationPart(typeof(TodoController).GetTypeInfo().Assembly);
Controllers are part of MVC middleware, they are not a separate part of request pipeline (but this is what middlewares are). When you register the custom middleware, it by default invokes on each request and you have HttpContext context as an input parameter to work with/edit
Request/Response data. But ASP.NET Core provides Map* extensions that are used as a convention for branching the pipeline.
Map branches the request pipeline based on matches of the given request path. If the request path starts with the given path, the branch is executed.
Example:
private static void HandleMapTodo(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("/api/todo was handled");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/api/todo", HandleMapTodo);
}
Note, that as middleware knows nothing about MVC middleware, you have only access to "raw" request and do not have features like model binding or MVC action filters.

Because it looks like the perfect microservices approach (similar than what my team is doing right now) I'd create a client assembly that can consume your API, the one that contains your TodoController, if you define a contract, and interface, for that API you can register it in your other assembly as it was a midleware and also you could mock that behaviour in your unit tests.
So, as I said, you could inject your client in ConfigureServices method, you can create:
public static IServiceCollection AddTodoRestClient(this IServiceCollection services)
{
services.AddSingleton<ITodoRestClient, TodoRestClient>();
return services;
}
Also consider that you will need to provide the enpoint so, it might looks like:
public static IServiceCollection AddConfiguredTodoClient(this IServiceCollection services, string todoEndpoint)
{
AddTodoClient(services);
ITodoRestClient todoRestClient = services.BuildServiceProvider().GetService<ITodoRestClient>();
// Imagine you have a configure method...
todoRestClient.Configure(services, todoEndpoint);
return services;
}
You can create those methods in a TodoRestClientInjector class and use them in Configure method on your startup.
I hope it helps
--- MORE DETAILS TO ANSWER COMMENTS ---
For me TodoClient is a Rest client library that implements calls to the ToDo API, (I've edited previous code to be TodoRestClient) methos like, i.e., CreateTodoItem(TodoDto todoItem) which implementation would call to the TodoController.Post([FromBody] item) or GetTodos() which wuold call TodoController.Get() and so on and so forth....
Regarding the enpoints... This approach implies to have (at least) two different applications (.NET Core apps), on the one hand the ASP NET Core app that has your TodoController and on the other hand a console application or another ASP NET Core API on which startup class you'll do the inyection adn the Rest client (the Todo Rest client) configuration ...
In a microservices approach using docker, in a dev environment, you'll use docker-compose-yml, but in a traditional approach you'll use concrete ports to define the endpoints...
So, imagine that you have in the second service a controller that need to use TodoController, to achieve so I'll use the above aproach and the "SecondController" would look like:
public class SecondController : Controller
{
private readonly SecondContext _context;
private readonly TodoRestClient _todoRestClient;
public TodoController(SecondContext context, ITodoRestClient todoRestClient)
{
_context = context;
_todoRestClient= todoRestClient;
}
// Whatever logic in this second controller... but the usage would be like:
_todoRestClient.GetTodos()
}
Just few final hints: it's key to minimize calls between services because it increases latency, and more and more if this happens on cascade. Also consider Docker usage, looks challenging but it is quite easy to start and, indeed, is thought to be used in scenarios that the one you presented and solutions like mine.
Again, I hope it helps.
Juan

Related

How to add global metadata to ASP.NET Core logging?

I'd like to add my app's build number to all logs in an ASP.NET Core 3.1 app that is using Application Insights for log storage. Is this possible without having to use BeginScope and EndScope everywhere? I assumed it would be part of the ConfigureLogging startup hook, but didn't see anything. I've done this in the past with Serilog's enrichers, but am not using that library currently.
You can achieve that with TelemetryInitializer. (https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-filtering-sampling#addmodify-properties-itelemetryinitializer)
public class BuildNumberTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
(telemetry as ISupportProperties).Properties.Add("BuildNumber", "ValueForBuildNumber");
}
You need to add this initializer to the config, which is done like below if you are on Asp.Net Core applications.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITelemetryInitializer, BuildNumberTelemetryInitializer >();
}

.NET Core DI passing info of AddHttpContextAccessor to a service

In my solution I have projects for my API, my Web App and also have another project which includes services, that are getting some information from a database and formatting them, these are currently only used by this API, but these could be used by other API projects in the future.
My API have a couple controllers that are returning JSON data from the result returned by the services.
In some cases the services needs to call the API to process some information before calling the request to the database. Since I have dev/staging/prod environment with their own URL I don't want to hardcode the URLs in the services I want to use DI to get these dynamicaly depending on the context.
In the Startup.cs of my API I have added services.AddHttpContextAccessor(); in the ConfigureServices(IServiceCollection services) section to gain access to the current http context :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
With that I know I can now access the information directly into my controller which I tried and it worked :
public class DataController : ControllerBase
{
...
private readonly string _baseUrl;
public FeaturesController(...
,IHttpContextAccessor httpContextAccessor)
{
...
_baseUrl = UrlHelpers.ShowBaseURL(httpContextAccessor) ?? throw new ArgumentNullException(nameof(_baseUrl));
}
}
public static class UrlHelpers
{
public static string ShowBaseURL(IHttpContextAccessor httpcontextaccessor)
{
var request = httpcontextaccessor.HttpContext.Request;
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent());
return absoluteUri;
}
}
I could do just about the same thing in the services but to me they should not act directly on the httpcontext, since this is not the job they are meant to do. I am sure I could do better by adding a class injected of some sort that would have then make the specific value available to my services.
I know I could also pass the _baseUrl directly as an argument when calling the services from my controller but since I am trying to better understand DI and use it I would rather find another way if it is viable.
I can't give credit but I went with Steven solution which make the most sens

Resolving dependencies in Integration test in ASP.NET Core

I have ASP.NET Core API. I have already gone through documentation here that shows how to do integration testing in asp.net core. The example sets up a test server and then invoke controller method.
However I want to test a particular class method directly (not a controller method)? For example:
public class MyService : IMyService
{
private readonly DbContext _dbContext;
public MyService(DbContext dbContext)
{
_dbContext = dbContext;
}
public void DoSomething()
{
//do something here
}
}
When the test starts I want startup.cs to be called so all the dependencies will get register. (like dbcontext) but I am not sure in integration test how do I resolve IMyService?
Note: The reason I want to test DoSomething() method directly because this method will not get invoked by any controller. I am using Hangfire inside this API for background processing. The Hangfire's background processing job will call DoSomething() method. So for integration test I want to avoid using Hangfire and just directly call DoSomething() method
You already have a TestServer when you run integration tests, from here you can easily access the application wide container. You can't access the RequestServices for obvious reason (it's only available in HttpContext, which is created once per request).
var testServer = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.UseEnvironment("DevelopmentOrTestingOrWhateverElse"));
var myService = testServer.Host.Services.GetRequiredService<IMyService>();

ASP.NET Core: Can not resolve a service instance through CallContextServiceLocator.Locator.ServiceProvider

This is part of my ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
...
//bus
services.AddSingleton<IRouteMessages, MessageRouter>();
services.AddSingleton<IBus, DirectBus>();
////
...
}
I'm trying to resolve the instance of IRouteMessages interface in my RegisterCommandHandlersInMessageRouter class:
public class RegisterCommandHandlersInMessageRouter
{
...
public static void BootStrap()
{
var router = CallContextServiceLocator.Locator.ServiceProvider.GetService(typeof (IRouteMessages));
new RegisterCommandHandlersInMessageRouter().RegisterRoutes(router as MessageRouter);
}
...
}
router variable is always null. Yet in my controllers where IRouterMessages is resolved automatically (in constructors) everything is fine.
I'm not sure what other parts of my code could be useful. I will provide more details.
Don't EVER use CallContextServiceLocator, this completely beats the purpose of having dependency injection. And NEVER relay on it.
CallContextServiceLocator is only used in some of the internal ASP.NET Core and is never be supposed to be used by developers creating ASP.NET Core applications. That being said, it can be removed, made internal or inaccessible at any time which would break existing applications.
Additionally, the CallContextServiceLocator only had runtime services registered (DNX Services, deprecated anyways). Source: David Fowl from ASP.NET Core team.
Infact CallContextServiceLocator is being removed in RC2, see the announcement.
Removed support for CallContextServiceLocator. Use PlatformServices and CompilationServices instead.
Instead, only use the built-in dependency injection, like this:
public static class RegisterCommandHandlersInMessageRouter
{
...
// This is extension method now
public static void RegisterCommandHandlers(this IServiceProvider services)
{
var router = services.GetService(typeof (IRouteMessages));
new RegisterCommandHandlersInMessageRouter().RegisterRoutes(router as MessageRouter);
}
...
}
and call it in your Startup.cs
public void Configure(IServiceProvider services)
{
...
services.RegisterCommandHandlers();
...
}

How to add global ASP.Net Web Api Filters?

I've created a Web Api filter (using System.Web.Http.Filters.ActionFilterAttribute) but I am unable to get it to work inside of ASP.Net MVC 4. I tried adding it to the RegisterGlobalFilters() method but that didn't work.
So if one is using Web Api hosted in ASP.Net MVC how does one register filters?
The following code, in my Global.asax, works for me:
public static void RegisterWebApiFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new MyWebApiFilter());
}
protected void Application_Start()
{
RegisterWebApiFilters(GlobalConfiguration.Configuration.Filters);
}
note that this answer holds true up to MVC 5/Web API 2
Short answer: MVC and Web API filters are not cross compatible, and if you want to register them globally, you must use the appropriate configuration classes for each.
Long answer: ASP.NET MVC and Web API are purposely designed to work in a similar way, but they are in fact different creatures.
Web API lives under the System.Web.Http namespace, whereas MVC lives under the System.Web.Mvc namespace. The two will happily live side by side, but one does not contain the other and despite the similarities in the programming model, the underlying implementations are different. Just as MVC controllers and Web API controllers inherit different base controller classes (MVC's is simply named Controller and Web API's is named ApiController) MVC filters and Web API filters inherit from different FilterAttribute classes (both share the same name in this case, but are separate classes which live in their respective namespaces).
Web API global filters are registered through the HttpConfiguration object available to you in the Register method WebApiConfig.cs if you're using a project template with WebActivator:
public static void Register(HttpConfiguration config)
{
//stuff before
config.Filters.Add(new MyWebApiFilter());
//stuff after
}
or otherwise in the global.asax.cs:
GlobalConfiguration.Configuration.Filters.Add(new MyWebApiFilter());
Mvc global filters are registered by way of a GlobalFilterCollection object, which is available to you through the RegisterGlobalFilters method of FilterConfig.cs for projects that are using WebActivator:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//stuff before
filters.Add(new MyMvcFilter());
//stuff after
}
}
or in the global.asax.cs file by way of GlobalFilters.Filters collection for those without WebActivator:
GlobalFilters.Filters.Add(new MyMvcFilter());
It's worth noting that in both cases you do not need to inherit from the appropriate FilterAttribute type. Web API Filters need only implement the System.Web.Http.IFilter interface, while MVC filter registration checks to ensure that your class inherits one of a handful of filter interfaces defined in the System.Web.Mvc namespace.
As of MVC 4 RC, the correct class name is HttpFilterCollection:
public static void RegisterWebApiFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new MyWebApiFilter());
}
protected void Application_Start()
{
RegisterWebApiFilters(GlobalConfiguration.Configuration.Filters);
}
Instead of using global filters I prefer to do this :
[MyWebApiFilter]
public class CustomizedApiControllerBase : ApiController
{
...
}
And after that inherit all of api controllers from CustomizedApiControllerBase
This approach is more expressive in comparison with global filters in global.ascx file.