Looking through the source for our applications, I found a common Spring MVC controller which display key and values of configuration copied and pasted several times. The class definitions are exactly the same, except for the RequestMapping value, since each application want to have this page available under different URLs.
I want to move this controller into a common library, and provide a default RequestMapping value.
#Controller
#RequestMapping (value="/property")
public class CommonPropertyController {
....
}
How would each application override this value if they want to use their own url pattern?
Looking at the source code I got an idea how to do it without having to go back to manual (pre-annotation) handler definition (which is also a way how to implement what you need).
Spring allows you to use property placeholder configurers in #RequestMapping values. So it is possible to use that fact and define #RequestMapping like:
#Controller
#RequestMapping("${routing.property.path}")
public class CommonPropertyController {
....
}
Then you can simply define PropertySourcesPlaceholderConfigurer with the right properties in your application context and you are good to go.
UPDATE You can also specify a default value using property placeholder if you want to have fallback mapping in case the property is not speciefied:
#RequestMapping("${routing.property.path:/property}")
Related
Recently i tried to create a MVC application using ASP.NET Core 2.0 and i had some values defined in appsettings.json,
"MySettings": {
"WebApiBaseUrl": "http://localhost:6846/api/"
}
In order to read these values i have added
services.Configure<MySettingsModel>(Configuration.GetSection("MySettings"));
above line in ConfigureServices method in Startup.cs
and in my home controller i have added
private readonly IOptions<MySettingsModel> appSettings;
public HomeController(IOptions<MySettingsModel> app)
{
appSettings = app;
}
MySettingsModel class is just a model with property same as key define in appsettings.json.
by this method i'm able to read the value of this key.
Now my issue is that i want to use this key in many controllers so i don't want to repeat this code in every controller so what i did was i created a BaseConntroller, added its constructor and i got my values there. But when i inherit other controllers with my BaseController , it throws me an error and tells me to generate it's constructor, so basically it tells me to add constructor in every controller which is what i wanted to avoid.
How can i achieve this?
You can see the image for the error
And these are the potential fixes that it shows me.
This is just basic C# inheritance. Derived classes must re-implement constructors on base classes (at least the ones you want or need). The only exception is the empty constructor, which is implicit. In other words, you simply need:
public class HomeController : BaseController
{
public HomeController(IOptions<MySettingsModel> app)
: base(app)
{
}
And, of course, you need to change the accessibility of the base class field to protected instead of private. Otherwise, derived classes will not be able to access it.
Of course, this doesn't really save you that much. However, there's no free lunch here. Like I said, this is a limitation of C#, itself, so you have no choice. Although, it's worth mentioning, that while this can sometimes be annoying, it's actually a kind of useful feature of C#. You can look at any class and see exactly what constructors it has available, without having to trace down all its ancestors.
Actually, there is a good solution here:
https://stackoverflow.com/a/48886242/2060975
I am mostly using this method.
[Authorize]
[ApiController]
public abstract class ApiControllerBase : ControllerBase
{
private IOptions<AppSettings> _appSettings;
protected IOptions<AppSettings> appSettings => _appSettings ?? (_appSettings = (IOptions<AppSettings>)this.HttpContext.RequestServices.GetService(typeof(IOptions<AppSettings>)));
...
}
I hope it helps someone:)
I have a spring-mvc application that is using java configuration, not xml. There are #Configuration annotations sprinkled through several files. In particular, there is a #PropertySources annotation in one file in a class that implements WebMvcConfigurerAdapter. There are two classes which contain an #Autowired Environment variable. One of these classes is itself a #Configuration class, and I would like it to have access to the fully-loaded Environment at the time it runs.
This isn't happening. When this code executes, the Environment is still null. I've tried reordering the #ComponentScan packages, tried moving the #PropertySources annotation, and nothing has helped me load the property sources in time.
I want this to happen first, before any other configuration.
What must I do to make it so?
UPDATE: After trying many things, including the Order annotation, I find the problem seems to be not so much that the #PropertySources are being loaded too late as that a class I have that is derived from org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer is being loaded too soon. Nothing in my code even references this class but Spring is somehow deciding that this, above all other classes, must be initialized first. No amount of messing around with #Order seems to change this. This in spite of the javadocs, which indicate that the behavior I want is the default:
Caveats
Subclasses of AbstractDispatcherServletInitializer will register their
filters before any other Filter. This means that you will typically
want to ensure subclasses of AbstractDispatcherServletInitializer are
invoked first. This can be done by ensuring the Order or Ordered of
AbstractDispatcherServletInitializer are sooner than subclasses of
AbstractSecurityWebApplicationInitializer.
You can use ContextInitializer, catch the moment when Boot prepared its environment and "inject" your property source programmatically as you want.
If you have a ConfigurableApplicationContext then it provides a ConfigurableEnvironment, which contains the propertySources as a list. If you want your PropertySource to rule all above the others than add it as first. If you want to place it to a special position then you can add it before an other, identified by its "name".
public class ConfigInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext ctx) {
// rule over all the others:
ctx.getEnvironment().getPropertySources().
addFirst(new ResourcePropertySource("file:/etc/conf/your_custom.properties"));
// rule over application.properties but not argument or systemproperties etc
ctx.getEnvironment().getPropertySources().
addBefore("applicationConfig: [classpath:/application.properties]",
new ResourcePropertySource("classpath:your_custom.properties"));
// names can be discovered by placing a breakpoint somewhere here and watch
// ctx.getEnvironment().getPropertySources().propertySourceList members,
// each has a name ...
}
}
You can bring into play this class by:
registering it in the application.properties :
context.initializer.classes=a.b.c.ConfigInitializer
or from application start :
new SpringApplicationBuilder(YourApplication.class).
initializers(new ConfigInitializer()).
run(args);
By the way this is a proper way to inject more of your custom properties, like properties coming from a database or a different host etc...
Not that I am an expert in Java config but maybe:
Spring 4 utilizes this feature in its #PropertySource annotation. To
remind you, #PropertySource annotation provides a mechanism for adding
a source of name/value property pairs to Spring's Environment and it
is used in conjunction with #Configuration classes.
Taken from: http://blog.codeleak.pl/2013/11/how-to-propertysource-annotations-in.html
Are you using #PropertySource or #PropertySources?
Have You tried 'order' property for spring bean initialisation?
I'm trying to use Groovy mixin transformation on a spring-mvc controller class but Spring does not pickup the request mapping from the mixed in class.
class Reporter {
#RequestMapping("report")
public String doReport() {
"report"
}
}
#Mixin(Reporter)
#Controller
#RequestMapping("/a")
class AController {
#RequestMapping("b")
public String doB() {
"b"
}
}
When this code is run .../a/b url is mapped and works but .../a/report is not mapped and returns HTTP 404. In debug mode, I can access doReport method on AController by duck typing.
This type of request mapping inheritance actually works with Java classes when extends is used; so why it does not work with Groovy's mixin? I'm guessing it's either that mixin transformation does not transfer annotations on the method or that spring's component scanner works before the mixin is processed. Either way, is there a groovier way to achieve this functionality (I don't want AController to extend Reporter for other reasons, so that's not an option) ?
You can find below the responses I got from Guillaume Laforge (Groovy project manager) in Groovy users mailing list.
Hi,
I haven't looked at Spring MVC's implementation, but I suspect that
it's using reflection to find the available methods. And "mixin"
adding methods dynamically, it's not something that's visible through
reflection.
We've had problems with #Mixin over the years, and it's implementation
is far from ideal and bug-ridden despite our efforts to fix it. It's
likely we're going to deprecate it soon, and introduce something like
static mixins or traits, which would then add methods "for real" in
the class, which means such methods like doReport() would be seen by a
framework like Spring MVC.
There are a couple initiatives in that area already, like a prototype
branch from Cédric and also something in Grails which does essentially
that (ie. adding "real" methods through an AST transformation).
Although no firm decision has been made there, it's something we'd
like to investigate and provide soon.
Now back to your question, perhaps you could investigate using
#Delegate? You'd add an #Delegate Reporter reporter property in your
controller class. I don't remember if #Delegate carries the
annotation, I haven't double checked, but if it does, that might be a
good solution for you in the short term.
Guillaume
Using the #Delegate transformation did not work on its own, so I needed another suggestion.
One more try... I recalled us speaking about carrying annotations for
delegated methods... and we actually did implement that already. It's
not on by default, so you have to activate it with a parameter for the
#Delegate annotation:
http://groovy.codehaus.org/gapi/groovy/lang/Delegate.html#methodAnnotations
Could you please try with #Delegate(methodAnnotations = true) ?
And the actual solution is:
class Reporter {
#RequestMapping("report")
public String doReport() {
"report"
}
}
#Controller
#RequestMapping("/a")
class AController {
#Delegate(methodAnnotations = true) private Reporter = new Reporter
#RequestMapping("b")
public String doB() {
"b"
}
}
When you map requests with annotations, what happens is that once the container is started, it scans the classpath, looks for annotated classes and methods, and builds the map internally, instead of you manually writing the deployment descriptor.
The scanner reads methods and annotations from the compiled .class files. Maybe Groovy mixins are implemented in such a way that they are resolved at runtime, so the scanner software can't find them in the compiled bytecode.
To solve this problem, you have to find a way to statically mixin code at compile time, so that the annotated method is actually written to the class file.
I've read Spring 3 docs about view resolvers... But in my project I didn't define any. I just return a ModelAndView or #ResponseBody String from the controller methods. I guess there is a default view resolver (perhaps UrlBasedViewResolver) that is already configured. What is that? Where can I get info in the docs about this?
If I want to add other views like JsonView or XmlView (right now I use a Jsp that renders the data, but I read I can avoid this by passing model Objects directly to special views that will do this for me) how do I deal with this default view resolver?
The default is an automatically-registered InternalResourceViewResolver (UrlBasedViewResolver is an abstract superclass of this).
If you declare your own view resolver(s), then the default InternalResourceViewResolver will not be used. You can, if you, wish, simply redeclare it as an explicit bean. If there are multiple view resolvers, then they will be consulted in order until one of them returns a view object. However, because of the way that the servlet API works, InternalResourceViewResolver must always be the last view resolver in the chain.
If your controller method returns a View object directly, then no view resolver is necessary, and the view will be rendered directly. Similarly, if you use #ResponseBody, the view resolver is bypassed.
I'm attempting to create a generic controller, ie:
public class MyController<T> : Controller where T : SomeType
{ ... }
However, when I try to use it, I'm running into this error everywhere...
Controller name must end in 'Controller'
So, my question, Is it possible to make a generic controller in asp.net mvc?
Thanks!
If I understand you properly, what you are trying to do, is route all requests for a given Model through a generic controller of type T.
You would like the T to vary based on the Model requested.
You would like /Product/Index to trigger MyController<Product>.Index()
This can be accomplished by writing your own IControllerFactory and implementing the CreateController method like this:
public IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = Type.GetType("MyController")
.MakeGenericType(Type.GetType(controllerName));
return Activator.CreateInstance(controllerType) as IController;
}
Yes you can, it's fine and I've used them lots myself.
What you need to ensure is that when you inherit from MyController you still end the type name with controller:
public class FooController : MyController<Foo>
{
...
}
The default controller factory uses "convention" around controller names when it's trying to find a controller to dispatch the request to. You could override this lookup functionality if you wanted, which could then allow your generic controller to work.
This MSDN article...
http://msdn.microsoft.com/en-us/magazine/dd695917.aspx
... has a good writeup of what's going on.
This is a duplicate of asp.net mvc generic controller which actually contains the correct answer. Jeff Fritz's answer is absolutely not correct. Creating your own IControllerFactory will not get past the limitation in ExpressionHelper.GetRouteValuesFromExpression which is generating the error you are seeing. Implementing your own IControllerFactory will still leave you with errors whenever you call RedirectToAction, BuildUrlFromExpression, ActionLink, RenderAction, BeginForm, any any methods that call those.
What is interesting to me, is that Microsoft's "restriction by convention" is already enforced by the constraint "where TController : Controller" that is placed upon the type in the ExpressionHelper.GetRouteValuesFromExpression method. No generic will ever satisfy the convention validation:
string controllerName = typeof(TController).Name;
if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) {
throw new ArgumentException(MvcResources.ExpressionHelper_TargetMustEndInController, "action");
}
unless it is inherited by a class ending in "Controller" because typeof(AnyGeneric).Name will never end with "Controller".
If i was you, i'd get the MVC source and create a test MVC project with the source code so you can examine where the exception is generated and see what you can do about your generic idea and the enforced "*controller" naming convention.