官术网_书友最值得收藏!

Using view engines

It was mentioned at the start of the chapter that ASP.NET Core only includes one view engine, Razor, but nothing prevents us from adding more. This can be achieved through the ViewEngines collection of MvcViewOptions, as illustrated in the following code snippet:

services
.AddMvc()
.AddViewOptions(options =>
{
options.ViewEngines.Add(new CustomViewEngine());
});

A view engine is an implementation of IViewEngine, and the only included implementation is RazorViewEngine.

Again, view engines are searched sequentially when ASP.NET Core is asked to render a view and the first one that returns one is the one that is used. The only two methods defined by IViewEngine are as follows:

  • FindView (ViewEngineResult): Tries to find a view from ActionContext
  • GetView(ViewEngineResult): Tries to find a view from a path

Both methods return null if no view is found.

A view is an implementation of IView, and the ones returned by RazorViewEngine are all RazorView. The only notable method in the IView contract is RenderAsync, which is the one responsible for actually rendering a view from ViewContext.

A view engine is not an easy task. You can find a sample implementation written by Dave Paquette in a blog post at: http://www.davepaquette.com/archive/2016/11/22/creating-a-new-view-engine-in-asp-net-core.aspx.

A Razor view is a template composed essentially of HTML, but it also accepts fragments—which can be quite large, actually—of server-side C# code. Consider the requirements for it, as follows:

  • First, you may need to define the type of model that your view receives from the controller. By default, it is dynamic, but you can change it with a @model directive, like this:
@model MyNamespace.MyCustomModel
  • Doing this is exactly the same as specifying the base class of your view. This is accomplished by using @inherits, like this:
@inherits RazorPage<MyNamespace.MyCustomModel>
Remember: the default is RazorPage<dynamic>. Don't forget: you cannot have @inherits and @model at the same time with different types!
  • If you don't want to write the full type name, you can add as many @using declarations as you want, as illustrated in the following code snippet:
@using My.Namespace
@using My.Other.Namespace
  • You can intermix HTML with Razor expressions, which are processed on the server side. Razor expressions always start with the @ character. For example, if you want to output the currently logged-in user, you could write this:
User: @User.Identity.Name
  • You can output any method that returns either a string or an IHtmlContent directly, like this:
@Html.Raw(ViewBag.Message)
  • If you need to evaluate some simple code, you will need to include it inside parentheses, like this:
Last week: @(DateTime.Today - TimeSpan.FromDays(7))

Remember—if your expression has a space, you need to include it inside parentheses, the only exception being the await keyword, as illustrated in the following code snippet:

@await Component.InvokeAsync("Process");
  • You can encode HTML (implicitly using the HtmlEncoder instance supplied in the HtmlEncoder property), like this:
@("<span>Hello, World</span>")

This will output an HTML-encoded string, as illustrated in the following code snippet:

&lt;span&gt;Hello, World&lt;/span&gt;

More complex expressions, such as the definition of variables, setting values to properties, or calling of methods that do not return a stringy result (string, IHtmlContent) need to go in a special block, in which you can put pretty much anything you would like in a .NET method, as illustrated in the following code snippet:

@{
var user = @User.Identity.Name;
OutputUser(user);
Layout = "Master";
}
Sentences inside @{} blocks need to be separated by semicolons.

A variable defined in this way can be used in any other place in the view—after the declaration, of course.

Let's look at conditionals (if, else if, else and switch) now, which are nothing special. Have a look at the following code snippet:

//check if the user issuing the current request is authenticated somehow
@if (this.User.Identity.IsAuthenticated)
{
<p>Logged in</p>
}
else
{
<p>Not logged in</p>
}

//check the authentication type for the current user
@switch (this.User.Identity.AuthenticationType)
{
case "Claims":
<p>Logged in</p>
break;

case null:
<p>Not logged in</p>
break;
}

The first condition checks whether the current user is authenticated, and displays an HTML block accordingly. On the second, we have a switch instruction, on which we can specify multiple possible values; in this case, we are only looking at two, "Claims" and null, which essentially yields the same result as the first condition.

Loops use a special syntax, where you can mix together HTML (any valid Extensible Markup Language (XML) element) and code, as illustrated in the following code snippet:

@for (var i = 0; i < 10; i++)
{
<p>Number: @i</p>
}

Note that this will not work because Number is not included inside an XML element, as illustrated in the following code snippet:

@for (var i = 0; i < 10; i++)
{
Number: @i
}

But the following syntax (@:) would work:

@:Number: @i

This makes the rest of the line be treated as an HTML chunk.

The same syntax can be used in foreach and while.

Now, let's have a look at try/catch blocks, shown in the following code snippet:

@try
{
SomeMethodCall();
}
catch (Exception ex)
{
<p class="error">An error occurred: @ex.Message</p>
Log(ex);
}

Consider the @using and @lock blocks shown in the following code snippet:

@using (Html.BeginForm())
{
//the result is disposed at the end of the block
}

@lock (SyncRoot)
{
//synchronized block
}

Now, what if you want to output the @ character? You need to escape it with another @, like this:

<p>Please enter your username @@domain.com</p>

But Razor views recognize emails and do not force them to be encoded, as illustrated in the following code snippet:

<input type="email" name="email" value="nobody@domain.com"/>

Finally, comments—single or multiline—are also supported, as illustrated in the following code snippet:

@*this is a single-line Razor comment*@
@*
this
is a multi-line
Razor comment
*@

Inside a @{} block, you can add C# comments too, as illustrated in the following code snippet:

@{
//this is a single-line C# comment
/*
this
is a multi-line
C# comment
*/
}

Of course, because a view is essentially HTML, you can also use HTML comments, as illustrated in the following code snippet:

<!-- this is an HTML comment -->
The difference between C#, Razor, and HTML comments is that only HTML comments are left by the Razor compilation process; the others are discarded.

We can add functions (which are actually, in object-oriented terminology, methods) to our Razor views; these are just .NET methods that are only visible in the scope of a view. To create them, we need to group them inside a @functions directive, like this:

@functions
{
int Count(int a, int b) { return a + b; }

public T GetValueOrDefault<T>(T item) where T : class, new()
{
return item ?? new T();
}
}

It is possible to specify visibility. By default, this happens inside a class, which is called a private class. It is probably pointless to specify visibility since the generated class is only known at runtime, and there is no easy way to access it.

The @functions name is actually slightly misleading, as you can declare fields and properties inside of it, as can be seen in the following code block:

@functions
{
int? _state;
int State
{
get
{
if (_state == null)
{
_state = 10;
}
return _state;
}
}
}

This example shows a simple private field that is encapsulated behind a property that has some logic behind it: the first time it is accessed, it sets the field to a default value; otherwise, it just returns what the current value is.

主站蜘蛛池模板: 泸西县| 成都市| 即墨市| 昌乐县| 神池县| 白玉县| 星座| 抚顺市| 驻马店市| 根河市| 玉环县| 余姚市| 定南县| 遵义市| 蓬安县| 炎陵县| 泉州市| 舒城县| 彭阳县| 仁怀市| 芦溪县| 屏边| 达州市| 岑溪市| 和静县| 万山特区| 阳东县| 怀宁县| 阿巴嘎旗| 苏尼特左旗| 庆元县| 孟村| 商水县| 玉屏| 钦州市| 平凉市| 霞浦县| 永登县| 岢岚县| 横峰县| 贵州省|