Newly created and published page gives 404?

Vote:
 

I created a new HelloWorld page type in alloy, then edited Admin->content type to allow this new page to appear under the root page.

I create the page, and published it.  The page has "Name in URL" of helloworld.  So I am guessing the URL I should use to "see" the page is:

http://localhost:60746/helloworld

Where http://localhost:60746/ is the working local alloy site.

http://localhost:60746/helloworld gives me the IIS 404 page.

Detailed Error Information:

Module IIS Web Core
Notification MapRequestHandler
Handler StaticFile
Error Code 0x80070002
Requested URL http://localhost:60746/helloworld
Physical Path D:\dev\epi\DxcAlloy\helloworld
Logon Method ApplicationCookie
Logon User Alice

http://localhost:60746/en/helloworld gives me a .net error page: "Server Error in '/' Application.  The resource cannot be found"

looking in App Data/EPiServerErrors.log, the only thing i see is the bellow error. If I search for "your_uri" in the project source, it doesnt find it.

2020-06-22 09:36:29,145 [16] ERROR EPiServer.Find.Cms.ContentEventIndexer: An exception occurred while indexing (IContent). The remote name could not be resolved: 'your_uri'.
System.Net.WebException: The remote name could not be resolved: 'your_uri'
at System.Net.HttpWebRequest.GetRequestStream(TransportContext& context)
at System.Net.HttpWebRequest.GetRequestStream()
at EPiServer.Find.Connection.JsonRequest.GetRequestStream(Int64 contentLength)
at EPiServer.Find.Json.Serializer.SerializeObjectsToJsonRequest(JsonSerializer serializer, IJsonRequest jsonRequest, IEnumerable values)
at EPiServer.Find.Api.BulkCommand.Execute(List`1& serializationFailures)
at EPiServer.Find.Api.BulkCommand.Execute()
at EPiServer.Find.Client.Index(IEnumerable objectsToIndex, Boolean deleteLanguageRoutingDuplicatesOnIndex)
at EPiServer.Find.Cms.ContentIndexer.IndexWithRetry(IContent[] contents, Int32 maxRetries, Boolean deleteLanguageRoutingDuplicatesOnIndex)
at EPiServer.Find.Cms.ContentIndexer.Index(IEnumerable`1 contents, IndexOptions options)
at EPiServer.Find.Cms.ContentEventIndexer.Index(IEnumerable`1 contentLinks)
at EPiServer.Find.Cms.ContentEventIndexer.<>c__DisplayClass25_0.<IndexPageQueue>b__0()
at EPiServer.Find.Cms.ContentEventIndexer.Sync(IEnumerable`1 contentToIndex, HashSet`1 contentToIndexSet, Func`1 index)
at EPiServer.Find.Cms.ContentEventIndexer.IndexPageQueue(IEnumerable`1 contentToIndex)
at EPiServer.Find.Cms.ContentEventIndexer.IndexPageQueue()

This is the page:

namespace DxcAlloy.Models.Pages
{
[ContentType(DisplayName = "HelloWorld", GUID = "5e4176f9-613d-4c9e-9c85-37dd1d1d70d9", Description = "")]
public class HelloWorld : PageData
{

[Display(
GroupName = SystemTabNames.Content,
Order = 10)]
public virtual ContentArea ItemLiistContentArea { get; set; }
}
}

#224517
Jun 22, 2020 7:55
Vote:
 

Hi,

As a thought, did you create anything to render the page type (e.g. a controller)?

#224519
Jun 22, 2020 8:22
Vote:
 

I noticed that blocks dont need a controller, so was tyring a page without a controller as I saw this approach on a few articles.

I did try creating a view in Views/HelloWorld/index.cshtml like this:

@model DxcAlloy.Models.Pages.HelloWorld

<div>
Hello!
</div>

But this did not help.

What should I put in a controller as bare minimum?

Inerestingly, when I right click on Controllers and select new item->Episerver, There is no page controller. only "block type", "block controller (MVC)", "Page Type" etc.  A long list which is only missing "Page Controller (MVC)" or anything like it.  Block controller is listed 4 times for some strange reason.

I manually created a class in Controllers called "HelloWorldController.cs" like this:

namespace DxcAlloy.Controllers
{
public class HelloWorldController:PageController<HelloWorld>
{
public ActionResult Index(HelloWorld currentPage)
{
return View(currentPage);
}
}
}

Now I get this:

The model item passed into the dictionary is of type 'Castle.Proxies.HelloWorldProxy', but this dictionary requires a model item of type 'DxcAlloy.Models.ViewModels.IPageViewModel`1[DxcAlloy.Models.Pages.SitePageData]'.

#224520
Edited, Jun 22, 2020 9:15
Vote:
 

Hi,

In the alloy site there is a fallback controller setup which will act as the controller for pages which don't have a controller defined so that's probably why you've seen page types without a controller which render correctly in alloy. That fallback controller only kicks in for pages which inherit SitePageData though and your page inherits PageData instead hence it not being handled in the same way.

I suspect the issue you're seeing with the model type will be related to your layout file. The default layout in alloy expects a model of type IPageViewModel<SitePageData> and you're passing in just the HelloWorld page instance as a model. If you want to use the alloy layout file, you'll need to construct an instance if IPageViewModel in your controller and pass that as the model to your view. Bear in mind though that IPageViewModel also expects a page type which inherits from SitePageData which yours doesn't. The alternative, if you don't want to use the alloy layout file, is to specify the layout file in your view either as null to have no layout or pointed to a valid layout file which can handle a model of type HelloWorld.

The best option for you will depend on what you're trying to achieve but I'd generally recommend having a base type in your solution which your pages inherit (such as SitePageData in Alloy) and a viewmodel passed to your views rather than just the PageData object. In the case of the base type, that will allow you to set up common properties such as meta tags which all of your page types will need. Using a viewmodel rather than just the PageData allows you to pass additional data such as navigation alongside your page content which would otherwise need to be fetched elsewhere.

#224524
Jun 22, 2020 11:48
Vote:
 

Thanks! how do I create a hello world layout?  i.e. not use the default alloy one?

#224525
Jun 22, 2020 11:59
Vote:
 

At it's most basic you could just create a cshtml file which looks something like this:

<!doctype html>
<html lang="en">
<head>
</head>
<body>
    @RenderBody()
</body>
</html>

The important bit being the "@RenderBody()" as that's where the template markup gets inserted. You may also want to include the following line in there as it gives you the handy orange CMS navigation which appears when you're logged in to the CMS:

@Html.RenderEPiServerQuickNavigator()

Then, at the top of your actual view for your page, reference the layout you want to use like this:

@{
    Layout = "~/Views/Path/To/My/Layouts/_MyLayout.cshtml";
}
#224527
Edited, Jun 22, 2020 12:18
johnv - Jun 22, 2020 21:51
Awesome. I see that _viewstart.cshtml has this:
@{
Layout = "~/Views/Shared/Layouts/_Root.cshtml";
}
My understanding of _viewstart is that this is called at the start of all views, so it should have been picked up by my page view. and my page should have been rendered with the _Root layout, and if I define a new layout at the top of my page view as you suggest (this is what I want), it would be ignored because it has already called the root layout from _viewstart?

Seems to work though!

Paul Gruffydd - Jun 23, 2020 8:05
_viewstart is the default layout. If you don't explicitly set a layout but do have a _viewstart.cshtml in the right location in your site, _viewstart will be used. If you do set a layout in your view, the layout you've defined will be used rather than _viewstart. If you set the layout in your view to null, no layout will be applied.
Vote:
 

Wouldn't it be easier to inherit SitePageData (not PageData) instead of creating a separate layout view?
Unless you actually want a different layout, of course.

#224568
Jun 22, 2020 18:49
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.