Add Instructions Razor Pages (#1503)

This commit is contained in:
Mario Codes
2026-04-28 08:09:16 +02:00
committed by GitHub
parent 1993d16ca4
commit 0d5ea7bc3c
2 changed files with 84 additions and 0 deletions

View File

@@ -67,6 +67,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-instructions) for guidelines on
| [Copilot Process tracking Instructions](../instructions/copilot-thought-logging.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-thought-logging.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-thought-logging.instructions.md) | See process Copilot is following where you can edit this to reshape the interaction or save when follow up may be needed |
| [Copilot Prompt Files Guidelines](../instructions/prompt.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fprompt.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fprompt.instructions.md) | Guidelines for creating high-quality prompt files for GitHub Copilot |
| [Cpp Language Service Tools](../instructions/cpp-language-service-tools.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcpp-language-service-tools.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcpp-language-service-tools.instructions.md) | You are an expert at using C++ language service tools (GetSymbolReferences_CppTools, GetSymbolInfo_CppTools, GetSymbolCallHierarchy_CppTools). Instructions for calling C++ Tools for Copilot. When working with C++ code, you have access to powerful language service tools that provide accurate, IntelliSense-powered analysis. **Always prefer these tools over manual code inspection, text search, or guessing.** |
| [Csharp Razorpages](../instructions/csharp-razorpages.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcsharp-razorpages.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcsharp-razorpages.instructions.md) | Razor Pages component and application patterns |
| [Custom Agent File Guidelines](../instructions/agents.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fagents.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fagents.instructions.md) | Guidelines for creating custom agent files for GitHub Copilot |
| [Custom Instructions File Guidelines](../instructions/instructions.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Finstructions.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Finstructions.instructions.md) | Guidelines for creating high-quality custom instruction files for GitHub Copilot |
| [Dart and Flutter](../instructions/dart-n-flutter.instructions.md)<br />[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fdart-n-flutter.instructions.md)<br />[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fdart-n-flutter.instructions.md) | Instructions for writing Dart and Flutter code following the official recommendations. |

View File

@@ -0,0 +1,83 @@
---
description: 'Razor Pages component and application patterns'
applyTo: '**/*.cshtml, **/*.cshtml.cs'
---
## Razor Pages Code Style and Structure
- Write idiomatic, efficient Razor Pages and C#.
- Stick to the conventions the framework is built around: handler-based PageModels, not MVC controller patterns shoehorned into pages.
- Keep PageModels focused on request/response orchestration; business logic belongs in injected domain services.
- Trivial handlers can stay inline. For pages with lots of handlers and dependencies, reach for a mediator like MediatR.
- Use async/await end-to-end so handlers don't block the request pipeline.
## Naming Conventions
- PascalCase for PageModel classes, handler methods, and public members (`CreateModel`, `OnPostAsync`, `OnPostDeleteAsync`).
- camelCase for private fields and locals, with the `_` prefix on private fields per the .NET convention (`_context`, `_logger`).
- Interface names start with "I" (`IEmailService`).
- Named handlers drop the `OnPost`/`Async` affixes when routed. `OnPostJoinListAsync` is reached as `handler=JoinList`.
## Model Binding and Overposting
- Don't put `[BindProperty]` on EF or domain entities directly. An attacker can post extra fields like `IsAdmin` or `Secret` and the binder will happily set them, even if the form doesn't render them.
- Bind to a dedicated Input Model or View Model that exposes only the properties the page is allowed to accept, then map to the entity.
- `TryUpdateModelAsync<T>` with an explicit allow-list of properties is another option, especially in edit scenarios.
- Avoid `[Bind]` for edits. Excluded properties get reset to `default(T)` rather than left alone, which is rarely what you want. Prefer Input Models.
- Don't enable `[BindProperty(SupportsGet = true)]` broadly. Razor Pages skips GET binding by default for a reason; opt in per-property and validate what comes in.
- For custom types (including strongly-typed IDs), implement `TryParse` or a `TypeConverter` so they bind from route and query values. Without one, the binder treats them as complex types and binding silently fails. One of those bugs that wastes an afternoon.
- `[BindRequired]` and `[Required]` aren't the same thing. `[BindRequired]` errors when the source value is *absent* from the posted form; `[Required]` validates that the bound value isn't null/empty. `[BindRequired]` only applies to form binding, since JSON and XML go through input formatters instead.
## Handler Methods and Request Flow
- Always use Post-Redirect-Get on successful POSTs. Return `RedirectToPage("./Index")`, never `Page()`. Returning `Page()` on success means a browser refresh resubmits the form.
```csharp
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid) return Page(); // re-render on error
await _service.CreateAsync(Input);
return RedirectToPage("./Index"); // PRG on success
}
```
- Guard every persistence path with `if (!ModelState.IsValid) return Page();`. Client-side validation can be bypassed; the server is authoritative.
- Use a handler parameter (`OnGetAsync(int id)`) for single-request route or query values. Use `[BindProperty]` for POST data that needs to round-trip back to the view on validation errors.
- Named handlers (`OnPostDeleteAsync`, `OnPostApproveAsync`) need the `asp-page-handler` tag helper on the submit button. Without it, plain buttons fall back to `OnPostAsync` or 404.
- If `OnGet` does expensive work, add a lightweight `OnHead`. Razor Pages falls back to `OnGet` for HEAD requests otherwise, so every probe pays the full GET cost.
- Filters work differently here than in MVC: `[ActionFilter]` attributes are silently ignored on page handlers. Use `IPageFilter` / `IAsyncPageFilter`, or register global conventions through `options.Conventions` in `Program.cs`.
## Project Structure and Conventions
- Shared layouts, partials, and templates go in `Pages/Shared/`, not `Views/Shared/`. Razor Pages resolves views hierarchically from the page's folder up through `Pages/`, and mixing in MVC conventions just fights the framework.
- Set `Layout` in `Pages/_ViewStart.cshtml`. Use `Pages/_ViewImports.cshtml` for `@namespace`, `@addTagHelper`, and shared directives.
- Keep `.cshtml` and `.cshtml.cs` colocated. Per-page locality is one of the main reasons to use Razor Pages in the first place, and splitting them across folders throws that away.
## Security
- Trust Razor's default `@` expression HTML encoding. Don't reach for `@Html.Raw()` on user-supplied content; it disables encoding and opens the door to XSS.
- Stick with `<form method="post">` and the Form Tag Helper so the antiforgery token gets injected automatically. For AJAX or `fetch`, render the token with `@Html.AntiForgeryToken()` and send it as the `RequestVerificationToken` header.
- Don't commit secrets to `appsettings.json`. Use `appsettings.{Environment}.json` for environment overrides, User Secrets (`dotnet user-secrets`) locally, and Azure Key Vault or environment variables in production. Bind via `IOptions<T>`.
## Dependency Injection in PageModels
- Watch for the scoped-in-singleton captive dependency trap. If a singleton holds a reference to a scoped service (like an EF `DbContext`), that instance leaks across requests. Common bug in PageModel-adjacent services.
- Don't register a `DbContext` as `Singleton`. The default `AddDbContext` registration is `Scoped` for a reason.
## Entity Framework Core in Page Handlers
- Project EF entities to DTOs or View Models with `.Select(...)` before returning them to the view. Passing entities with navigation properties straight through causes lazy-loading exceptions, N+1 queries, or serialization cycles when the view renders.
- Use `.AsNoTracking()` on read-only queries like list pages or details pages without edit. The change tracker has overhead you don't need there.
- Prefer `FindAsync(key)` over `FirstOrDefaultAsync(x => x.Id == key)` when fetching by primary key without `Include`. `FindAsync` checks the change tracker first.
## State Management
- `TempData` is for one-shot, cross-redirect messages like flash notifications after a PRG. It's read-once, cookie-serialized by default, and not a substitute for session storage.
- For actual per-user session state, use `ISession`. For per-request data, `HttpContext.Items`. For shared state within a single request, request-scoped DI services.
- Call `TempData.Keep()` or `TempData.Peek()` when a value needs to survive multiple redirects without being consumed.
## Testing
- Unit-test `PageModel` classes directly. Instantiate them with mocked dependencies (Moq, NSubstitute) and assert on the returned `IActionResult`: `PageResult` for re-renders, `RedirectToPageResult` for successful PRG, `NotFoundResult` for 404 paths.
- For integration tests that exercise routing, model binding, and antiforgery, use `WebApplicationFactory<TEntryPoint>` with `Microsoft.AspNetCore.Mvc.Testing`.
- When testing handlers that read `ModelState`, populate it manually with `PageModel.ModelState.AddModelError(...)`. The binding pipeline doesn't run in unit tests.