Skip to content

Commit

Permalink
Updates to Razor Pages intro doc (dotnet#4262)
Browse files Browse the repository at this point in the history
* Updates to Razor Pages intro doc

* More updates

* Remove @functions conversion reference

* Remove all @functions references and code

* Change code snippet type to cshtml
  • Loading branch information
scottaddie authored Sep 13, 2017
1 parent a3a79df commit d2f705f
Show file tree
Hide file tree
Showing 32 changed files with 74 additions and 319 deletions.
63 changes: 25 additions & 38 deletions aspnetcore/mvc/razor-pages/index.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
title: Introduction to Razor Pages in ASP.NET Core
author: Rick-Anderson
description: Overview of Razor Pages in ASP.NET Core
keywords: ASP.NET Core, Razor Pages
description: This doc provides an overview of using Razor Pages in ASP.NET Core to ease the development of page-focused scenarios.
keywords: ASP.NET Core,Razor Pages
ms.author: riande
manager: wpickett
ms.date: 08/15/2017
ms.date: 09/12/2017
ms.topic: get-started-article
ms.technology: aspnet
ms.prod: asp.net-core
Expand All @@ -25,7 +25,7 @@ If you're looking for a tutorial that uses the Model-View-Controller approach, s

Install [.NET Core](https://www.microsoft.com/net/core) 2.0.0 or later.

If you're using Visual Studio, install [Visual Studio](https://www.visualstudio.com/vs/) 15.3 or later with the following workloads:
If you're using Visual Studio, install [Visual Studio](https://www.visualstudio.com/vs/) 2017 version 15.3 or later with the following workloads:

* **ASP.NET and web development**
* **.NET Core cross-platform development**
Expand Down Expand Up @@ -58,35 +58,33 @@ Run `dotnet new razor` from the command line.

Razor Pages is enabled in *Startup.cs*:

[!code-cs[main](index/sample/RazorPagesIntro/Startup.cs?name=Startup)]
[!code-cs[main](index/sample/RazorPagesIntro/Startup.cs?name=snippet_Startup)]

Consider a basic page:
<a name="OnGet"></a>

[!code-cshtml[main](index/sample/RazorPagesIntro/Pages/Index.cshtml)]

The preceding code looks a lot like a Razor view file. What makes it different is the `@page` directive. `@page` makes the file into an MVC action - which means that it handles requests directly, without going through a controller. `@page` must be the first Razor directive on a page. `@page` affects the behavior of other Razor constructs. The [@functions](xref:mvc/views/razor#functions) directive enables function-level content.
The preceding code looks a lot like a Razor view file. What makes it different is the `@page` directive. `@page` makes the file into an MVC action - which means that it handles requests directly, without going through a controller. `@page` must be the first Razor directive on a page. `@page` affects the behavior of other Razor constructs.

A similar page, with the `PageModel` in a separate file, is shown in the following two files. The *Pages/Index2.cshtml* file:
A similar page, using a `PageModel` class, is shown in the following two files. The *Pages/Index2.cshtml* file:

[!code-cshtml[main](index/sample/RazorPagesIntro/Pages/Index2.cshtml)]

The *Pages/Index2.cshtml.cs* 'code-behind' file:
The *Pages/Index2.cshtml.cs* "code-behind" file:

[!code-cs[main](index/sample/RazorPagesIntro/Pages/Index2.cshtml.cs)]

By convention, the `PageModel` class file has the same name as the Razor Page file with *.cs* appended. For example, the previous Razor Page is *Pages/Index2.cshtml*. The file containing the `PageModel` class is named *Pages/Index2.cshtml.cs*.

For simple pages, mixing the `PageModel` class with the Razor markup is fine. For more complex code, it's a best practice to keep the page model code separate.

The associations of URL paths to pages are determined by the page's location in the file system. The following table shows a Razor Page path and the matching URL:

| File name and path | matching URL |
| ----------------- | ------------ |
| */Pages/Index.cshtml* | `/` or `/Index` |
| */Pages/Contact.cshtml* | `/Contact` |
| */Pages/Store/Contact.cshtml* | `/Store/Contact` |
| */Pages/Store/Index.cshtml* | `/Store` or `/Store/Index` |
| */Pages/Store/Index.cshtml* | `/Store` or `/Store/Index` |

Notes:

Expand All @@ -111,9 +109,9 @@ The *Pages/Create.cshtml* view file:

The *Pages/Create.cshtml.cs* code-behind file for the view:

[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=ALL)]
[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=snippet_ALL)]

By convention, the `PageModel` class is called `<PageName>Model` and is in the same namespace as the page. Not much change is needed to convert from a page using `@functions` to define handlers and a page using a `PageModel` class.
By convention, the `PageModel` class is called `<PageName>Model` and is in the same namespace as the page.

Using a `PageModel` code-behind file supports unit testing, but requires you to write an explicit constructor and class. Pages without `PageModel` code-behind files support runtime compilation, which can be an advantage in development. <!-- review: advantage because you can make changes and refresh the browser without explicitly compiling the app -->

Expand All @@ -126,7 +124,7 @@ The `Async` naming suffix is optional but is often used by convention for asynch

The previous `OnPostAsync` method:

[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=OnPostAsync)]
[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=snippet_OnPostAsync)]

The basic flow of `OnPostAsync`:

Expand All @@ -141,19 +139,10 @@ When the submitted form has validation errors (that are passed to the server), t

The `Customer` property uses `[BindProperty]` attribute to opt in to model binding.

[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=PageModel&highlight=10-11)]
[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=snippet_PageModel&highlight=10-11)]

Razor Pages, by default, bind properties only with non-GET verbs. Binding to properties can reduce the amount of code you have to write. Binding reduces code by using the same property to render form fields (`<input asp-for="Customer.Name" />`) and accept the input.

The following code shows the combined version of the create page:

[!code-cshtml[main](index/sample/RazorPagesContacts/Pages/CreateCombined.cshtml)]

Rather than using `@model`, we're taking advantage of a new feature for Pages. By default, the generated `Page`-derived class *is* the model. Using a *view model* with Razor views is a best practice. With Pages, you get a view model *automatically*.

The main change is replacing constructor injection with injected (`@inject`) properties. This page uses [@inject](xref:mvc/views/razor#inject) for [constructor dependency injection](xref:mvc/controllers/dependency-injection#constructor-injection). The `@inject` statement generates and initializes the `Db` property that is used in `OnPostAsync`. Injected (`@inject`) properties are set before handler methods run.


The home page (*Index.cshtml*):

[!code-cshtml[main](index/sample/RazorPagesContacts/Pages/Index.cshtml)]
Expand All @@ -164,7 +153,7 @@ The code behind *Index.cshtml.cs* file:

The *Index.cshtml* file contains the following markup to create an edit link for each contact:

```html
```cshtml
<a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>
```

Expand Down Expand Up @@ -210,7 +199,7 @@ The [Layout](xref:mvc/views/layout#specifying-a-layout) property is set in *Page

[!code-cshtml[main](index/sample/RazorPagesContacts2/Pages/_ViewStart.cshtml)]

Note: The layout is in the *Pages* folder. Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. A layout in the *Pages* folder can be used from any Razor page under the *Pages* folder.
**Note:** The layout is in the *Pages* folder. Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. A layout in the *Pages* folder can be used from any Razor page under the *Pages* folder.

We recommend you **not** put the layout file in the *Views/Shared* folder. *Views/Shared* is an MVC views pattern. Razor Pages are meant to rely on folder hierarchy, not path conventions.

Expand All @@ -234,23 +223,21 @@ When the `@namespace` directive is contained in *_ViewImports.cshtml*, the speci

For example, the code behind file *Pages/Customers/Edit.cshtml.cs* explicitly sets the namespace:

[!code-cs[main](index/sample/RazorPagesContacts2/Pages/Customers/Edit.cshtml.cs?name=namespace)]
[!code-cs[main](index/sample/RazorPagesContacts2/Pages/Customers/Edit.cshtml.cs?name=snippet_namespace)]

The *Pages/_ViewImports.cshtml* file sets the following namespace:

[!code-cshtml[main](index/sample/RazorPagesContacts2/Pages/_ViewImports.cshtml?highlight=1)]

The generated namespace for the *Pages/Customers/Edit.cshtml* Razor Page is the same as the code behind file. The `@namespace` directive was designed so the C# classes added to a project and pages-generated code *just work* without having to add an `@using` directive for the code behind file.

Note: `@namespace` also works with conventional Razor views.
**Note:** `@namespace` also works with conventional Razor views.

The original *Pages/Create.cshtml* view file:

[!code-cshtml[main](index/sample/RazorPagesContacts/Pages/Create.cshtml?highlight=2)]

The updated page:

The *Pages/Create.cshtml* view file:
The updated *Pages/Create.cshtml* view file:

[!code-cshtml[main](index/sample/RazorPagesContacts2/Pages/Customers/Create.cshtml?highlight=2)]

Expand All @@ -262,9 +249,9 @@ The [Razor Pages starter project](#rpvs17) contains the *Pages/_ValidationScript

The `Create` page, shown previously, uses `RedirectToPage`:

[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=OnPostAsync&highlight=10)]
[!code-cs[main](index/sample/RazorPagesContacts/Pages/Create.cshtml.cs?name=snippet_OnPostAsync&highlight=10)]

The app has the following file/folder structure
The app has the following file/folder structure:

* */Pages*

Expand Down Expand Up @@ -302,8 +289,9 @@ ASP.NET Core exposes the [TempData](https://docs.microsoft.com/aspnet/core/api/m

The `[TempData]` attribute is new in ASP.NET Core 2.0 and is supported on controllers and pages.

The following code sets the value of `Message` using `TempData`.
[!code-cs[main](index/sample/RazorPagesContacts2/Pages/Customers/CreateDot.cshtml.cs?highlight=10-11,27-28&name=snippetTemp)]
The following code sets the value of `Message` using `TempData`:

[!code-cs[main](index/sample/RazorPagesContacts2/Pages/Customers/CreateDot.cshtml.cs?highlight=10-11,25&name=snippet_Temp)]

The following markup in the *Pages/Customers/Index.cshtml* file displays the value of `Message` using `TempData`.

Expand Down Expand Up @@ -347,7 +335,6 @@ If you don't like the query string `?handler=JoinList` in the URL, you can chang

[!code-cshtml[main](index/sample/RazorPagesContacts2/Pages/Customers/CreateRoute.cshtml?highlight=1)]


The preceding route puts the handler name in the URL path instead of the query string. The `?` following `handler` means the route parameter is optional.

You can use `@page` to add additional segments and parameters to a page's route. Whatever's there is **appended** to the default route of the page. Using an absolute or virtual path to change the page's route (like `"~/Some/Other/Path"`) is not supported.
Expand All @@ -356,12 +343,12 @@ You can use `@page` to add additional segments and parameters to a page's route.

To configure advanced options, use the extension method `AddRazorPagesOptions` on the MVC builder:

[!code-cs[main](index/sample/RazorPagesContacts/StartupAdvanced.cs?name=snippet1)]
[!code-cs[main](index/sample/RazorPagesContacts/StartupAdvanced.cs?name=snippet_1)]

Currently you can use the `RazorPagesOptions` to set the root directory for pages, or add application model conventions for pages. We hope to enable more extensibility this way in the future.

To precompile views, see [Razor view compilation](xref:mvc/views/view-compilation) .

[Download or view sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/mvc/razor-pages/index/sample).

See [Getting started with Razor Pages in ASP.NET Core](xref:tutorials/razor-pages/razor-pages-start), which builds on this introduction.
See [Getting started with Razor Pages in ASP.NET Core](xref:tutorials/razor-pages/razor-pages-start), which builds on this introduction.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ public class AppDbContext : DbContext
public AppDbContext(DbContextOptions options)
: base(options)
{

}

public DbSet<Customer> Customers { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ public class Customer
[Required, StringLength(100)]
public string Name { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#region ALL
#region snippet_ALL
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
#region PageModel
#region snippet_PageModel
public class CreateModel : PageModel
{
private readonly AppDbContext _db;
Expand All @@ -19,7 +19,7 @@ public CreateModel(AppDbContext db)
[BindProperty]
public Customer Customer { get; set; }

#region OnPostAsync
#region snippet_OnPostAsync
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ public async Task<IActionResult> OnPostAsync()
return RedirectToPage("/Index");
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
#define Debug

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using System.IO;

namespace RazorPagesContacts
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
#if Debug
.UseStartup<StartupDebug>()
#else
.UseStartup<Startup>()
#endif
.Build();

host.Run();
}
}
}
Loading

0 comments on commit d2f705f

Please sign in to comment.