Tried all, build rebuild etc.
I reverted back to 7.4.1 and hijacked controllers work ok.
Also for the new test project I made starting at 7.4.2 that didn't worked as well, I downgraded it to 7.4.1 and works.
The render models I create are like :
public class TextPageModel : RenderModel
{
public TextPageModel()
: base(UmbracoContext.Current.PublishedContentRequest.PublishedContent,UmbracoContext.Current.PublishedContentRequest.Culture)
{
}
public string CustomProperty { get; set; }
}
And the RenderMvcController like:
public class TextPageController : RenderMvcController
{
// GET: Home
public ActionResult textPage(TextPageModel model)
{
model.CustomProperty="Yeah!";
return CurrentTemplate(model);
}
}
of course I user DocType / ViewName auto route. Works nicely in 7.4.1.
public class TextPageController : RenderMvcController{
public ActionResult textPage()
{
var model = new TextPageModel();
model.CustomProperty="Yeah!";
return CurrentTemplate(model);
}
As it turns out i think this is the one scenario we didn't test for: A sub-classed RenderModel type being bound to a controller's action parameter. I'd actually recommend not doing this as it's not quite best practices and relies on Singletons (i.e. not unit testable)
There's a few work around's you can do:
Leave your controller's action parameter as type: RenderModel and create your custom model inside of your MVC Action
this is actually nicer anyways because you don't really want to rely on Singleton's in your models constructor - this makes it near impossible to unit test. You'd be better off using the given RenderModel 's values in the Action parameter to constructor your custom model.
Create a custom ModelBinder that inherits from DefaultModelBinder and implements IModelBinderProvider then in the IModelBinder GetBinder(Type modelType) method you would be targeting your model type explicitly like: return modelType == typeof (TextPageModel);
You can then assign this model binder directly using the [ModelBinder] attribute on your Action parameter, or you can assign it globally during startup.
I'll create an issue on the tracker for 7.4.3 to fix this but these are suitable work-arounds for the time being.
Are you saying that this is not the proper way to do it? Then I would appreciate some guidance on how to this properly. I am aware of the unit testing limitation, of course - and if there is a way of doing this and making it unit testable, well - that would be great :)
I would not recommend doing it that way because again, you are tied to Singletons - the use of Singletons is an anti-pattern, they are there mostly for legacy reasons.
I don't understand why you cannot create this model inside of the Action, can you explain this?
That blog post example should be changed, the code should be:
public class BlogOverview : RenderModel
{
public BlogOverview(IPublishedContent content) : base(content)
{
}
public int Page { get; set; }
}
public class BlogOverviewController : RenderMvcController
{
public ActionResult BlogOverview(RenderModel model)
{
var blogOverviewModel = new BlogOverview(model.Content);
if (blogOverviewModel.Page == 0) // default value for int
blogOverviewModel.Page = 1;
// do paging
return CurrentTemplate(blogOverviewModel);
}
}
In 7.4.3 I've updated the code so that your original way will still work - but I really don't recommend that for the reasons above.
Hey Shannon, thank you for your reply.
Is there any possibility to create a quick fix for 7.4.2?
I think many devs have implemented RenderModels such way and would face pain when the try to upgrade to 7.4.2 (most reason of the true/false issue of 7.4.1).
Following an upgrade from 7.4.1 to 7.4.2, I was also getting errors of type Cannot bind source type Umbraco.Web.Models.RenderModel to model type Odin.Models.ViewModels.MyCustomPageModel..
Shannon's fix you confirmed as working here, also worked for me.
Then rename the class to something unique and give it a different namespace. Then in an ApplicationEventHandler class you remove the faulty RenderModelBinder and add your own instead:
public class Startup : ApplicationEventHandler
{
protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
//Workaround for http://issues.umbraco.org/issue/U4-8216
var rendermodelbinder = ModelBinderProviders.BinderProviders.FirstOrDefault(x => x.GetType() == typeof(RenderModelBinder));
ModelBinderProviders.BinderProviders.Remove(rendermodelbinder);
ModelBinderProviders.BinderProviders.Add(new FixedRenderModelBinder());
}
}
I'm actually in a similar scenario to Asbjørn here, I'm sending form post data to my model, as a result the model is now coming through as null!
Changing this to a RenderModel() would obviously lose my custom values passed through...
Any workarounds in this scenario? I'm working more with traditional MVC methods in this scenario.
I would suggest that you don't use a sub class of RenderModel for posting data. That model is for "rendering" information, not for posting. I would have to assume that you don't actually require all of the information in RenderModel to accomplish what you want?
In any case, if you want to continue using a sub class of RenderModel for posting data
you can use the latest nightly I posted previously
or you can use Asbjørn's workaround to register a 'Fixed' model binder
or you can probably just attribute your action's parameter with [ModelBinder(typeof(DefaultModelBinder))]' since it was only working previously based on theDefaultModelBinder` executing
There are some Umbraco queries inside the controller, which threw a PublishedContentRequest error if I didn't inherit from RenderModel.
The attributes didn't seem to fix anything either.
I'm actually receiving an empty model, rather than the error mentioned above, but it's stopped working after the update. I'm wondering if it's a slightly different version of the bug.
RenderMvcControllers are not intended to be POSTed to, that is what SurfaceControllers are for, but i guess everyone does all sorts of zany things with Umbraco.
In any case, why do you need to POST the whole RenderModel? What is this Action actually doing? What was your code before (i.e. when it was working)?
You said you tried this.... is this actually what you tried while keeping your model the same as it was before?
public ActionResult Post([ModelBinder(typeof(DefaultModelBinder)]MyViewModel model)
Can you remove the "Surface" from the SurfaceController URL? So I can keep a nicely formatted URL for users.
But ideally, yes, I do need to post the whole model, or a model of somekind, there are about 15+ properties, of many different types.
I'd stupidly put [ModelBinder(typeof(DefaultModelBinder))] as an attribute on the class, not the paramater. My mistake. Applying it to the attribute does work.
Thank you!
Part of the point of a SurfaceController is that is routed nicely (the same URL), it does NOT route to the /umbraco/surface URL. Please try it and you'll see. It all works by using BeginUmbracForm
Thanks to that blog post by Sebastian, I have no doubt that many people have run into this issue. In order to prevent other developers from the headache I just went through trying to find this thread, this NEEDS to be listed as a breaking change in the release notes for 7.4.2. Especially since what I thought was going to be an easy update turned out to be a pain.
Someone over at Umbraco HQ needs to work on quality control 'cuz this shit is bananas.
Hi there, I wonder if you could help me. I'm new to Umbraco and .net MVC, but was following a tutorial to set up my own route hijacking controller. I ended here up because I was getting the same initial error.
Cannot bind source type Umbraco.Web.Models.RenderModel to model type .....
I reworked my code as Shannon suggested, ie:
public class RSSFeedController : RenderMvcController
{
// GET: RSSPage
public ActionResult RssFeed(RenderModel model)
{
var rssFeedModel = new RssFeedModel(model.Content);
rssFeedModel.RssFeed = GetRssFeedItems(model);
Response.AddHeader("content-type", "text/xml");
return CurrentTemplate(rssFeedModel);
}
private IEnumerable<IPublishedContent> GetRssFeedItems(RenderModel model)
{
const int pageSize = 5;
var posts = model.Content.Children.ToList();
return posts.OrderByDescending(x => x.CreateDate).Take(pageSize);
}
}
Except now I get a compilation error in my view:
CS1061: 'Umbraco.Web.Models.RenderModel' does not contain a definition for 'RssFeed' and no extension method 'RssFeed' accepting a first argument of type 'Umbraco.Web.Models.RenderModel' could be found (are you missing a using directive or an assembly reference?)
at this line:
@foreach (var article in Model.RssFeed)
How do I tell my View to expect an RssFeedModel, and not a standard RenderModel? My RssFeedModel is based on RenderModel:
public class RssFeedModel : RenderModel
{
public RssFeedModel(IPublishedContent content) : base(content) { }
public IEnumerable<IPublishedContent> RssFeed { get; set; }
}
Upgrading to the nightly (v7.4.3build3482) has fixed the issue for me, though it does introduce a ReflectionTypeLoadException in the Developer section of the backoffice. I get this exception on the call for GetNonCoreTypesAssignableFrom.
You can attribute your parameter with the specific model binder:
public ActionResult Post([ModelBinder(typeof(DefaultModelBinder)]MyViewModel model)
Maybe someone can mark that as fixed so it's obvious what the work around is.
@Matthew if you have steps to reproduce the other error you are getting with the nightly can you please open a ticket with details and steps to reproduce. I haven't seen this error before.
public ActionResult RssFeed([ModelBinder(typeof(DefaultModelBinder))]RssFeedModel model)
{
/*
var rssFeedModel = new RssFeedModel(model.Content);
rssFeedModel.RssFeed = GetRssFeedItems(model);
* */
model.RssFeed = GetRssFeedItems(model);
Response.AddHeader("content-type", "text/xml");
return CurrentTemplate(model);
}
private IEnumerable<IPublishedContent> GetRssFeedItems(RenderModel model)
{
const int pageSize = 5;
var posts = model.Content.Children.ToList();
return posts.OrderByDescending(x => x.CreateDate).Take(pageSize);
}
But I still get the compile error
Compiler Error Message: CS1061: 'Umbraco.Web.Models.RenderModel' does not contain a definition for 'RssFeed' and no extension method 'RssFeed' accepting a first argument of type 'Umbraco.Web.Models.RenderModel' could be found (are you missing a using directive or an assembly reference?)
Source Error:
Line 16:
Line 17:
Line 18: @foreach (var article in Model.RssFeed)
Line 19: {
Line 20: var tease = article.GetPropertyValue<string>("teaserText");
Hi Andy, Can you please advise that this worked before or are you just having a new issue?
First thing I want to mention is that you should really not be using RenderModel for this purpose. IPublishedContent is the real model you should be using to display data that comes from it. In v8 we won't even have RenderModel everything will be IPublishedContent, RenderModel was only ever invented just in case we had additional model information that we needed to pass to a view outside of the data contained in IPublishedContent and we've determined that will never happen since there's never been a need in over 2 years. This is actually the first time I've seen people sub classing RenderModel, i didn't know people were actually doing this ... had I known that was going to happen we would have made RenderModel sealed since it's not exactly what we intended by design.
In your case you could have a custom implementation of IPublishedContent called RssFeedContent that inherits from PublishedContentWrapped and you pass in the current IPublishedContent instance to its ctor. Then you can add your RssFeed property to this class... which you could also make with a backing field so that it's lazily resolved and only resolved once. Or you could just as easily make a GetRssFeedItems extension method on IPublishedContent
Of course if this used to work then we need to fix it - which should be fixed in 7.4.3 which will be out soon
I'm surprised this is the first time you've seen this. I thought it was commonplace for people to subclass RenderModel in order to pass data from their custom controllers to their views. This approach is even suggested in the Umbraco documentation.
What do you consider best practice for passing data from custom controllers to views?
That is not the problem being discussed on this thread. You can subclass RenderModel all you want to pass to your views, you can create and use any model that you want to pass to your views.... The problem listed here is that people are subclassing RenderModel to model bind to controller actions.
We broke that by accident because that isn't a documented practice and not something that I'd recommend. It will work again with the latest release but it sort of works by fluke.
This guy.. ;-) has updated his blog post (the one on umbraco.com was a crossposted copy) and the related gist to make sure all code examples are now correct.
Since this is brand new code, if you use the technique outlined in Sebastiaan's updated code and then ensure that you set the correct Model on your view @inherits UmbracoViewPage<BlogOverview> it should work... pretty sure that is the part you are missing.
Thanks @Shannon and @Sebastiaan. I'll have a look into that.
Sorry, @Sebastiaan I hadn't spotted out you were "the guy" who had written both posts. :-)
I guess if RenderModel is going away in v8 I should look at the IPublishedContent solution anyway, but for the meantime I'll try this. What's the worst that could happen - I build it, it goes live, and we forget all about it...
Sorry, me again. Have confirmed my code now matches Sebastiaan's, and the line that's causing the problem is this one in the view:
@inherits UmbracoViewPage<RssFeed>
(apols to everyone if that's blindingly obvious). I've renamed my model from RssFeedModel to just RssFeed, in case there was any dependency on the name like there is with controller, but that made no difference.
With the above line in my view I get this error message:
Cannot bind source type Umbraco.Web.Models.RenderModel to model type EandT2016.Models.RssFeed.
Without it (and of course without trying to use anything in the model), a simple page will render.
Sorry, meant to come back to this and admit humbly that I'd not spotted that my controller name didn't match with the document type name. Schoolboy error!
@Sebastiaan With your updated code in the blog post and the related gist I fail to see how paging can work
public class BlogOverviewController : RenderMvcController
{
public ActionResult BlogOverview(RenderModel model)
{
var blogOverviewModel = new BlogOverview(model.Content);
if (blogOverviewModel.Page == 0) // default value for int
blogOverviewModel.Page = 1;
// do paging
return CurrentTemplate(blogOverviewModel);
}
}
As you're creating a new BlogOverview, Page is always going to be 0.
The view is passing the page number in the query string and this was previously automatically bound to the model.
In MVC & WebApi, query strings are bound to action parameters... that is the very nature of how MVC binds things. You can just add a page parameter to your action. For example, Articulate does paging, it's just a p querystring which gets mapped to an action's parameter:
I always suggest doing some very simple MVC sites and tutorials outside of Umbraco since all of this stuff is covered and is quite a fundamental part of MVC.
I'm glad I stumbled on this as I was fumbling around for hours trying to figure out what should be relatively simple.
Coming from a vanilla MVC world, it's very common:
Design your model
Create a GET controller method that returns a new() instance of that model
Write a view that renders an appropriate form template using Razor helpers like @Html.TextBoxFor() etc
Post that data to a POST controller method (often with the same name, just the [HttpPost] attribute) that accepts an object of type (model). MVC ModelBinding takes care of the magic.
So given that the Umbraco docs tell us to subclass RenderModel for returning custom views, why wouldn't we then proceed to just post data back and rely on modelbinding to do the heavy lifting?
And yes, the example posted is clearly wrong because there's no model binding going on anywhere in there and so it never actually reads the page parameter. Having to manually bind model parameters is just... ew.
Now that I see we're supposed to use Surface controllers for posting data, and there's a whole Html.BeginUmbracoForm, I will have to adjust all my code, but man, if the documentation and all the code examples for RenderModel would just say "GET ONLY, NOT FOR POSTING, SEE ..." you might be able to avoid some of this confusion in the future.
As this thread is quite long can you please answer the following:
And yes, the example posted is clearly wrong because there's no model binding going on anywhere in there and so it never actually reads the page parameter. Having to manually bind model parameters is just... ew.
Can you please link to what is wrong so it can be fixed?
if the documentation and all the code examples for RenderModel would just say "GET ONLY, NOT FOR POSTING, SEE ..." you might be able to avoid some of this confusion in the future.
The last code block has a controller. This controller method never binds the input search parameter. A possible fix is this (changed method signature, added line to assign page; although I'd probably make a new constructor for the model if I was doing this from scratch). I also then removed the assignment that checks for page 0, since the default parameter handles it.
public ActionResult BlogOverview(RenderModel model, int page = 1)
{
var blogOverviewModel = new BlogOverview(model.Content) { Page = page };
// do paging
return CurrentTemplate(blogOverviewModel);
}
If I have time later, I'll submit a bug on the documentation, but as is, I'm behind several hours from trying to figure out how to make a simple GET-POST-REDIRECT custom search form.
I saw it eventually; it just wasn't the first things I found in a search.
What I really want to see is documentation on how to use a custom model with model binding in a GET scenario. I shouldn't have to POST for a search form (read operations should use GET); and I shouldn't have to manually wire up all the complex search parameters that I have.
I just finished writing some very basic code, and I'm running into trouble where something is not model binding correctly.
I took a simple
public class SearchModel: RenderModel {
[Required]
public string Keyword { get; set; }
}
public class SearchSurfaceController : SurfaceController
{
public ActionResult DoSearch(SearchModel m)
{
...
}
}
And I can put a breakpoint on DoSearch(); it does get routed there, but the search term is not bound correctly. It's always null, so ModelState.IsValid is false. Request.Form["SearchTerm"] has the correct value; so it's just model binding that's failing. Aggravating.
So you are doing the thing that this whole thread is about, you are sub-classing RenderModel and then trying to model bind it on the server. This will work in some cases but it's just not intended. If you are POSTing, then use a small custom model and post what you need. If you are GETing, then just get normally with query strings. In any case, I'm pretty sure your model binding isn't working about because you have a property called Keyword on your model and you are passing up a property from some model called SearchTerm.
What you want to do is quite simple - you really just want to get query strings to your page. You can just use normal html for that, only thing is the action will depend on what url your search page is. If it's the current page you can just supply no action, otherwise supply the one you want
It's also worth noting that BeginUmbracoForm has lots of overloads, just like the normal BeginForm and contains a FormMethod method so you can give it a GET
The keyword/searchTerm was just copy & paste laziness; I was trying to abstract out my code and make it as simple as possible, forgot to change one example.
I know how to write basic HTML forms and MVC forms; I have 6+ years of MVC development, so it's not my first rodeo. I don't want to have to put in 10 extra parameters to a method. That's why we have models and model binding!
If I want to make a search form with 10 different ways to search, I should be able to just model-bind, and to write a good Razor form, I want to use Razor helpers like LabelFor and TextBoxFor, so that I have proper intellisense and so that they will model-bind when I submit the form to my handler (which should be agnostic whether it's GET or POST).
Yes, you can add 10 different parameters to the ActionMethod, but that's very bad MVC practice! What if you add a new parameter to your model? Now you have to update the ActionMethod. Normally you'd just update the service layer / function call that's doing something with the data, but now you have to tell the controller to expect new data... ugly.
Coming from an MVC background i realize that it might seem strange that by default you have this RenderModel bound to a GET request as a parameter when using a custom controller for an umbraco page (hijacked routes). The reason for this is because it's just easier for the majority of users (which are generally not MVC devs) to use that parameter since its the standard data source/model for the page.
In a normal MVC scenario (as you mentioned above) you have GET action that creates a model - which is normally based on a data source. In Umbraco, the page data source is RenderModel or more accurately IPublishedContent. For simplicity this data source is model bound (custom binder) as an action parameter. This is totally optional.
Your custom controller action could just look like this and instead of having RenderModel as an action parameter you can create your model based on the umbraco page data source.
public ActionResult Index()
{
var model = new Umbraco.Web.Models.RenderModel(CurrentPage);
return CurrentTemplate(model);
}
In this scenario the base class exposes CurrentPage (the instance of IPublishedContent) as the data source for the page. You can use this data source to build up any model you want to pass to your view.
Regarding model binding a search model, i mentioned query strings and individual parameters because that's simple for a single search field. If you want to bind a complex model, you certainly can. Whether you want your data source (RenderModel) model bound as a parameter or if you just want to use CurrentPage as your data source you can do both and it will bind:
public ActionResult Index(SearchModel searchModel)
public ActionResult Index(RenderModel render, SearchModel searchModel)
This would be based on GET requests to the current page without using a SurfaceController. But you could also use a SurfaceController which could make sense if you wanted to have this possible with both a POST (+ validation) and a GET. For example, you could do this:
public class SearchSurfaceController : SurfaceController
{
public ActionResult DoSearch(SearchModel search)
{
//If you wanted to support POST with some validation you would
// put that logic here
return CurrentUmbracoPage();
}
}
Hi, Sorry about posting on that again. I just need to have a custom Model to fill up the page with more data. Not for passing data back to the controller.
I try to be "more umbraco way" and follow Shannon post.
I finally get:
Controller:
public class StoreController : RenderMvcController
{
public ActionResult Store(RenderModel model)
{
var storeModel = new StoreModel(model.Content);
// adding some stuff
return CurrentTemplate(storeModel);
}
}
Model
public class StoreModel: RenderModel
{
public StoreModel(IPublishedContent content) : base(content) { }
public int Page { get; set; }
}
My umbraco version is at 7.3.1, but I still get the error:
Cannot bind source type Umbraco.Web.Models.RenderModel to model type umbLVC2.models.StoreModel
I need to get these things in the docs but this just emphasizes that if you want to work with totally custom models, bound action parameters, etc... you certainly can.
I've been stuck the entire day on the same problem.
I get that this isn't the correct way of using RenderModel, all I want to do is to insert dynamic data from my model into the view.
ATM I have thos code:
Model:
public class VerzoekIndienenModel : RenderModel
{
public IEnumerable<string> Project { get; set; }
public IEnumerable<string> Categorie { get; set; }
public List<string> Test { get; set; }
public string TestString { get; set; }
public VerzoekIndienenModel(IPublishedContent content, CultureInfo culture)
: base(content, culture)
{}
}
Controller:
public class VerzoekIndienenController : RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
//throw new Exception("bang");
var vmodel = new VerzoekIndienenModel(model.Content, model.CurrentCulture);
vmodel.TestString = "test";
return CurrentTemplate(vmodel);
}
}
I can't even try to acces TestString.
The problem I got was the exact same one as this thread is about:
Cannot bind source type Umbraco.Web.Models.RenderModel to model type .....
I switched to doing everything with AJAX now and not using a seperate controller for the page.
It's sort of confusing as the official umbraco page has documentation about how to implement this (https://our.umbraco.org/documentation/reference/routing/custom-controllers). But here I'm reading RenderModel isn't supposed to be used in this way.
public class SSRSReportController : RenderMvcController
{
public override ActionResult Index(Umbraco.Web.Models.RenderModel model)
{ ..
and I call it like this
switch (docTypeAlias)
{
case "ssrsreport":
<h1>Textpage for Reports</h1>
@Html.Partial("SSRSReport", new UmbracoGitHubReportViewer.Models.ReportSettingsModel(@component));
break;
}
If you want to use a controller for a Partial View then you would use a Child Action, which is also just an ASP.NET MVC process (not directly related to Umbraco)
Update to 7.4.2. all custom ? hijacked controllers stopped working
Hello, I just upgraded a 7.4.1 to 7.4.2 via nugget.
All hijacked controllers stopped working. the error is
I even created a new 7.4.2 with a highjacked controller but the same issue occurs.
ModelBuilder is turned off for this project.
Any thoughts? Any workaround?
Hi Lefteris,
Did you check configs ? Maybe some nuget made some changes in your config.
Thanks
All configs checked and are ok.
Maybe you need to rebuild your models ?
Tried all, build rebuild etc. I reverted back to 7.4.1 and hijacked controllers work ok.
Also for the new test project I made starting at 7.4.2 that didn't worked as well, I downgraded it to 7.4.1 and works.
The render models I create are like :
And the RenderMvcController like:
of course I user DocType / ViewName auto route. Works nicely in 7.4.1.
Is this a bug of 7.4.2?
Hi, try this:
Hi, I've got the same problem after upgrading to 7.4.2.
I return base.Index(model); in my RenderMvcController and use @inherits UmbracoViewPage
I assume it is a full bug in 7.4.2
Anyone else tested hijacked controllers?
As it turns out i think this is the one scenario we didn't test for: A sub-classed
RenderModel
type being bound to a controller's action parameter. I'd actually recommend not doing this as it's not quite best practices and relies on Singletons (i.e. not unit testable)There's a few work around's you can do:
RenderModel
and create your custom model inside of your MVC ActionRenderModel
's values in the Action parameter to constructor your custom model.ModelBinder
that inherits fromDefaultModelBinder
and implementsIModelBinderProvider
then in theIModelBinder GetBinder(Type modelType)
method you would be targeting your model type explicitly like:return modelType == typeof (TextPageModel);
[ModelBinder]
attribute on your Action parameter, or you can assign it globally during startup.I'll create an issue on the tracker for 7.4.3 to fix this but these are suitable work-arounds for the time being.
@Shannon I use this construct to pass additional values back and forth. So creating the model inside the action wouldn't really work.
Basically, I am doing what is shown in this example: https://umbraco.com/follow-us/blog-archive/2014/1/27/a-practical-example-of-route-hijacking-in-umbraco/
Are you saying that this is not the proper way to do it? Then I would appreciate some guidance on how to this properly. I am aware of the unit testing limitation, of course - and if there is a way of doing this and making it unit testable, well - that would be great :)
I would not recommend doing it that way because again, you are tied to Singletons - the use of Singletons is an anti-pattern, they are there mostly for legacy reasons.
I don't understand why you cannot create this model inside of the Action, can you explain this?
That blog post example should be changed, the code should be:
In 7.4.3 I've updated the code so that your original way will still work - but I really don't recommend that for the reasons above.
http://issues.umbraco.org/issue/U4-8216
Hey Shannon, thank you for your reply. Is there any possibility to create a quick fix for 7.4.2?
I think many devs have implemented RenderModels such way and would face pain when the try to upgrade to 7.4.2 (most reason of the true/false issue of 7.4.1).
Same problem here, I can confirm that switching from
To
Has fixed it for me. Thanks!
Following an upgrade from 7.4.1 to 7.4.2, I was also getting errors of type
Cannot bind source type Umbraco.Web.Models.RenderModel to model type Odin.Models.ViewModels.MyCustomPageModel.
.Shannon's fix you confirmed as working here, also worked for me.
Thanks, Chris
An easy fix for this is to grab the fixed RenderModelBinder from https://raw.githubusercontent.com/umbraco/Umbraco-CMS/cd401f5e37b8517c2a6d39e2746d8295d56ebf92/src/Umbraco.Web/Mvc/RenderModelBinder.cs.
Then rename the class to something unique and give it a different namespace. Then in an ApplicationEventHandler class you remove the faulty RenderModelBinder and add your own instead:
You can find the nightly build here with the fix: https://ci.appveyor.com/project/Umbraco/umbraco-cms-hs8dx/build/artifacts
We will get 7.4.3 out after the Easter break.
Hi Shannon,
I'm actually in a similar scenario to Asbjørn here, I'm sending form post data to my model, as a result the model is now coming through as null! Changing this to a RenderModel() would obviously lose my custom values passed through...
Any workarounds in this scenario? I'm working more with traditional MVC methods in this scenario.
I would suggest that you don't use a sub class of
RenderModel
for posting data. That model is for "rendering" information, not for posting. I would have to assume that you don't actually require all of the information in RenderModel to accomplish what you want?In any case, if you want to continue using a sub class of RenderModel for posting data
[ModelBinder(typeof(DefaultModelBinder))]' since it was only working previously based on the
DefaultModelBinder` executingThere are some Umbraco queries inside the controller, which threw a PublishedContentRequest error if I didn't inherit from RenderModel.
The attributes didn't seem to fix anything either.
I'm actually receiving an empty model, rather than the error mentioned above, but it's stopped working after the update. I'm wondering if it's a slightly different version of the bug.
Maybe you can show us some code as I don't really understand what you are doing and what you have tried.
I've got quite a lot of complex code going on, but here's a similar version of the code
Before I updated model, would either by an empty
MyViewModel
, or contain the posted data. Now it's always null.RenderMvcControllers
are not intended to be POSTed to, that is whatSurfaceController
s are for, but i guess everyone does all sorts of zany things with Umbraco.In any case, why do you need to POST the whole RenderModel? What is this Action actually doing? What was your code before (i.e. when it was working)?
You said you tried this.... is this actually what you tried while keeping your model the same as it was before?
Can you remove the "Surface" from the SurfaceController URL? So I can keep a nicely formatted URL for users.
But ideally, yes, I do need to post the whole model, or a model of somekind, there are about 15+ properties, of many different types.
I'd stupidly put
[ModelBinder(typeof(DefaultModelBinder))]
as an attribute on the class, not the paramater. My mistake. Applying it to the attribute does work. Thank you!Please see these docs on creating forms:
https://our.umbraco.org/Documentation/Reference/Templating/Mvc/Forms
Part of the point of a SurfaceController is that is routed nicely (the same URL), it does NOT route to the /umbraco/surface URL. Please try it and you'll see. It all works by using
BeginUmbracForm
Thanks for the pointers, Shannon. I shall definitely be changing my code in the future, then - good to know.
Perhaps some docs/tutorials on the interaction of Route Hijacking and SurfaceControllers would be useful? (I knows you guys are busy, of course...)
Have a nice Easter
This is quite an oversight.
Thanks to that blog post by Sebastian, I have no doubt that many people have run into this issue. In order to prevent other developers from the headache I just went through trying to find this thread, this NEEDS to be listed as a breaking change in the release notes for 7.4.2. Especially since what I thought was going to be an easy update turned out to be a pain.
Someone over at Umbraco HQ needs to work on quality control 'cuz this shit is bananas.
Hi there, I wonder if you could help me. I'm new to Umbraco and .net MVC, but was following a tutorial to set up my own route hijacking controller. I ended here up because I was getting the same initial error.
I reworked my code as Shannon suggested, ie:
Except now I get a compilation error in my view:
at this line:
How do I tell my View to expect an RssFeedModel, and not a standard RenderModel? My RssFeedModel is based on RenderModel:
Thanks in advance for any advice.
Andy
Upgrading to the nightly (v7.4.3build3482) has fixed the issue for me, though it does introduce a ReflectionTypeLoadException in the Developer section of the backoffice. I get this exception on the call for GetNonCoreTypesAssignableFrom.
Hi @Matthew & @Andy,
To fix this you can use the workaround posted above, it will also continue to work after you upgrade because the fix is to ensure that the
DefaultModelBinder
continues to execute for any sub classes ofRenderModel
. See above: https://our.umbraco.org/forum/using-umbraco-and-getting-started/75998-update-to-742-all-custom-hijacked-controllers-stopped-working#comment-243220You can attribute your parameter with the specific model binder:
Maybe someone can mark that as fixed so it's obvious what the work around is.
@Matthew if you have steps to reproduce the other error you are getting with the nightly can you please open a ticket with details and steps to reproduce. I haven't seen this error before.
Done deal: http://issues.umbraco.org/issue/U4-8282
Hi @Shannon, thanks the speedy reply.
My controller now looks like this:
But I still get the compile error
Any thoughts?
Cheers Andy
Hi Andy, Can you please advise that this worked before or are you just having a new issue?
First thing I want to mention is that you should really not be using
RenderModel
for this purpose.IPublishedContent
is the real model you should be using to display data that comes from it. In v8 we won't even haveRenderModel
everything will beIPublishedContent
,RenderModel
was only ever invented just in case we had additional model information that we needed to pass to a view outside of the data contained inIPublishedContent
and we've determined that will never happen since there's never been a need in over 2 years. This is actually the first time I've seen people sub classingRenderModel
, i didn't know people were actually doing this ... had I known that was going to happen we would have made RenderModel sealed since it's not exactly what we intended by design.In your case you could have a custom implementation of
IPublishedContent
called RssFeedContent that inherits fromPublishedContentWrapped
and you pass in the currentIPublishedContent
instance to its ctor. Then you can add your RssFeed property to this class... which you could also make with a backing field so that it's lazily resolved and only resolved once. Or you could just as easily make aGetRssFeedItems
extension method onIPublishedContent
Of course if this used to work then we need to fix it - which should be fixed in 7.4.3 which will be out soon
Hi Shannon,
I'm surprised this is the first time you've seen this. I thought it was commonplace for people to subclass
RenderModel
in order to pass data from their custom controllers to their views. This approach is even suggested in the Umbraco documentation.What do you consider best practice for passing data from custom controllers to views?
That is not the problem being discussed on this thread. You can subclass RenderModel all you want to pass to your views, you can create and use any model that you want to pass to your views.... The problem listed here is that people are subclassing RenderModel to model bind to controller actions.
We broke that by accident because that isn't a documented practice and not something that I'd recommend. It will work again with the latest release but it sort of works by fluke.
Ah OK, my mistake. Yes, I thought that was a little weird as well.
Still interesting to hear that the
RenderModel
is going to go in v8 though. What will be the alternative approach?Hi there,
No this is brand new code - well, new for me, anyway. Actually I was following this tutorial
https://cultiv.nl/blog/whats-this-umbraco-route-hijacking-all-aboutl/
which is by the same guy as that other blog post reference.
I'm going to have to have a sit down with a cup of coffee to work out how I should be using IPublishedContent. Any examples anywhere I can look at?
Thanks again.
This guy.. ;-) has updated his blog post (the one on umbraco.com was a crossposted copy) and the related gist to make sure all code examples are now correct.
Since this is brand new code, if you use the technique outlined in Sebastiaan's updated code and then ensure that you set the correct Model on your view
@inherits UmbracoViewPage<BlogOverview>
it should work... pretty sure that is the part you are missing.Thanks @Shannon and @Sebastiaan. I'll have a look into that.
Sorry, @Sebastiaan I hadn't spotted out you were "the guy" who had written both posts. :-)
I guess if RenderModel is going away in v8 I should look at the IPublishedContent solution anyway, but for the meantime I'll try this. What's the worst that could happen - I build it, it goes live, and we forget all about it...
Cheers Andy
Sorry, me again. Have confirmed my code now matches Sebastiaan's, and the line that's causing the problem is this one in the view:
(apols to everyone if that's blindingly obvious). I've renamed my model from RssFeedModel to just RssFeed, in case there was any dependency on the name like there is with controller, but that made no difference.
With the above line in my view I get this error message:
Without it (and of course without trying to use anything in the model), a simple page will render.
Sorry, meant to come back to this and admit humbly that I'd not spotted that my controller name didn't match with the document type name. Schoolboy error!
@Sebastiaan With your updated code in the blog post and the related gist I fail to see how paging can work
As you're creating a new BlogOverview, Page is always going to be 0.
The view is passing the page number in the query string and this was previously automatically bound to the model.
Sure, just use
Request.Querystring["Page"]
for now, I don't know of a better way quickly to fix this some other way.In MVC & WebApi, query strings are bound to action parameters... that is the very nature of how MVC binds things. You can just add a
page
parameter to your action. For example, Articulate does paging, it's just ap
querystring which gets mapped to an action's parameter:https://github.com/Shazwazza/Articulate/blob/master/src/Articulate/Controllers/ArticulateTagsController.cs#L47
I always suggest doing some very simple MVC sites and tutorials outside of Umbraco since all of this stuff is covered and is quite a fundamental part of MVC.
Thanks Shannon, I was pointing out that the updated blog post / gist are not correct as stated above.
Ideally they need updated again so that paging still works i.e. something like :-
I'm glad I stumbled on this as I was fumbling around for hours trying to figure out what should be relatively simple.
Coming from a vanilla MVC world, it's very common:
So given that the Umbraco docs tell us to subclass RenderModel for returning custom views, why wouldn't we then proceed to just post data back and rely on modelbinding to do the heavy lifting?
And yes, the example posted is clearly wrong because there's no model binding going on anywhere in there and so it never actually reads the page parameter. Having to manually bind model parameters is just... ew.
Now that I see we're supposed to use Surface controllers for posting data, and there's a whole Html.BeginUmbracoForm, I will have to adjust all my code, but man, if the documentation and all the code examples for RenderModel would just say "GET ONLY, NOT FOR POSTING, SEE ..." you might be able to avoid some of this confusion in the future.
As this thread is quite long can you please answer the following:
Can you please link to what is wrong so it can be fixed?
Can you please link to the documentation you think should be updated please - or create a task here: https://github.com/umbraco/umbracodocs/issues
On https://umbraco.com/follow-us/blog-archive/2014/1/27/a-practical-example-of-route-hijacking-in-umbraco/
The last code block has a controller. This controller method never binds the input search parameter. A possible fix is this (changed method signature, added line to assign page; although I'd probably make a new constructor for the model if I was doing this from scratch). I also then removed the assignment that checks for page 0, since the default parameter handles it.
If I have time later, I'll submit a bug on the documentation, but as is, I'm behind several hours from trying to figure out how to make a simple GET-POST-REDIRECT custom search form.
Thanks for the links.
I'm assuming you've seen these docs on forms - there's some simple tutorial links there: https://our.umbraco.org/Documentation/Reference/Templating/Mvc/Forms
I saw it eventually; it just wasn't the first things I found in a search.
What I really want to see is documentation on how to use a custom model with model binding in a GET scenario. I shouldn't have to POST for a search form (read operations should use GET); and I shouldn't have to manually wire up all the complex search parameters that I have.
I just finished writing some very basic code, and I'm running into trouble where something is not model binding correctly.
I took a simple
View (called correctly on GET)
And I can put a breakpoint on DoSearch(); it does get routed there, but the search term is not bound correctly. It's always null, so ModelState.IsValid is false. Request.Form["SearchTerm"] has the correct value; so it's just model binding that's failing. Aggravating.
So you are doing the thing that this whole thread is about, you are sub-classing
RenderModel
and then trying to model bind it on the server. This will work in some cases but it's just not intended. If you are POSTing, then use a small custom model and post what you need. If you are GETing, then just get normally with query strings. In any case, I'm pretty sure your model binding isn't working about because you have a property calledKeyword
on your model and you are passing up a property from some model calledSearchTerm
.What you want to do is quite simple - you really just want to get query strings to your page. You can just use normal html for that, only thing is the action will depend on what url your search page is. If it's the current page you can just supply no action, otherwise supply the one you want
If your search page is a hijacked route page you can just add those query strings as parameters to your Action, MVC automatically binds query strings to action parameters, for example see here in Articulate source: https://github.com/Shazwazza/Articulate/blob/master/src/Articulate/Controllers/ArticulateSearchController.cs#L53
It's also worth noting that
BeginUmbracoForm
has lots of overloads, just like the normal BeginForm and contains aFormMethod method
so you can give it a GETThe keyword/searchTerm was just copy & paste laziness; I was trying to abstract out my code and make it as simple as possible, forgot to change one example.
I know how to write basic HTML forms and MVC forms; I have 6+ years of MVC development, so it's not my first rodeo. I don't want to have to put in 10 extra parameters to a method. That's why we have models and model binding!
If I want to make a search form with 10 different ways to search, I should be able to just model-bind, and to write a good Razor form, I want to use Razor helpers like LabelFor and TextBoxFor, so that I have proper intellisense and so that they will model-bind when I submit the form to my handler (which should be agnostic whether it's GET or POST).
Yes, you can add 10 different parameters to the ActionMethod, but that's very bad MVC practice! What if you add a new parameter to your model? Now you have to update the ActionMethod. Normally you'd just update the service layer / function call that's doing something with the data, but now you have to tell the controller to expect new data... ugly.
Hi, some things to note, firstly yes, we need to get some docs up on GET forms. I'll make a note here for now: https://github.com/umbraco/UmbracoDocs/issues/336
Coming from an MVC background i realize that it might seem strange that by default you have this
RenderModel
bound to a GET request as a parameter when using a custom controller for an umbraco page (hijacked routes). The reason for this is because it's just easier for the majority of users (which are generally not MVC devs) to use that parameter since its the standard data source/model for the page.In a normal MVC scenario (as you mentioned above) you have GET action that creates a model - which is normally based on a data source. In Umbraco, the page data source is
RenderModel
or more accuratelyIPublishedContent
. For simplicity this data source is model bound (custom binder) as an action parameter. This is totally optional.Your custom controller action could just look like this and instead of having RenderModel as an action parameter you can create your model based on the umbraco page data source.
In this scenario the base class exposes
CurrentPage
(the instance ofIPublishedContent
) as the data source for the page. You can use this data source to build up any model you want to pass to your view.Regarding model binding a search model, i mentioned query strings and individual parameters because that's simple for a single search field. If you want to bind a complex model, you certainly can. Whether you want your data source (RenderModel) model bound as a parameter or if you just want to use CurrentPage as your data source you can do both and it will bind:
This would be based on GET requests to the current page without using a
SurfaceController
. But you could also use a SurfaceController which could make sense if you wanted to have this possible with both a POST (+ validation) and a GET. For example, you could do this:in conjunction with a partial view like:
and the search model would still bind to either of these action definitions for your hijacked controller:
Hi, Sorry about posting on that again. I just need to have a custom Model to fill up the page with more data. Not for passing data back to the controller. I try to be "more umbraco way" and follow Shannon post.
I finally get: Controller:
Model
And in my page:
@using umbLVC2.models @inherits Umbraco.Web.Mvc.UmbracoViewPage
My umbraco version is at 7.3.1, but I still get the error: Cannot bind source type Umbraco.Web.Models.RenderModel to model type umbLVC2.models.StoreModel
I am lost... ;-) Any suggestion?
HO! I found it. I need to change the action in the custom controller from:
to:
Now that work. It is a "normal" way to achieve that?
Hi, if you want to use your custom
StoreModel
then you'll want to use that model in your view, currently your view is:Which is not using your StoreModel at all, you'd want
The docs for this is here: https://our.umbraco.org/Documentation/Reference/Routing/custom-controllers
If you want to have a custom Action for a specific template, then Action Name --> Template Name
Otherwise if you just want one Action to execute for all templates for that doc Type, then
Index
is what you wantIf you want to read further about the many ways of having custom models, here's the Options: http://issues.umbraco.org/issue/U4-8357#comment=67-28909
I need to get these things in the docs but this just emphasizes that if you want to work with totally custom models, bound action parameters, etc... you certainly can.
Hello,
I've been stuck the entire day on the same problem. I get that this isn't the correct way of using RenderModel, all I want to do is to insert dynamic data from my model into the view.
ATM I have thos code:
Model:
Controller:
View:
Can anyone help me? Thanks in advance.
Hi BartKrul,
That looks fine to me. What is the problem you're having?
You should be able to access your
TestString
property in the view as follows:@Model.Content
will be your current content as usual.Hi Steve,
I can't even try to acces TestString. The problem I got was the exact same one as this thread is about: Cannot bind source type Umbraco.Web.Models.RenderModel to model type .....
I switched to doing everything with AJAX now and not using a seperate controller for the page.
It's sort of confusing as the official umbraco page has documentation about how to implement this (https://our.umbraco.org/documentation/reference/routing/custom-controllers). But here I'm reading RenderModel isn't supposed to be used in this way.
The problem you are having is most likely because you have different views and layouts defined with different models.
Your view is declared with:
which means it's
@Model
=VerzoekIndienenModel
What is your Master.cshtml declared as?
If it's forms you are having issues with (i.e. you've switched to AJAX), you can follow these tutorials to get a form working: https://our.umbraco.org/documentation/Reference/Templating/Mvc/forms
Hey, I have the problem that I'm calling a partial view
and additionally I have a hijacked controller
and I call it like this
The Controller is never called !!!
Html.Partial does not execute any controllers, it just renders a partial view.
So Route hijacking is not possible for Partial Views ?
Route hijacking has nothing to do with Partial Views https://our.umbraco.org/documentation/reference/routing/custom-controllers - that is for dealing with a request to a content item.
Partial Views are purely based an ASP.NET MVC and Umbraco doesn't have anything to do with that pipeline.
https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial
If you want to use a controller for a Partial View then you would use a Child Action, which is also just an ASP.NET MVC process (not directly related to Umbraco)
http://stackoverflow.com/questions/12530016/what-is-an-mvc-child-action
is working on a reply...