If I have an asp.net core razor page, with no controller, how do I hook the dependency injection as suggested in
#1152
and
https://learn.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core#how-can-i-track-telemetry-thats-not-automatically-collected
The code as follows runs but the events are never written when I use the dependency injections methods for .net core. Other regular app insights events process fine.
namespace simplewebapp.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger _logger;
private TelemetryClient telemetry;
public IndexModel(ILogger logger, TelemetryClient telemetry)
{
_logger = logger;
this.telemetry = telemetry;
telemetry.TrackEvent("IndexPage_IndexModel");
}
Related
I am upgrading a CAS 4 to a CAS 6. I have done several Spring Boot 2 apps, so I know what I am doing there. I can even do some webflow, but only from scratch.
The documentation clearly states not to mess with the base webflow xml, and to "inject" your own services.
How does one "inject" a service? I really just need to add a message of the day to the login page.
Does anyone have an example of something this simple?
Find below my approach, tested on a cas-maven-overlay installation with cas version at 5.3.x. Some things maybe different on cas 6 branch but I assume the main idea remains.
First, we should create an Action class that will be injected in the login flow and will add the desired message in the flow scope in order to be available at the template(view).
public class DailyMessageAction extends AbstractAction{
#Override
protected Event doExecute(RequestContext context) throws Exception {
context.getFlowScope().asMap().put("dailyMessage", "YOUR_AWESOME_MESSAGE");
return success();
}
}
Then create a WebflowConfigurer class and inject our newly created DailyMessageAction in the actions list(see doInitialize method).
public class DailyMessageWebflowConfigurer extends AbstractCasWebflowConfigurer{
final Action dailyMessageAction;
public DailyMessageWebflowConfigurer(FlowBuilderServices flowBuilderServices,
FlowDefinitionRegistry flowDefinitionRegistry,
ApplicationContext applicationContext,
CasConfigurationProperties casProperties,Action dailyMessageAction){
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
this.dailyMessageAction = dailyMessageAction;
}
#Override
protected void doInitialize() {
final Flow flow = super.getLoginFlow();
flow.getStartActionList().add(dailyMessageAction);
}
}
After that we should inject DailyMessageWebflowConfigurer in cas runtime. This is achieved by creating a configuration class and inject our configurer.
#Configuration
public class CustomWebflowConfiguration {
#Autowired
private CasConfigurationProperties casProperties;
#Autowired
#Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowDefinitionRegistry;
#Autowired
private ApplicationContext applicationContext;
#Autowired
private FlowBuilderServices flowBuilderServices;
#RefreshScope
#ConditionalOnMissingBean(name = "dailyMessageAction")
#Bean
public Action dailyMessageAction(){
return new DailyMessageAction();
}
#ConditionalOnMissingBean(name = "dailyMessageWebflowConfigurer")
#Bean
#RefreshScope
public CasWebflowConfigurer dailyMessageWebflowConfigurer(){
final DailyMessageWebflowConfigurer w = new DailyMessageWebflowConfigurer(flowBuilderServices,
loginFlowDefinitionRegistry,
applicationContext,
casProperties,
dailyMessageAction());
w.initialize();
return w;
}
}
Include our CustomWebflowConfigurationclass in META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=your_package.CustomWebflowConfiguration
The final step is to present the added message in the view. Achieved by adding this line
<div th:utext="${dailyMessage}"></div>
in the templates/casLoginView.html file.
... add a message of the day to the login page...
Modifying the spring webflow directly is not recommended in CAS. read this for more info
So if I were you instead of tinkering with spring webflow, I would try to do something like the following:
Note:
Bare in mind this might not be the recommended way to do so, but I think this will work, and much less work than overriding spring webflow
As you said you are quite familiar with Spring boot, so I won't bored you with detail implementation, I can follow up if you / other reader are confused
If your message of the day can be hard coded, just skip 1-3 and go straight with 4.
Ok here we go:
Override the CasSupportActionsConfiguration, only adding the initialFlowSetupAction bean
Adding a custom class (let named it MyInitialFlowSetupAction) and implement the InitialFlowSetupAction
In MyInitialFlowSetupAction, add something like this:
#Override
public Event doExecute(final RequestContext context) {
Event returnEvent = super.doExecute(context);
configureMyAwesomeMessageOfTheDay(context)
return returnEvent;
}
private void configureMyAwesomeMessageOfTheDay(final RequestContext context) {
String messageOfTheDay = "Spring is the best season!";//Your logic here
context.getFlowScope().put("MESSAGE_OF_THE_DAY", messageOfTheDay);
}
4 . CAS 6 is using WAR overlay, so you can overlay the html file, including this one
https://github.com/apereo/cas/blob/v6.0.3/webapp/resources/templates/casLoginView.html
overlay that file, and add your MESSAGE_OF_THE_DAY to it
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout}">
...
<body class="login">
<main role="main" class="container mt-3 mb-3">
Message of the day is: ${MESSAGE_OF_THE_DAY}
...
</main>
</body>
</html>
See if this helps you
I have a init() method that use injected private instance<>. How can I test this class using JUnit or Mockito? I tried to make some fake class and add them to a list and set this list to my private field but I have this error
java.lang.IllegalArgumentException: Can not set javax.enterprise.inject.Instance field ......
MyClass is:
#Singleton
#Startup
public class HandlerManager {
#Any
#Inject
private Instance<RollbackHandler<RollbackData>> handlers;
private RollbackHandler<RollbackData> rollbackHandler;
#PostConstruct
public void init() {
for (RollbackHandler<RollbackData> bean : handlers) {
//do something
}
}
}
Any annotation is not processed by frameworks, unless you use custom work. you will have to define all those dependencies as mocks in your test using #Mock and call injectMocks() from before test methods such as setup(). It is a multi part problem.
Use constructor injection, field injection is evil. you still will be able to annotate your constructor with #Inject.
when(provider.iterator()).thenReturn(list.iterator);
works for me.
You can create a temporary list with concrete implementations of the RollbackHandler, and mock the iterator() method of your Instance<RollbackHandler<RollbackData>> object so that it returns the iterator of the temporary list.
Example:
private void mockIterator() {
Instance<RollbackHandler<RollbackData>> handlers = mock(Instance.class);
List<RollbackHandler<RollbackData>> handlersList = Collections.singletonList(new RollbackHandlerImpl<>());
when(handlers.iterator()).thenReturn(handlersList.iterator());
}
With Asp.Net it was easy to see the code generated by the Razor View Engine: Add a compile error and the error page will give access to the source of the Razor Page.
This changed with Asp.Net Core, which I read somewhere creates the code in memory and does not allow access to that code easily.
Question: Someone knows a trick how to access generated Razor source code with Asp.Net Core?
Add the following class to your ASP.NET Core MVC project:
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
public class CustomCompilationService : DefaultRoslynCompilationService, ICompilationService
{
public CustomCompilationService(ApplicationPartManager partManager,
IOptions<RazorViewEngineOptions> optionsAccessor,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory)
: base(partManager, optionsAccessor, fileProviderAccessor, loggerFactory)
{
}
CompilationResult ICompilationService.Compile(RelativeFileInfo fileInfo,
string compilationContent)
{
return base.Compile(fileInfo, compilationContent);
}
}
Override the ICompilationService added by MVC with the above class;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ICompilationService, CustomCompilationService>();
}
Set a break point on the Compile method of CustomCompilationService and view compilationContent.
Notes
View lookups are case sensitive. If your controller routing seeks a view named Index (Index.cshtml) but you've named your view file index (index.cshtml), you'll receive an exception:
InvalidOperationException: The view 'Index' was not found.
Artificial Stupidity provided the correct answer for ASP.NET core 1.x. For version 2.0 of the framework, one can instead use a custom razor template engine:
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
public class CustomMvcRazorTemplateEngine : MvcRazorTemplateEngine
{
public CustomMvcRazorTemplateEngine(RazorEngine engine, RazorProject project) : base(engine, project)
{ }
public override RazorCSharpDocument GenerateCode(RazorCodeDocument codeDocument)
{
RazorCSharpDocument razorCSharpDocument = base.GenerateCode(codeDocument);
// Set breakpoint here for inspecting the generated C# code in razorCSharpDocument.GeneratedCode
// The razor code can be inspected in the Autos or Locals window in codeDocument.Source._innerSourceDocument._content
return razorCSharpDocument;
}
}
Then override the RazorTemplateEngine of the framework:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<RazorTemplateEngine, CustomMvcRazorTemplateEngine>();
}
In version 2.1 of ASP.NET Core, the RazorTemplateEngine seems to be legacy, and the above mechanism does not work anymore. The changes may have to do with the move towards precompilation of razor views, but since I am not involved in the development, I can only guess at the developers' motives.
I would now recommend inspecting the precompiled views in the **.Views.dll generated at build or publish time, depending on your project settings. I personally use Telerik's JustDecompile for this purpose.
If you really need to have a programmatic solution, you can hook into the RazorProjectEngine with a custom phase:
using Microsoft.AspNetCore.Razor.Language;
namespace Econet.PAG.UI
{
internal class DebugRazorEnginePhase : IRazorEnginePhase
{
public RazorEngine Engine { get; set; }
public void Execute(RazorCodeDocument codeDocument)
{
RazorCSharpDocument razorCSharpDocument = codeDocument.GetCSharpDocument();
// Set breakpoint here for inspecting the generated C# code in razorCSharpDocument.GeneratedCode
// The razor code can be inspected in the Autos or Locals window in codeDocument.Source._innerSourceDocument._content
}
}
}
and register it in the creation of the RazorProjectEngine
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(s =>
{
var fileSystem = s.GetRequiredService<RazorProjectFileSystem>();
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder =>
{
RazorExtensions.Register(builder);
// Roslyn + TagHelpers infrastructure
var metadataReferenceFeature = s.GetRequiredService<LazyMetadataReferenceFeature>();
builder.Features.Add(metadataReferenceFeature);
builder.Features.Add(new CompilationTagHelperFeature());
// TagHelperDescriptorProviders (actually do tag helper discovery)
builder.Features.Add(new DefaultTagHelperDescriptorProvider());
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
builder.Phases.Add(new DebugRazorEnginePhase());
});
}
Note that except for the line adding the custom phase, the code inside AddSingleton is copied from Microsoft.Extensions.DependencyInjection.MvcRazorMvcCoreBuilderExtensions.AddRazorViewEngineServices(IServiceCollection services) in the Microsoft.AspNetCore.Mvc.Razor sources.
In a simple Console Application:
using Microsoft.AspNetCore.Razor.Language;
class Program
{
static void Main(string[] args)
{
var sourceDocument = RazorSourceDocument.Create("Hello world", "");
var codeDocument = RazorCodeDocument.Create(sourceDocument);
var engine = RazorEngine.Create();
engine.Process(codeDocument);
var csharpDocument = codeDocument.GetCSharpDocument();
var csharp = csharpDocument.GeneratedCode;
Console.WriteLine(csharp);
}
}
Output is:
#pragma checksum "" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "7b502c3a1f48c8609ae212cdfb639dee39673f5e"
// <auto-generated/>
#pragma warning disable 1591
namespace Razor
{
#line hidden
public class Template
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("Hello world");
}
#pragma warning restore 1998
}
}
#pragma warning restore 1591
Does Jodd framework provide mechanism to inject petitebeans references for the objects created by other frameworks.
Below are scenarios
- Domain/Service objects are created by Spring Framework
- Domain objects created are by ORM Frameworks
- These objects need to be injected with Repository/DAO object (Singleton objects registered as PetiteBean via AutomagicPetiteConfigurator)
Below is sample code, after petite container is shutdown, initMethod() is invoked when pc.getBean(Greetings.class).message(null) is invoked and destroyMethod() is not invoked, can you please point me what I am doing wrong?
#PetiteBean("greetings")
public class EnglishGreetings implements Greetings {
#Override
public String message(String message) {
if (message == null) {
return "defaultMessage";
}
return message;
}
#PetiteInitMethod
public void initMethod() {
System.out.println("Entered initMethod");
}
#PetiteDestroyMethod
public void destroyMethod() {
System.out.println("Entered destroyMethod");
}
}
public class GreetingRunner {
final static Logger logger = LoggerFactory.getLogger(GreetingRunner.class);
#PetiteInject
public Greetings greetings;
public static void main(String s[]) {
jodd.log.LoggerFactory.setLoggerFactory(new Slf4jLoggerFactory());
PetiteContainer pc = new PetiteContainer();
AutomagicPetiteConfigurator configurator = new AutomagicPetiteConfigurator();
configurator.setIncludedEntries("com.rans.*");
configurator.configure(pc);
pc.shutdown();
System.out.println(pc.getBean(Greetings.class).message(null));
}
}
Destroy method has not been invoked because of lazy aspect of Petite - if bean has not been used, no destroy method will be called. The same applies to init methods. If bean is not used, Petite simple ignores it.
Now back to the question:
Does Jodd framework provide mechanism to inject petitebeans references for the objects created by other frameworks.
Technically, yes - if you overwrite it :) See PetiteProxettaContainer. You may override getBean and use 3rd party container to fetch the bean. Actually, you may override createBeanDefinitionForRegistration method to register the bean in the different container. To be honest, we might make this more obvious :)
(Sorry for late response)
Given the following setup, I have three assemblies.
Web (ASP.NET MVC 3.0 RC1)
Models
Persistence (Fluent NHibernate, Castle.Windsor)
This is my ControllerInstaller.
using System;
using System.Web.Mvc;
using Castle;
using Castle.Windsor;
using Castle.MicroKernel;
using Castle.MicroKernel.SubSystems;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
namespace Persistence.Installers
{
public class ControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
AllTypes
.FromAssembly(System.Reflection.Assembly.GetExecutingAssembly())
.BasedOn<IController>()
.Configure(c => c.Named(
c.Implementation.Name.ToLowerInvariant()).LifeStyle.Transient));
}
}
}
This is my ControllerFactory...
using System;
using System.Web;
using System.Web.Mvc;
namespace Persistence.Containers
{
/// <summary>
/// Utilize Castle.Windsor to provide Dependency Injection for the Controller Factory
/// </summary>
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly Castle.Windsor.IWindsorContainer container;
public WindsorControllerFactory()
{
container = WindsorContainerFactory.Current();
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return (IController)container.Resolve(controllerType);
}
}
}
This is my Application_Start in the global.asax file..
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
// Register the Windsor Container
ControllerBuilder.Current
.SetControllerFactory(typeof(Persistence.Containers.WindsorControllerFactory));
}
I am getting the error
No component for supporting the service Project.Web.Controllers.HomeController was found
at the GetControllerInstance.
So , I'm not really sure what I am doing wrong, and why I cannot get the Controllers registered.
Your Castle Windsor setup code all belongs in your Web project. It is nothing to do with Persistence.
This is causing the problem because your ControllerInstaller is trying to register the controllers in the Persistence assembly rather than the Web assembly with the following code:
System.Reflection.Assembly.GetExecutingAssembly().
So move the IoC code to the Web project and it will find your controllers.