Nguon: Microsoft Learn · .NET 8.0

Tham chiếu cú pháp Razor cho ASP.NET Core

Nguồn: Razor syntax reference for ASP.NET Core

Bởi Rick Anderson, Taylor Mullen, và Dan Vicarel

Razor là một cú pháp markup (đánh dấu) để nhúng code .NET vào trong các trang web. Cú pháp Razor bao gồm Razor markup, C# và HTML. Các file chứa Razor thường có phần mở rộng .cshtml. Razor cũng được tìm thấy trong các file Razor component (.razor). Cú pháp Razor tương tự như các engine template (khuôn mẫu) của các framework JavaScript single-page application (SPA) khác nhau, như Angular, React, VueJs và Svelte.

Giới thiệu lập trình web ASP.NET sử dụng cú pháp Razor cung cấp nhiều ví dụ lập trình với cú pháp Razor. Mặc dù bài viết được viết cho ASP.NET thay vì ASP.NET Core, nhưng hầu hết các ví dụ đều áp dụng cho ASP.NET Core.

Render HTML

Ngôn ngữ mặc định của Razor là HTML. Render HTML từ Razor markup không khác gì render HTML từ một file HTML. Markup HTML trong các file Razor .cshtml được server render không thay đổi.

Cú pháp Razor

Razor hỗ trợ C# và sử dụng ký hiệu @ để chuyển đổi từ HTML sang C#. Razor đánh giá các biểu thức C# và render chúng trong output HTML.

Khi ký hiệu @ được theo sau bởi một từ khóa dành riêng của Razor, nó chuyển đổi sang markup đặc thù của Razor. Nếu không, nó chuyển đổi sang HTML thuần túy.

Để thoát ký hiệu @ trong Razor markup, sử dụng ký hiệu @ thứ hai:

razor
<p>@@Username</p>

Code được render trong HTML với một ký hiệu @ duy nhất:

html
<p>@Username</p>

Các thuộc tính HTML và nội dung chứa địa chỉ email không coi ký hiệu @ là ký tự chuyển đổi. Các địa chỉ email trong ví dụ sau không bị ảnh hưởng bởi quá trình phân tích cú pháp Razor:

razor
<a href="mailto:Support@contoso.com">Support@contoso.com</a>

Scalable Vector Graphics (SVG)

Các phần tử SVG foreignObject được hỗ trợ:

html
@{
    string message = "foreignObject example with Scalable Vector Graphics (SVG)";
}

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" 
        fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

Biểu thức Razor ngầm định (Implicit Razor expressions)

Biểu thức Razor ngầm định bắt đầu bằng @ theo sau là code C#:

razor
<p>@DateTime.Now</p>
<p>@DateTime.IsLeapYear(2016)</p>

Ngoại trừ từ khóa await của C#, các biểu thức ngầm định không được chứa khoảng trắng. Nếu câu lệnh C# có phần kết thúc rõ ràng, khoảng trắng có thể xen kẽ:

razor
<p>@await DoSomething("hello", "world")</p>

Biểu thức ngầm định không thể chứa generic (kiểu tổng quát) C#, vì các ký tự bên trong dấu ngoặc (<>) được diễn giải là thẻ HTML. Code sau không hợp lệ:

razor
<p>@GenericMethod<int>()</p>

Code trên tạo ra lỗi biên dịch tương tự một trong những lỗi sau:

Các lệnh gọi method generic phải được bọc trong một biểu thức Razor tường minh hoặc một code block Razor.

Biểu thức Razor tường minh (Explicit Razor expressions)

Biểu thức Razor tường minh bao gồm ký hiệu @ với dấu ngoặc đơn cân bằng. Để render thời gian của tuần trước, sử dụng Razor markup sau:

razor
<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>

Bất kỳ nội dung nào trong dấu ngoặc @() đều được đánh giá và render ra output.

Biểu thức ngầm định, được mô tả trong phần trước, thường không thể chứa khoảng trắng. Trong code sau, một tuần không bị trừ khỏi thời gian hiện tại:

cshtml
<p>Last week: @DateTime.Now - TimeSpan.FromDays(7)</p>

Code render HTML sau:

html
<p>Last week: 7/7/2016 4:39:52 PM - TimeSpan.FromDays(7)</p>

Biểu thức tường minh có thể được sử dụng để nối text với kết quả biểu thức:

razor
@{
    var joe = new Person("Joe", 33);
}

<p>Age@(joe.Age)</p>

Nếu không có biểu thức tường minh, <p>Age@joe.Age</p> được coi như là địa chỉ email, và <p>Age@joe.Age</p> được render. Khi viết dưới dạng biểu thức tường minh, <p>Age33</p> được render.

Biểu thức tường minh có thể được sử dụng để render output từ các method generic trong các file .cshtml:

razor
<p>@(GenericMethod<int>())</p>

Mã hóa biểu thức (Expression encoding)

Các biểu thức C# được đánh giá thành chuỗi đều được mã hóa HTML. Các biểu thức C# được đánh giá thành IHtmlContent được render trực tiếp qua IHtmlContent.WriteTo. Các biểu thức C# không được đánh giá thành IHtmlContent được chuyển đổi thành chuỗi bằng ToString và được mã hóa trước khi render.

razor
@("<span>Hello World</span>")

Code trên render HTML sau:

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

HTML được hiển thị trong trình duyệt dưới dạng văn bản thuần túy:

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

Output HtmlHelper.Raw không được mã hóa nhưng được render dưới dạng HTML markup.

Sử dụng HtmlHelper.Raw trên dữ liệu đầu vào chưa được làm sạch từ người dùng là một rủi ro bảo mật. Đầu vào của người dùng có thể chứa JavaScript độc hại hoặc các khai thác khác. Việc làm sạch đầu vào người dùng là khó khăn. Tránh sử dụng HtmlHelper.Raw với đầu vào người dùng.

razor
@Html.Raw("<span>Hello World</span>")

Code render HTML sau:

html
<span>Hello World</span>

Code block Razor

Code block Razor bắt đầu bằng @ và được bao bọc bởi {}. Không giống như biểu thức, code C# bên trong code block không được render. Code block và biểu thức trong một view chia sẻ cùng scope (phạm vi) và được định nghĩa theo thứ tự:

razor
@{
    var quote = "The future depends on what you do today. - Mahatma Gandhi";
}

<p>@quote</p>

@{
    quote = "Hate cannot drive out hate, only love can do that. - Martin Luther King, Jr.";
}

<p>@quote</p>

Code render HTML sau:

html
<p>The future depends on what you do today. - Mahatma Gandhi</p>
<p>Hate cannot drive out hate, only love can do that. - Martin Luther King, Jr.</p>

Trong code block, khai báo local functions (hàm cục bộ) với markup để phục vụ như các method template:

razor
@{
    void RenderName(string name)
    {
        <p>Name: <strong>@name</strong></p>
    }

    RenderName("Mahatma Gandhi");
    RenderName("Martin Luther King, Jr.");
}

Code render HTML sau:

html
<p>Name: <strong>Mahatma Gandhi</strong></p>
<p>Name: <strong>Martin Luther King, Jr.</strong></p>

Chuyển đổi ngầm định (Implicit transitions)

Ngôn ngữ mặc định trong code block là C#, nhưng Razor Page có thể chuyển đổi trở lại HTML:

razor
@{
    var inCSharp = true;
    <p>Now in HTML, was in C# @inCSharp</p>
}

Chuyển đổi được phân định tường minh (Explicit delimited transition)

Để định nghĩa một phần con của code block cần render HTML, hãy bao quanh các ký tự để render bằng thẻ Razor <text>:

razor
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    <text>Name: @person.Name</text>
}

Sử dụng cách tiếp cận này để render HTML không được bao quanh bởi thẻ HTML. Nếu không có thẻ HTML hoặc Razor, sẽ xảy ra lỗi runtime Razor.

Thẻ <text> hữu ích để kiểm soát khoảng trắng khi render nội dung:

Chuyển đổi dòng tường minh (Explicit line transition)

Để render phần còn lại của toàn bộ một dòng dưới dạng HTML bên trong code block, sử dụng cú pháp @::

razor
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    @:Name: @person.Name
}

Nếu không có @: trong code, sẽ tạo ra lỗi runtime Razor.

Render thuộc tính có điều kiện (Conditional attribute rendering)

Razor tự động bỏ qua các thuộc tính không cần thiết. Nếu giá trị truyền vào là null hoặc false, thuộc tính sẽ không được render.

Ví dụ, xem xét Razor markup sau:

razor
<div class="@false">False</div>
<div class="@null">Null</div>
<div class="@("")">Empty</div>
<div class="@("false")">False String</div>
<div class="@("active")">String</div>
<input type="checkbox" checked="@true" name="true" />
<input type="checkbox" checked="@false" name="false" />
<input type="checkbox" checked="@null" name="null" />

Razor markup trên tạo ra HTML sau:

html
<div>False</div>
<div>Null</div>
<div class="">Empty</div>
<div class="false">False String</div>
<div class="active">String</div>
<input type="checkbox" checked="checked" name="true">
<input type="checkbox" name="false">
<input type="checkbox" name="null">

Razor giữ lại các thuộc tính data- nếu giá trị của chúng là null hoặc false.

Xem xét Razor markup sau:

razor
<div data-id="@null" data-active="@false"></div>

Razor markup trên tạo ra HTML sau:

html
<div data-id="" data-active="False"></div>

Cấu trúc điều khiển (Control structures)

Cấu trúc điều khiển là phần mở rộng của code block. Tất cả các khía cạnh của code block (chuyển đổi sang markup, C# inline) cũng áp dụng cho các cấu trúc sau:

Điều kiện @if, else if, else, và @switch

@if kiểm soát khi nào code chạy:

razor
@if (value % 2 == 0)
{
    <p>The value was even.</p>
}

elseelse if không yêu cầu ký hiệu @:

razor
@if (value % 2 == 0)
{
    <p>The value was even.</p>
}
else if (value >= 1337)
{
    <p>The value is large.</p>
}
else
{
    <p>The value is odd and small.</p>
}

Markup sau đây cho thấy cách sử dụng lệnh switch:

razor
@switch (value)
{
    case 1:
        <p>The value is 1!</p>
        break;
    case 1337:
        <p>Your number is 1337!</p>
        break;
    default:
        <p>Your number wasn't 1 or 1337.</p>
        break;
}

Vòng lặp @for, @foreach, @while, và @do while

HTML template có thể được render với các câu lệnh điều khiển vòng lặp. Để render một danh sách người:

razor
@{
    var people = new Person[]
    {
          new Person("Weston", 33),
          new Person("Johnathon", 41),
          ...
    };
}

Các câu lệnh vòng lặp được hỗ trợ:

@for

razor
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>
}

@foreach

razor
@foreach (var person in people)
{
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>
}

@while

razor
@{ var i = 0; }
@while (i < people.Length)
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>

    i++;
}

@do while

razor
@{ var i = 0; }
@do
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>

    i++;
} while (i < people.Length);

Compound @using

Trong C#, câu lệnh using được sử dụng để đảm bảo một đối tượng được dispose (giải phóng tài nguyên). Trong Razor, cơ chế tương tự được sử dụng để tạo HTML Helpers chứa nội dung bổ sung. Trong code sau, HTML Helpers render một thẻ <form> bằng câu lệnh @using:

razor
@using (Html.BeginForm())
{
    <div>
        <label>Email: <input type="email" id="Email" value=""></label>
        <button>Register</button>
    </div>
}

@try, catch, finally

Xử lý ngoại lệ tương tự C#:

cshtml
@try
{
    throw new InvalidOperationException("You did something invalid.");
}
catch (Exception ex)
{
    <p>The exception message: @ex.Message</p>
}
finally
{
    <p>The finally statement.</p>
}

@lock

Razor có khả năng bảo vệ các phần tới hạn bằng câu lệnh lock (khóa):

razor
@lock (SomeLock)
{
    // Do critical section work
}

Comments (Chú thích)

Razor hỗ trợ chú thích C# và HTML:

razor
@{
    /* C# comment */
    // Another C# comment
}
<!-- HTML comment -->

Code render HTML sau:

html
<!-- HTML comment -->

Chú thích Razor được xóa bởi server trước khi trang web được render. Razor sử dụng @* *@ để phân định chú thích. Code sau được chú thích ra, vì vậy server không render bất kỳ markup nào:

razor
@*
    @{
        /* C# comment */
        // Another C# comment
    }
    <!-- HTML comment -->
*@

Directives (Chỉ thị)

Razor directives được biểu diễn bởi các biểu thức ngầm định với các từ khóa dành riêng theo sau ký hiệu @. Một directive thường thay đổi cách một view được biên dịch hoặc hoạt động.

@attribute

Directive @attribute thêm thuộc tính đã cho vào lớp của trang hoặc view được tạo ra. Ví dụ sau thêm thuộc tính [Authorize]:

razor
@attribute [Authorize]

@code

Kịch bản này chỉ áp dụng cho Razor components (.razor).

Block @code cho phép một Razor component thêm các thành phần C# (fields, properties, và methods) vào component:

razor
@code {
    // C# members (fields, properties, and methods)
}

@functions

Directive @functions cho phép thêm các thành phần C# (fields, properties, và methods) vào lớp được tạo ra:

razor
@functions {
    // C# members (fields, properties, and methods)
}

Ví dụ:

cshtml
@functions {
    public string GetHello()
    {
        return "Hello";
    }
}

<div>From method: @GetHello()</div>

Code tạo ra HTML markup sau:

html
<div>From method: Hello</div>

@implements

Directive @implements implement (cài đặt) một interface (giao diện) cho lớp được tạo ra.

@inherits

Directive @inherits cung cấp toàn quyền kiểm soát lớp mà view kế thừa:

razor
@inherits TypeNameOfClassToInheritFrom

@inject

Directive @inject cho phép Razor Page inject (tiêm) một service từ service container vào một view.

@model

Kịch bản này chỉ áp dụng cho MVC views và Razor Pages (.cshtml).

Directive @model chỉ định kiểu của model được truyền vào một view hoặc page:

razor
@model TypeNameOfModel

@namespace

Directive @namespace:

razor
@namespace Your.Namespace.Here

@page

Directive @page có các hiệu ứng khác nhau tùy thuộc vào loại file nơi nó xuất hiện. Directive:

@section

Kịch bản này chỉ áp dụng cho MVC views và Razor Pages (.cshtml).

Directive @section được sử dụng cùng với MVC và Razor Pages layouts để cho phép views hoặc pages render nội dung trong các phần khác nhau của trang HTML. Để biết thêm thông tin, xem Layout in ASP.NET Core.

@using

Directive @using thêm directive using của C# vào view được tạo ra:

cshtml
@using System.IO
@{
    var dir = Directory.GetCurrentDirectory();
}
<p>@dir</p>

Directive attributes (Thuộc tính chỉ thị)

Razor directive attributes được biểu diễn bởi các biểu thức ngầm định với các từ khóa dành riêng theo sau ký hiệu @. Một directive attribute thường thay đổi cách một phần tử được biên dịch hoặc hoạt động.

@attributes

Kịch bản này chỉ áp dụng cho Razor components (.razor).

@attributes cho phép một component render các thuộc tính chưa được khai báo.

@bind

Kịch bản này chỉ áp dụng cho Razor components (.razor).

Data binding (ràng buộc dữ liệu) trong components được thực hiện với thuộc tính @bind.

@on{EVENT}

Kịch bản này chỉ áp dụng cho Razor components (.razor).

Razor cung cấp các tính năng xử lý sự kiện cho components.

@key

Kịch bản này chỉ áp dụng cho Razor components (.razor).

Directive attribute @key khiến thuật toán diffing (so sánh) của components đảm bảo giữ nguyên các phần tử hoặc components dựa trên giá trị của key.

@ref

Kịch bản này chỉ áp dụng cho Razor components (.razor).

Component references (@ref) cung cấp một cách để tham chiếu đến một instance component để bạn có thể gửi lệnh đến instance đó.

Templated Razor delegates

Kịch bản này chỉ áp dụng cho MVC views và Razor Pages (.cshtml).

Razor templates cho phép bạn định nghĩa một đoạn UI với định dạng sau:

razor
@<tag>...</tag>

Ví dụ sau minh họa cách chỉ định một templated Razor delegate dưới dạng Func<T,TResult>:

razor
@{
    Func<dynamic, object> petTemplate = @<p>You have a pet named <strong>@item.Name</strong>.</p>;

    var pets = new List<Pet>
    {
        new Pet { Name = "Rin Tin Tin" },
        new Pet { Name = "Mr. Bigglesworth" },
        new Pet { Name = "K-9" }
    };
}

Template được render với pets được cung cấp bởi câu lệnh foreach:

razor
@foreach (var pet in pets)
{
    @petTemplate(pet)
}

Output được render:

html
<p>You have a pet named <strong>Rin Tin Tin</strong>.</p>
<p>You have a pet named <strong>Mr. Bigglesworth</strong>.</p>
<p>You have a pet named <strong>K-9</strong>.</p>

Tag Helpers

Kịch bản này chỉ áp dụng cho MVC views và Razor Pages (.cshtml).

Có ba directive liên quan đến Tag Helpers.

DirectiveChức năng
@addTagHelperLàm cho Tag Helpers có sẵn cho một view.
@removeTagHelperXóa Tag Helpers đã được thêm trước đó khỏi một view.
@tagHelperPrefixChỉ định một tiền tố thẻ để kích hoạt hỗ trợ Tag Helper và làm cho việc sử dụng Tag Helper rõ ràng.

Các từ khóa dành riêng của Razor

Từ khóa Razor

Các từ khóa Razor được thoát bằng @(Razor Keyword) (ví dụ: @(functions)).

Từ khóa Razor C#

Các từ khóa Razor C# phải được thoát kép bằng @(@C# Razor Keyword) (ví dụ: @(@case)). @ đầu tiên thoát Razor parser. @ thứ hai thoát C# parser.

Từ khóa dành riêng không được Razor sử dụng

Kiểm tra lớp C# Razor được tạo ra cho một view

Razor SDK xử lý việc biên dịch các file Razor. Theo mặc định, các file code được tạo ra không được emit (xuất ra). Để kích hoạt emit các file code, đặt directive EmitCompilerGeneratedFiles trong file project (.csproj) thành true:

xml
<PropertyGroup>
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

Khi build một dự án 6.0 (net6.0) trong cấu hình build Debug, Razor SDK tạo ra một thư mục obj/Debug/net6.0/generated/ trong root dự án. Thư mục con của nó chứa các file code Razor page được emit.

Tra cứu view và phân biệt chữ hoa/thường

Razor view engine thực hiện tra cứu phân biệt chữ hoa/thường cho các view. Tuy nhiên, file system (hệ thống file) cơ bản quyết định tra cứu thực tế:

Các developer được khuyến khích khớp cách viết hoa/thường của tên file và thư mục với cách viết hoa/thường của:

Khớp cách viết hoa/thường đảm bảo các deployment (triển khai) tìm thấy các view của chúng bất kể file system cơ bản.

Imports được Razor sử dụng

Các imports sau được tạo ra bởi các web template ASP.NET Core để hỗ trợ các file Razor:

code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;