The ASP.NET Core version of the template is the current recommended version for new applications on the website.
For an ASP.NET Core MVC project, you can install our design using the following steps:
-
In Visual Studio create a new ASP.NET Core Web Application using the "Empty" project template.
-
Install
Escc.EastSussexGovUK.Core
from our private NuGet feed. -
Add the services required for the template to the
Startup
class in the order shown below:using Escc.EastSussexGovUK.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddEastSussexGovUK(Configuration); } ... public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseEastSussexGovUK(env); app.UseStaticFiles(); app.UseMvc(); } }
-
Create a view model which inherits from
Escc.EastSussexGovUK.Core.BaseViewModel
. Its constructor must inject an instance ofIViewModelDefaultValuesProvider
since this is required to build the template.using Escc.EastSussexGovUK.Core; public class MyCustomModel : BaseViewModel { public MyCustomModel(IViewModelDefaultValuesProvider defaultValues): base(defaultValues) { } }
-
Add a controller. The constructor must inject the resources required for the website template. Any controller action which results in a view using the website template must be marked with the
async
keyword and returnTask<IActionResult>
. It must also load the template HTML, and usually web chat too, using the following code. You can put this in a base controller class if you have many controllers in your application.Add one or more routes, either using attribute routing as shown here or as part of the call to
app.UseMvc();
in theStartup
class.using System; using System.Threading.Tasks; using Escc.EastSussexGovUK.Core; using Escc.EastSussexGovUK.Features; using Exceptionless; using Microsoft.AspNetCore.Mvc; public class ExampleController : Controller { private readonly IEastSussexGovUKTemplateRequest _templateRequest; private readonly IViewModelDefaultValuesProvider _defaultModelValues; public ExampleController(IEastSussexGovUKTemplateRequest templateRequest, IViewModelDefaultValuesProvider defaultModelValues) { _templateRequest = templateRequest; _defaultModelValues = defaultModelValues; } [Route("")] public async Task<IActionResult> Index() { var model = new MyCustomModel(_defaultModelValues); // inheriting from BaseViewModel try { // Do this if you want your page to support web chat. It should, unless you have a reason not to. model.WebChat = await _templateRequest.RequestWebChatSettingsAsync(); } catch (Exception ex) { // Catch and report exceptions - don't throw them and cause the page to fail ex.ToExceptionless().Submit(); } try { // Do this to load the template controls. model.TemplateHtml = await _templateRequest.RequestTemplateHtmlAsync(); } catch (Exception ex) { // Catch and report exceptions - don't throw them and cause the page to fail ex.ToExceptionless().Submit(); } return View(model); } }
-
Add a
~/Views/_ViewImports.cshtml
file with the following contents. This makes tag helpers in your views work.@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
You may want to add
@using Escc.MyApplication
in here too, whereEscc.MyApplication
is the default namespace of your application. -
Add a view. The main content of your page should usually use the standard text styles. See the website style guide for a full guide.
<div class="full-page"> <div class="text-content content"> <h1>Example page</h1> <p>Page text goes here.</p> </div> </div>
In addition there are a number of sections you can override to add your own content at specific places within the template.
Add content in the
<head>
– useful for metadata and stylesheets:@section Metadata { <meta name="robots" content="noindex, nofollow" /> <link rel="stylesheet" href="~/css/[email protected]" /> }
Replace the site header:
@section Header { <header>My custom header</header> }
Replace the breadcrumb trail:
@section Breadcrumb { <p>My custom breadcrumb</p> }
Replace the site footer:
@section Footer { <footer>My custom footer</footer> }
Include JavaScript at the end of the page (or use the
async
ordefer
attributes when loading it earlier):@section JavaScript { <script src="~/js/[email protected]"></script> }
-
Add configuration for the template elements and exception handling. For example, if you are using
appsettings.json
for configuration:{ "Escc.EastSussexGovUK": { "BreadcrumbTrail": [ { "Name": "Home", "Url": "https://www.eastsussex.gov.uk/" }, { "Name": "Example section", "Url": "https://www.eastsussex.gov.uk/example/" } ], "Mvc": { "PartialViewUrl": "https://www.eastsussex.gov.uk/masterpages/remote/control.aspx?control={0}" }, "WebChat": { "WebChatSettingsUrl": "https://www.eastsussex.gov.uk/umbraco/api/WebChat/GetWebChatUrls" } }, "Exceptionless": { "ApiKey": "API_KEY_HERE", "ServerUrl": "https://hostname/" } }
If your application runs behind a proxy server you can configure the proxy URL and authentication details in using the settings documented in the Escc.Net project.
-
Run the project.
If you are using Visual Studio and targeting IIS for development, you'll need to update
~/Properties/launchSettings.json
which is configured for IIS Express by default. A minimallaunchSettings.json
for IIS looks like this:{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iis": { "applicationUrl": "https://localhost" } }, "profiles": { "IIS": { "commandName": "IIS", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "ancmHostingModel": "OutOfProcess" } } }
You can change
https://localhost
to any site or application URL in IIS. You can also change most of these settings inProject Properties > Debug
.IIS is set to run in
OutOfProcess
mode, meaning it acts as a reverse proxy to the built-in web server, Kestrel. Changing this toInProcess
should be more efficient but, at the time of writing, in development theASPNETCORE_ENVIRONMENT
setting automatically hasProduction;
prepended, which doesn’t pass theIHostingEnvironment.IsDevelopment()
test. UseInProcess
when deploying to production. -
Publish the project. Before you publish you need to add a
netcoreapp2.2 InProcess false ...<MvcRazorExcludeRefAssembliesFromPublish />
line to the.csproj
file, which is required because the EastSussexGovUK template includes embedded views. This causes arefs
folder to be included in the publish. Without this you will see the error "Cannot find compilation library location for package [package name]
".
The call to services.AddEastSussexGovUK(Configuration)
sets up:
- EmbeddedFileProviders so that views included in NuGet packages can be used (it's the only was to include files in a NuGet package built using .NET Core)
- dependency injection for the services used by the template, including calling
services.AddOptions()
to enable configuration - configuration for enforcing TLS using redirection to HTTPS and HSTS.
The call to app.UseEastSussexGovUK(env)
sets up:
- enables the TLS support configured earlier
- branded error pages
- the Content Security Policy (which can be customised - see Varying the Content Security Policy)
- other headers which make the site more secure
The call to await templateRequest.RequestTemplateHtmlAsync()
attempts to download the template controls from a remote URL set by the Escc.EastSussexGovUK:Mvc:PartialViewUrl
configuration setting. The template code will try to fetch HTML from that URL, passing a token for the control it wants instead of {0}
. It then populates a TemplateHtml
property on the model. The use of an asynchronous call is very important as it allows the site to scale successfully using this approach.
The fetched HTML is saved in a local cache so that it is not requested remotely every time. The cache expires after an hour by default, but this can be changed using the optional CacheMinutes
setting:
{
"Escc.EastSussexGovUK": {
"Mvc": {
"CacheMinutes": 60
}
}
}
There is a similar cache for the web chat settings, which also defaults to an hour and can be changed in configuration:
{
"Escc.EastSussexGovUK": {
"WebChat": {
"CacheMinutes": 60
}
}
}
Requesting any page with the querystring ?ForceCacheRefresh=1
will cause the cached template to be updated even if the cache has not expired.
Using HTTP2 there is no longer a need to bundle client-side files, so they should be included in the page individually. However it is still useful to include a version number so that the latest version is always loaded after each deployment.
@section Metadata {
<link rel="stylesheet" href="~/css/[email protected]" />
<script src="~/js/[email protected]" async="async"></script>
}
It is also still useful to minify files, and this can be done using YUI Compressor. See the YUICompressor.msbuild
file in Escc.EastSussexGovUK.TemplateSource
for an example of how to configure this. The process is triggered by a post-build action in the properties for that project.
Some CSS and JavaScript is used sitewide, managed in the Escc.EastSussexGovUK.TemplateSource
project. To use this you will usually want a different URL in development and production. While ASP.NET Core has tag helpers to do this, they still require the base URL to be in the view where it may be a problem if it's different for each developer. A better solution is to specify the base URL in configuration and use it as shown below.
@section Metadata {
<link rel="stylesheet" href="@Model.ClientFileBaseUrl/css/[email protected]" />
}
If you are using appsettings.json
for configuration:
{
"Escc.EastSussexGovUK": {
"Mvc": {
"ClientFileBaseUrl": "https://www.eastsussex.gov.uk/escc.eastsussexgovuk",
"ClientFileVersion": "1234"
}
}
}
Often you will want to use the standard media queries for our site, and these can easily be accessed from the view model:
@section Metadata {
<link rel="stylesheet" href="~/css/[email protected]" />
<link rel="stylesheet" href="~/css/[email protected]" media="@Model.MediaQueryMedium" />
<link rel="stylesheet" href="~/css/[email protected]" media="@Model.MediaQueryLarge" />
}
Pages on our site can have different designs applied. By default we use a responsive design ("desktop"), but there is also a full-width, minimal design ("fullscreen") which is suitable for applications like maps, and a "plain" design which can be used as an API to request just the content of a page. You can make dramatic changes to the layout of the site by updating the configuration to point to a different layout:
{
"Escc.EastSussexGovUK": {
"Mvc": {
"DesktopViewPath": "~/Views/_My_Custom_Desktop.cshtml",
"FullScreenViewPath": "~/Views/_My_Custom_FullScreen.cshtml",
"PlainViewPath": "~/Views/_My_Custom_Plain.cshtml"
}
}
}
MvcViewSelector
is called (assuming it is injected as IViewSelector
) in the constructor of EastSussexGovUKTemplateRequest
and in the ~/Views/_ViewStart.cshtml
file. It selects the layout based on a number of criteria, including the configuration settings mentioned above.
In an MVC application you can't see the ViewStart.cshtml
file or any of the layouts because they're implemented as embedded resources. You can create your own ViewStart.cshtml
if you need to add additional code, but you'll need to copy the call to MvcViewSelector into your new file to continue to apply the correct layout.
If you want to override the layout view for a specific page in an application, you can set the Layout
property in a view:
@{
Layout = "~/views/_eastsussexgovuk_fullscreen.cshtml";
}
If you want to override the layout view for a single request, you can specify a token in the querystring:
https://hostname/my-application/my-page?template=Plain
The parameter name defaults to template
but you can change it using the configuration below.
{
"Escc.EastSussexGovUK": {
"Mvc": {
"ViewParameterName": "custom"
}
}
}
The value (for example Plain
in ?template=Plain
) replaces the {0}
in {0}ViewPath
, and the result is used to look up the layout view path.
These can be applied on top of MVC layouts for smaller changes. A skin class inherits IEsccWebsiteSkin
and can specify JavaScript and CSS files to load, fonts from Google and a custom Content Security Policy. Loading custom scripts gives you a lot of flexibility to alter the design and behaviour of the page. One skin can inherit from another by inheriting its class, allowing subtle variations to be created without repeating the settings.
The skin also contains the rules for when to apply it which might, for example, be based on the URL.
It is then up to individual templates and pages to name the skins that they support using the SkinSelector
class from this project. For example, to allow either the ExampleSkin
or CoronerSkin
when their conditions are met, but default to the CustomerFocusSkin
when they are not, use the following command. The skins are tested in order and the first matching skin is applied.
Model.EsccWebsiteSkin = SkinSelector.SelectSkin(
new IEsccWebsiteSkin[]
{
new ExampleSkin(Request.Url),
new CoronerSkin(Request.Url)
},
new CustomerFocusSkin()
);
The Content Security Policy protects pages from attacks including XSS.
The default policy allows scripts, styles, images and objects from and connections to our own site, plus the use of https://ajax.googleapis.com
for script libraries (JQuery), Google Fonts, Google Analytics and Crazy Egg. It also allows resources from any port on localhost
when ASPNETCORE_ENVIRONMENT
is set to Development
.
Predefined policies including the default are maintained in a separate project, Escc.EastSussexGovUK.ContentSecurityPolicy
, so that the package can be updated by consuming applications without needing to update the whole website template.
Often applications will need to add additional rules to the standard Content Security Policy, and they can do this by providing an instance of CspOptions
during Startup
:
using PeterJuhasz.AspNetCore.Extensions.Security;
public class Startup
{
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseEastSussexGovUK(env, new CspOptions {
ImgSrc = CspDirective.Empty.AddSource("https://www.example.org")
});
app.UseStaticFiles();
app.UseMvc();
}
}
The most common cases are preconfigured. For example, to add support for displaying Google Maps and YouTube:
using PeterJuhasz.AspNetCore.Extensions.Security;
public class Startup
{
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseEastSussexGovUK(env, new CspOptions().AddGoogleMaps().AddYouTube());
app.UseStaticFiles();
app.UseMvc();
}
}
You can also add additional Content Security Policies from controllers and views by registering a ContentSecurityPolicyDependency
in an IClientDependencySet
. The .Alias
property should match the name of one of the preconfigured methods. For example, an alias of YouTube
causes the .AddYouTube()
method to be run.
Dependency class:
using Escc.EastSussexGovUK;
public class MyCustomDependency : IClientDependencySet
{
public IEnumerable<ContentSecurityPolicyDependency> RequiresContentSecurityPolicy()
{
return new ContentSecurityPolicyDependency[1]
{
new ContentSecurityPolicyDependency(){ Alias = "YouTube" }
};
}
...
}
View that registers the dependency:
@inject Escc.EastSussexGovUK.Core.IClientDependencySetEvaluator dependencySetEvaluator;
dependencySetEvaluator.EvaluateDependencySet(new MyCustomDependency());
An IClientDependencySet
is a set of Content Security Policy changes, CSS and JavaScript files that together allow a feature to be added to a page. It also includes a method of deciding whether that feature is appropriate for a given page. A skin is an example of an IClientDependencySet
.
Some features included with Escc.EastSussexGovUK
are examples of an IClientDependencySet
:
EmbeddedGoogleMaps
EmbeddedYouTubeVideos
WebChat
Loading resources using an IClientDependencySet
is supported by ClientDependencySetEvaluator
. For example, to embed YouTube videos in a view:
@inject Escc.EastSussexGovUK.Core.IClientDependencySetEvaluator dependencySetEvaluator;
dependencySetEvaluator.EvaluateDependencySet(new EmbeddedYouTubeVideos() { Html = new []
{
Model.MyRichTextField.ToString(),
Model.MyOtherRichTextField.ToString()
}});
CSS and JavaScripts are loaded from the Escc.EastSussexGovUK.TemplateSource project using the CssRelativeUrl
and JsRelativeUrl
properties of the dependency. Content Security Policies are loaded by matching the name of a predefined method in Escc.EastSussexGovUK.ContentSecurityPolicy.ContentSecurityPolicyExtensions
(see 'Varying the policy for a page' above).
Other examples of IClientDependencySet
are used internally by the partial views listed below.
MVC pages can load partial views to add the following features, which are used frequently throughout the site:
~/_EastSussexGovUK_Banners.cshtml
, to support sitewide banners (see Promotional banners)~/_EastSussexGovUK_EastSussex1Space.cshtml
, for an EastSussex1Space search widget~/_EastSussexGovUK_Escis.cshtml
, for an ESCIS search widget~/_EastSussexGovUK_Facebook.cshtml
, for a Facebook feed known as the 'Page Plugin'~/_EastSussexGovUK_Latest.cshtml
, to show a latest update on a page~/_EastSussexGovUK_Share.cshtml
, for social media sharing and comment on this page links~/_EastSussexGovUK_Twitter.cshtml
, for a Twitter feed
For example, to add an ESCIS search widget to your page:
// Controller
Model.ShowEscisWidget = true;
// View
<partial name="~/_EastSussexGovUK_Escis.cshtml" />