Are attribute-based routes supported by T4MVC in some way?
I have applied a RoutePrefixAttribute to my MVC 5 controller and a Route attribute on my action. T4MVC, as it stands, does not seem to provide routes based on these attributes. The route that it provides is the convention-based routes of /Area/Controller/Action.
Folder structure is:
/
Areas
Ratio
Controllers
RatioSet
PresetGroupController.cs
Views
RatioSet
GroupDetails.cshtml
Controller:
[RoutePrefix("Ratio/RatioSet/Preset/Group")]
public partial class PresetGroupController
{
[Route("Details")]
public virtual ActionResult Details()
{
//.....
return View(MVC.Ratio.RatioSet.Views.GroupDetails, model);
}
}
Now, if I try the following:
return RedirectToAction(MVC.Ratio.PresetGroup.Details());
I get a 404 error, because the requested URL is:
<app_root>/Ratio/PresetGroup/Details
which is the "default" route, rather than the correct attribute-specified:
<app_root>/Ratio/RatioSet/Preset/Group/Details
So, does T4MVC only work with the convention-based routes, inferred from the folder structure, and not any routes that are specified via attributes?
I know this is an old question, but I had the same problem and ended up fixing it adding the RouteArea attribute to the Controller.
Something like this:
[RouteArea("Ratio")]
[RoutePrefix("Ratio/RatioSet/Preset/Group")]
public partial class PresetGroupController
{
[Route("Details")]
public virtual ActionResult Details()
{
//.....
return View(MVC.Ratio.RatioSet.Views.GroupDetails, model);
}
}
Have you verified that the non-T4MVC equivalent is working? If so, what does that line look like?
Note that T4MVC really doesn't generate routes itself, but instead calls into standard MVC framework methods to do this. See section 1.1 in the docs.
My guess is that you're running into an issue that is unrelated to T4MVC. e.g. see this issue where the problem is the order of registration calls.
Related
I want to use controller to load directly some models by using default constructor, but CodeIgniter4 was removed __construct method from CodeIgniter4 framework and I got error message Cannot call constructor. please see code below:
public function __construct(){
parent::__construct();
//Do magic task here
}
Instead of CI3 is working by using above code, Can anyone suggest me in CI4?
Thank You for your tips or comments!
Best Regards!
CI4 default controller namespaced CodeIgniter\Controller doesn't have a class constructor. So if you're extending directly from it, parent::__construct() can't be called.
If you really need a constructor for every one of your controller you should modify App\Controllers\BaseController and making your others controllers extending it with the code you provided.
Also if your goal is to execute some code before or after your controller is called you should check out Filters in CI4. They are perfectly designed for this need : https://codeigniter4.github.io/userguide/incoming/filters.html
You can use constructor in CI4 to load models
Add the code in your Controller class and it should work.
protected $userModel;
public function __construct()
{
$this->userModel = new UserModel();//Create a instance of the model
helper('form', 'url');
}
I am trying to create a custom manager which is passed in the controller when it is being called and I am having troubles understanding the current implementation of new MVC5 project in c#.
Here is the default implementation:
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
above all of that are declarations for them:
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Now from my understanding the SignInManager and UserManager get created when application gets created for the first time in Startup.Auth.cs which looks like this:
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
So now whenever I call UserManager I will get that first instance that was created when project ran for the first time.
I have 2 questions. Question 1 is is anything I said above wrong and Do I have a wrong understanding of how MVC5 works?
Question2: How is UserManager and SignInManager generated and passed in the controller? Where is the code that creates that first instance of the manager and passes it in the controller? I am assuming it is app.CreatePerOwnContext that does it. If so, can I then just create my own Manager and then register it with Owin in the same fashion and reuse throughout the project? Will my code get the latest data from the database if I do this and not cache it?
The code you're showing is coming from the IMO very ugly MVC5 template, which works out of the box but does some ugly things.
This constructor:
public AccountController(ApplicationUserManager userManager,
ApplicationSignInManager signInManager)
makes you think OWIN automagically injects the managers for you. But in fact this is not the case. That is why the template comes with the ugly properties you supplied in the questions. When you do not change anything to the template, the default constructor is called (also present in the template). To try it, just delete, or comment, the default constructor and you'll see the AccountController can't be created anymore.
So what is actually happening there is that both managers are located using the Service Locator anti pattern in the getters of the supplied properties.
So now whenever I call UserManager I will get that first instance that was created when project ran for the first time?
No this is not the case. What this line:
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
does, is creating a delegate to the Create method of both managers. The managers are cached within an Owin Request. The next request the delegates are called again and you get a fresh ApplicationUserManager etc.
To be a little bit more verbose this line could be rewritten as:
Func<ApplicationUserManager> userManagerFactory = () => ApplicationUserMangager.Create();
app.CreatePerOwinContext<ApplicationUserManager>(userManagerFactory);
So if you would a breakpoint here:
public ApplicationUserManager UserManager
{
get
{
// place breakpoint here
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
// ....
You would see that while stepping through the code, you will hit the line where you created the UserManagerFactory which in his turn will call the Create() method of the ApplicationUserManager.
How is UserManager and SignInManager generated and passed in the controller
It isn't! You would need to use dependency injection for that.
If so, can I then just create my own Manager and then register it with Owin in the same fashion and reuse throughout the project
Yes you can. You can completely refactor the ApplicationUserManager you also got 'for free' in the template. As long as you supply a factory method to the 'CreatePerOwinContext' extension method.
Will my code get the latest data from the database if I do this and not cache it?
The instances are cached on per request base. So each request you will get a new one, with a new DbContext etc.
I'm unsure how familiar you are with dependency injection but MVC5 is a pretty easy framework to start with it, IMO.
I once wrote a blogpost how to configure my DI container of choice (Simple Injector) to use with the MVC5 template.
I also wrote several answers here on SO regarding this template: specifically this one, should interest you. This one is interesting also!
I'm getting this error message, and any of the advice that I've seen does not appear to be applicable; i.e. all views, controllers and models are in the correct folders.
More detail:
I have a master view, which shows a graphical flowchart-like interface for interacting with the application. The user selects the "Open Study" symbol, and I redirect to another view which allows the user to select a Study to work with.
The OpenStudyController code retrieves the selected study and then redirects back to the master view:
public ActionResult SelectStudy( Guid? id )
{
// code elided for clarity
return RedirectToAction( "ActivateStudy", "Home" );
}
HomeController has a method called ActivateStudy(...), which does get invoked with the appropriate environment:
public ActionResult ActivateStudy()
{
// code elided for clarity
return View();
}
As I said, all views, controllers and models are in the correct folders.
When the "return View()" code in ActivateStudy() is executed, the error message occurs:
Server Error in '/' Application.
The view 'ActivateStudy' or its master was not found or no view engine supports the searched locations.The following locations were searched:
~/Views/Home/ActivateStudy.aspx
~/Views/Home/ActivateStudy.ascx
~/Views/Shared/ActivateStudy.aspx
~/Views/Shared/ActivateStudy.ascx
~/Views/Home/ActivateStudy.cshtml
~/Views/Home/ActivateStudy.vbhtml
~/Views/Shared/ActivateStudy.cshtml
~/Views/Shared/ActivateStudy.vbhtml
What am I missing? Some additional parameter in RedirectToAction(...)? Some new entry in RouteConfig?
If you have a _ViewStart.cshtml and it contains something like the following you do not need to specify the same layout in each view.
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
I believe your problem might be related to a missing slash. In your comment you said that your view contained the following line:
Layout = "~Views/Shared/_Layout.cshtml"
I think that needs to have a slash after the tilde.
Layout = "~/Views/Shared/_Layout.cshtml"
But, as I said, you should be able to remove that line entirely.
Well, I don't know if this is the "right" way to do it, but, in my HomeController.ActivateStudy() method, I return the Home "index" view:
public ActionResult ActivateStudy()
{
// code elided for clarity
return View( "Index" );
}
And this works.
It's at times like these where you realize you really don't know very much. Back to the books and code editor. Learn by doing.
I'm planning to organize my controllers in sails using subfolder but I'm not sure how to do it. When I tried using like admin/PageController.js and connect it with the route I keep getting a 404 error.
You can definitely do this. The trick is, the controller identity is its path, in your case admin/PageController. So a custom route in config/routes.js would be something like:
'GET /admin/page/foo': 'admin/PageController.foo'
The great thing is, automatic actions still work, so if you have an index action in the controller then browsing to /admin/page will automatically run it.
You can also create controllers like this with sails generate controller admin/page.
Edit
Since commit 8e57d61 you can do this to get blueprint routes and functionality on nested controllers, assuming there is an AdminPage model in your project:
// api/controllers/admin/PageController.js
module.exports = {
_config: {
model: 'adminpage'
}
}
or this:
// config/routes.js
module.exports.routes = {
'admin/page': {
model: 'adminpage'
}
}
Old Answer
Your options
Defining explicit routes to your grouped controllers in config/routes.js.
Look at Scott Gress' answer for more details.
(If you are a bit adventurous) As i had the exact same requirement for a project of mine I created a Pull Request on Sails that allows you to override the model - controller association. You could install it via
npm install -g git://github.com/marionebl/sails.git#override-controller-model
Assuming it is the api/models/Page.js model you want the blueprint methods for on api/controllers/admin/PageController.js you then could do:
// api/controllers/admin/PageController.js
...
module.exports = {
_config: {
model: 'page'
}
}
Explanation
While generating/creating grouped controllers like this is perfectly valid an possible, you will not get the default blueprint routes you'd expect for controllers accompanied by models with the same identity.
E.g. api/controllers/UserController.js and api/models/User.js share the same identity user, thus the blueprint routes are mounted if they are enabled in config/blueprints.js.
In fact at the moment it is not possible to group models into subfolders in a valid way. This means you won't be able to create a model that matches the identity admin/page of your controller api/controllers/admin/PageController.js - the blueprint routes are not mounted for PageController.
The source responsible for this behavior can be inspected on Github.
I made a diagram that shows how implicit routes, explicit policies, nested controllers, singular models and nested views are related. It does not show an overridden model-controller association as described by #marionebl.
It was mostly an exercise for me to understand this topic better, but I hope it helps somebody else too. Please let me know if I made any mistakes:
Thanks merionebl, its work fine for me and I want to share with all guys my answer derived from merionebl answer.
/config/routes.js
'get /admin/user' : {
controller: "Admin/UserController", action: "find",
model : 'user',
},
My aim is not repeat answer just have upgrade and clear example.
Thanks
I have an asp.net mvc application and I am developing a custom attribute to secure some wcf end points inheriting from a CodeAccessSecurityAttribute.
I'm having difficulty finding out how I would use autofac to inject a service dependancy that I can use within this attribute.
[Serializable]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class SecuredResourceAttribute : CodeAccessSecurityAttribute
{
public ISecurityService SecurityService { get; set; }
public SecuredResourceAttribute(SecurityAction action) : base(action)
{
}
public override IPermission CreatePermission()
{
// I need access to the SecurityService here
// SecurityService == null :(
}
}
I have tried from the application start to register for property auto wiring, but this is not working. What's the best way to inject a dependancy into an attribute?
builder.RegisterType<SecuredResourceAttribute>().PropertiesAutowired();
Thanks
The way you are approaching this is not going to pan out for a couple reasons:
Registering an attribute in autofac will do nothing as you're not using autofac to instantiate the attribute.
Attributes are applied before code execution, and thus rely on constant inputs.
You're going to have to use a service location pattern inside your CreatePermission() method to locate the SecurityService, as I am assuming the CreatePermission() call comes after the container is setup (and the constructor does not!)
Keep in mind ServiceLocation will hinder your class testability, as you will have to configure/set-up the service locator for each test.
Please use with caution
You should start your journey into ServiceLocation here but honestly this should make you question your design. Is an attribute best suited for the role you've tasked it? Perhaps you should look into Aspect-Oriented Programming like PostSharp