ASP.NET core - middleware on MVC - asp.net-web-api

I try to create an asp.net core web api on macOS.
But my middleware isn't called on mvc-call.
My Config:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, BackendDbContext context)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//app.UseStaticFiles();
app.UseMvc();
app.UseMiddleware<AuthMiddleware>();
BackendDbInitializer.Init(context);
}
And my Middleware:
public class AuthMiddleware
{
private readonly RequestDelegate _next;
public AuthMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
Console.WriteLine("Invoke......................");
await _next.Invoke(context);
}
}
When i do a HTTP-request, that doesn't match a controller request. The middleware class is called.
How can i set up that the middleware class is only called on a mvc-request.

You can use middleware as MVC filters:
public class HomeController : Controller
{
[MiddlewareFilter(typeof(AuthMiddleware))]
public IActionResult Index()
{
return View();
}
}
In this case, AuthMiddleware will run each time the action method Index is called.
PS: You need to install this package (Microsoft.AspNetCore.Mvc.Core)
more info (see Middleware as MVC filters section)

Related

How to enable compression in aspnetboilerplate dynamic web api

I tried adding Microsoft.AspNetCore.ResponseCompression in Myproject.web.host
And configured this
public void ConfigureServices(IServiceCollection services)
{
//other configs...
services.AddResponseCompression();
//other configs...
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//other configs...
app.UseResponseCompression();
app.UseMvc();
}
it worked for swagger, but not for the generated dynamic web api
Try to use a tool such as F12 developer tools, Fiddler or Postman to check the Accept-Encoding setting in the request header and the response headers. Perhaps the Content-Encoding and Vary headers aren't present on the response.
To solve this issue, you could try to refer the following steps to set the compression provider:
create a BrotliCompressionProvider class:
public class BrotliCompressionProvider : ICompressionProvider
{
public string EncodingName => "br";
public bool SupportsFlush => true;
public Stream CreateStream(Stream outputStream) => new BrotliStream(outputStream, CompressionMode.Compress);
}
Change the configure service as below:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddResponseCompression(options => {
options.Providers.Add<BrotliCompressionProvider>();
options.EnableForHttps = true;
});
services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//other configs...
app.UseResponseCompression();
app.UseMvc();
}

Configure Autofac DI container in ASP.NET CORE 3.1 Web API and consumer service from controller

I need to configure Autofac DI container in ASP.NET CORE 3.1 Web API application and call register class from the container in Web API controller. I install Autofac.Extensions.DependencyInjection (6.0.0) and try to register container in my Startup.cs class but I am not able to use service. Also, do I need to configure the container in ConfigureServices(IServiceCollection services) class? The debugger does not hit IoCConfigurator() class after hitting point builder.RegisterModule(new IoCConfigurator());
Program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public ContainerBuilder containerBuilder { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
containerBuilder = new ContainerBuilder();
}
public void ConfigureServices(IServiceCollection services)
{
ServicesConfigurator.Configure(services, Configuration);
ConfigureIoC(services, containerBuilder);
}
public void ConfigureIoC(IServiceCollection services, ContainerBuilder builder)
{
builder.RegisterModule(new IoCConfigurator());
}
IoCConfigurator.cs
public class IoCConfigurator: Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<NotifyService>().As<INotificationService>();
builder.RegisterType<UsersService>().AsSelf();
}
}
INotification Interface & Class
public interface INotificationService
{
void notifyUsernameChanged(Users users);
}
public class NotifyService : INotificationService
{
public void notifyUsernameChanged(Users users)
{
string changedUsername = users.Username;
Console.WriteLine("Username has changed to ... ");
Console.WriteLine(changedUsername);
}
}
User & User Service Class
public class Users
{
public string Username { get; set; }
public Users(string username)
{
this.Username = username;
}
}
public class UsersService
{
private INotificationService _notificationService;
public UsersService(INotificationService notificationService)
{
this._notificationService = notificationService;
}
public void ChangeUsername(Users users, string newUsername)
{
users.Username = newUsername;
_notificationService.notifyUsernameChanged(users);
}
}
API Controller where I want to class the UserService Class and get reference from DI container
[Authorize]
[Route("txn/v1/[controller]/[action]")]
[ApiController]
public class DashboardController : ControllerBase
{
[HttpPost("{name}")]
public ActionResult<HelloMessage> GetMessage(string name)
{
// call container here...
var result = new HelloMessage()
{
GivenName = name,
ReturnMessage = "Dashboard# Hello, Welcome to Texanite Digital"
};
return result;
}
Here is how I set it up. From command line:
md autof
cd autof
dotnet new webapi
dotnet add package Autofac.Extensions.DependencyInjection
Then edit using VS or VSCode. Program.cs - as you had it:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Next in Startup.cs, forget about ConfigureIoC, just register the services you want/need:
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac, like:
//builder.RegisterModule();
builder.RegisterType<NotifyService>().As<INotificationService>();
}
Then in DashboardController.cs you need to "inject" the needed services from the constructor:
public class HelloMessage {
public string GivenName { get; set; }
public string ReturnMessage { get; set; }
}
//[Authorize] Easier without Auth - don't need
[Route("[controller]")]
[ApiController]
public class DashboardController : ControllerBase
{
private readonly INotificationService _notifyService;
public DashboardController(INotificationService notifyService)
{
_notifyService = notifyService;
}
//[HttpPost("{name}")] - easier to test Get
[HttpGet("{name}")]
public ActionResult<HelloMessage> GetMessage(string name)
{
// call container here...
_notifyService.notifyUsernameChanged(new Users(name));
var result = new HelloMessage()
{
GivenName = name,
ReturnMessage = $"Dashboard {name}, Welcome to Texanite Digital"
};
return result;
}
}
My Results:
With console output:
Your UserService was a little "out of the loop" but you can add an Interface for it and register with container and add it to injected services of the controller(s).
I could zip the whole thing up, just don't know where to put it or send it...
Change your code like, that is all I think
Program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new IoCConfigurator());
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public ContainerBuilder containerBuilder { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
containerBuilder = new ContainerBuilder();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}

web api combining attribute routing with convention-based routing does not work

Based on documentaion
Attribute routing can be combined with convention-based routing. To
define convention-based routes, call the MapHttpRoute method.
I want every route to start with api. For example i want the route to be http://localhost:40000/api/client/{id}/batch/approve so here is my WebApiConfig class
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Then in controller i have
public class BatchController : ApiController
{
private readonly MyService _service;
public BatchController(MyService service)
{
_service = service;
}
[HttpPost]
[Route("client/{id}/batch/approve")]
public async Task<IHttpActionResult> Approve(int id, RequestDTO request)
{
return Ok(await _service.Approve(clientID,request).ConfigureAwait(false));
}
When client invoke this route, it receives 404 Not Found response.
However if i prefix api in Route attribute like below then it worked
[Route("api/client/{id}/batch/approve")]
Why convention based routing not prefixing api to route, why i also need to explicitly add api prefix to Route attribute
You must set RoutePrefixAttribute for the controller:
[RoutePrefix("api")]
public class BatchController : ApiController
{
[HttpPost]
[Route("client/{id}/batch/approve")]
public async Task<IHttpActionResult> Approve(int id, RequestDTO request)
{
...
}
}
https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Vaadin Dashboard demo using Spring Boot

I'm trying to put together a simple application using Vaadin + Spring Boot with just two views: login and dashboard. I'm using pieces of code from the Vaadin Dashboard demo. There is this method in the DashboardUI class:
private void updateContent() {
User user = (User) VaadinSession.getCurrent().getAttribute(
User.class.getName());
if (user != null && "admin".equals(user.getRole())) {
// Authenticated user
setContent(new MainView());
removeStyleName("loginview");
getNavigator().navigateTo(getNavigator().getState());
} else {
setContent(new LoginView());
addStyleName("loginview");
}
}
As you see the views are instantiated via the new keyword. Since I'm using Spring I would like the container take care of that. But I cannot figure out how to have the views injected by Spring.
Annotate your view with #VaadinUIScope and #VaadinComponent. And then you can use #Autowired to inject your view.
You should probably setup two UI classes i.e LoginUI.class and MainUI.class. Have Spring Security redirect unauthorized to /login and authorized to /
LoginUI.class
#Theme("valo")
#SpringUI(path = "login")
#PreserveOnRefresh
public class LoginUI extends UI {
private SpringViewProvider viewProvider;
#Autowired
public LoginUI(SpringViewProvider viewProvider) {
this.viewProvider = viewProvider;
}
#Override
protected void init(VaadinRequest request) {
Navigator navigator = new Navigator(this, this);
navigator.addProvider(viewProvider);
navigator.navigateTo(LoginView.VIEW_NAME);
}
}
MainAppUI.class
#Theme("valo")
#SpringUI
#PreserveOnRefresh
public class MainAppUI extends UI {
private SpringViewProvider viewProvider;
#Autowired
public AppUI(SpringViewProvider viewProvider) {
this.viewProvider = viewProvider;
}
#Override
protected void init(VaadinRequest request) {
getPage().setTitle("Main App");
setSizeFull();
Navigator navigator = new Navigator(this, viewDisplay);
navigator.addProvider(viewProvider);
setContent();//viewport
navigator.navigateTo(DashboardView.VIEW_NAME);
}
}
Then just use #SpringView(name = "moduleOne", ui = MainAppUI.class) on your module views for your app as suggested by Morfic to have the main navigator and module views registered only when a user has logged in to save on resources.
Note: Taking a look at that example it seems that they're not actually using views & navigator, they're somewhat faking them. If you want to proceed on the same path you can simply autowire the fake views in the UI instead of using the navigator as below.
The Vaadin-spring boot wiki offers an introduction to this by the use of:
1) #SpringUI with autowired SpringViewProvider
#Theme("valo")
#SpringUI
public class MyVaadinUI extends UI {
// we can use either constructor autowiring or field autowiring
#Autowired
private SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest request) {
// other init stuff
Navigator navigator = new Navigator(this, viewContainer);
navigator.addProvider(viewProvider);
}
}
2) #SpringView
#SpringView(name = DefaultView.VIEW_NAME)
public class DefaultView extends VerticalLayout implements View {
public static final String VIEW_NAME = "";
#PostConstruct
void init() {
addComponent(new Label("This is the default view"));
}
#Override
public void enter(ViewChangeEvent event) {
// the view is constructed in the init() method()
}
}
For the decision whether the user should be redirected to the login view, or the other ones, I usually use a ViewChangeListener, something along the lines of:
navigator.addViewChangeListener(new ViewChangeListener() {
#Override
public boolean beforeViewChange(ViewChangeEvent event) {
if (VaadinSession.getCurrent().getAttribute("user") == null) {
navigator.getDisplay().showView(loginView);
return false;
} else {
return true;
}
}
#Override
public void afterViewChange(ViewChangeEvent event) {
// meh
}
});

HTTP Error 403.14 in ASP.NET5 MVC6

I am just exploring ASP.NET 5 MVC 6 web app with new Visual Studio Community 2015 RC. DotNet framework 4.6.
I've added reference Microsoft.AspNet.MVC (6.0.0-beta4) from nuget. Then created Models,Views & Controllers directory. Also added HomeController and a view.
Here is my Startup.cs-
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
Home Conctoller-
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
But while i try to run the project, browser shows
HTTP Error 403.14
A default document is not configured for the requested URL.
Do I need to do anything to configure?
Try-
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
});
}
}

Resources