07 - Razor Pages
HTTP - GET
HTTP is purely text based protocol, basically you are just sending bunch of key-value pairs from browser to server.
GET https://localhost:44363/People/Create HTTP/1.1
Host: localhost:44363
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://localhost:44363/People
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,et;q=0.8
Cookie: _ga=GA1.1.1433392067.1524809543;
GET Response
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcYWthdmVyXHNvdXJjZVxyZXBvc1xIVFRQVGVzdFxXZWJBcHBcUGVvcGxlXENyZWF0ZQ==?=
X-Powered-By: ASP.NET
Date: Sat, 26 Jan 2019 12:24:13 GMT
Content-Length: 3367
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Create - WebApp</title>
...
HTTP - POST
POST https://localhost:44363/People/Create HTTP/1.1
Host: localhost:44363
Connection: keep-alive
Content-Length: 194
Cache-Control: max-age=0
Origin: https://localhost:44363
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://localhost:44363/People/Create
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,et;q=0.8
Cookie: _ga=GA1.1.1433392067.1524809543;
Name=Andres&__RequestVerificationToken=CfDJ8JXfYWVYzpxPgG_38dOFAzHQGkoaDtNMXtzBr3vNUFUDOYe7XT_ueh6IMyEfRXYWTTOvd2Bjm8gxjOwN8jRBGngeIIKZmnJB-xD4Wvd1enqy0M5GrgAWVgAOg0vZPgHNzvIYDBTmwJqi3L2WYM-5mm4
POST Response
HTTP/1.1 302 Found
Location: /People
Server: Microsoft-IIS/10.0
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcYWthdmVyXHNvdXJjZVxyZXBvc1xIVFRQVGVzdFxXZWJBcHBcUGVvcGxlXENyZWF0ZQ==?=
X-Powered-By: ASP.NET
Date: Sat, 26 Jan 2019 12:17:16 GMT
Content-Length: 0
HTTP - Response Codes
- 1xx Informational response 100 continue, 101 switching protocols
- 2xx Success
200 OK, 201 Created, 202 Accepted, 204 No Content - 3xx Redirection
300 Multiple choices, 301 Moved Permanently, 302 Found, 303 See other, 304 Not Modified - 4xx Client errors
400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict - 5xx Server errors
500 Internal Server Error, 501 Not implemented, 503 Service Unavailable
Razor Pages
- ASP.NET Core MVC – full and complex web application development framework (next semester, in Distributed course)
- Razor Pages – new aspect of ASP.NET, meant for page-focused workflows
It's simpler!
Basics
- Basic page consists of two files
<pagename>.cshtml
– HTML/Razor file<pagename>.cshtml.cs
– C# code file, containing data and methods for servicing different web verbs (POST, GET and others)
- Razor
- Microsoft developed language for mixing C# and HTML
Simplest page
Simplest .cshtml
razor page:
@page
<h1>Hello, world!</h1>
And C# code file (not required):
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorTest.Pages{
public class SimpleModel : PageModel
{
public void OnGet(){}
public void OnPost(){}
}
}
Result:
Language
Default language in .cshtml
files is HTML.
@
- sign in html?@
denotes special Razor syntax – either Razor directive or C# expression/code block (similar to php’s<?php … ?>
@page
– mandatory Razor directive, has to be on first line!@SomeCSharpMethod()
– method is executed,.ToString()
applied to result – and then output included into HTML- There is no end sign (no
?>
like in php)
Adding data to page
Lets add some data to page:
@model
– model class file for this page@Model
– refers to model instance during runtime (strongly typed)
@page
@model RazorTest.Pages.SimpleModel
<h1>Hello, world!</h1>
<h2>@Model.Message</h2>
public class SimpleModel : PageModel
{
public string Message { get; private set; } = "Hello from code: ";
public void OnGet() {
Message += $"Server time is { DateTime.Now }";
}
public void OnPost(){}
}
Result:
Technical background
Where is all this coming from? Where is the web server? How is the correct page chosen? Cant we have headers, footers, static files etc....
ASP.NET Core MVC applications are Console Apps!
Web applications include built in webserver called Kestrel. Extremely fast and small – but not really meant to be exposed directly to full web. In case of public deployment – proxy is typically used (Apache, IIS, ngnix). Kestrel takes over full ip:port – no multitenant hosting.
Structure
Default web project structure:
- wwwroot – static web content (/images, css, js). Everything is accessible via browser from /.
- Pages – razor pages
- Appsettings.json – settings, db connection string
- Program.cs – console app, kestrel host
- Startup.cs – Services and request pipeline
Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Universal files
Pages folder:
_ViewImports.cshtml
- Razor directives imported into every page_ViewStart.cshtml
– Sets the Layout property for every pageShared/_Layout.cshtml
- layout for every page (can be changed per page)
Page filename & URL matching
- URL path matching is determined by page’s location in file system
/Pages/Index.cshtml
->http://.../ or http://.../Index
/Pages/Contact.cshtml
->http://../Contact
/Pages/Store/Index.cshtml
->http://.../Store/
orhttp://../Store/Index
Layout.cshtml - Minimal example
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RazorTest</title>
</head>
<body>
@RenderBody()
</body>
</html>
@RenderBody
– output of specific razor page is inserted here
Default layout features
Default layout has many more features:
- Bootstrap based layout
- Clientside unobtrusive validation
- CDN usage with fallback to local files
- App specific js and css (on top of bootstrap)
Create Domain entity and DAL
namespace Domain{
public class Person
{
public int PersonId { get; set; }
[MaxLength(64, ErrorMessage = "Too long!")]
[MinLength(2, ErrorMessage = "Too short!")]
public string Name { get; set; }
}
}
namespace DAL{
public class AppDbContext: DbContext
{
public DbSet<Person> Persons { get; set; }
public AppDbContext(DbContextOptions options)
: base(options){
}
}
}
Register DBContext in services (Dependency Injection)
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase("inmemorydb"));
services.AddRazorPages();
}
Access database in IndexModel
public class IndexModel : PageModel
{
private AppDbContext _db;
public IndexModel(AppDbContext db)
{
_db = db;
}
public int ContactCount { get; set; }
public List<Person> Persons { get; set; }
public void OnGet()
{
ContactCount = _db.Persons.Count();
Persons = _db.Persons.ToList();
}
}
Output data in HTML
@page @model IndexModel
<div>Contacts in db: @Model.ContactCount</div>
@foreach (var person in Model.Persons) {
<div>@person.Name</div>
}
Create ContactModel
- BindProperty – binds data in HTTP post to C# properties
- Does not work in case of GET, use
[BindProperty(SupportsGet = true)]
- To bind all public properties, use
[BindProperties]
on top of class. - Binding searches for key-value pairs from (and in order):
Form, Body, Route, Query string, Uploaded files
public class ContactModel : PageModel
{
private readonly AppDbContext _db;
public ContactModel(AppDbContext db)
{
_db = db;
}
// bind data on incoming http post request
[BindProperty]
public Person Person { get; set; }
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Persons.Add(Person);
_db.SaveChanges();
return RedirectToPage("/Index");
}
}
Contact.cshtml
@page @model ContactModel
<p>Enter your name.</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div>FirstName: <input asp-for="Person.Name" /></div>
<input type="submit" />
</form>