The foundation: what the official documentation explains
MVC views use Razor to combine markup with server-side values and can share layouts. JavaScript referenced by a rendered page executes in the browser, not in the Razor renderer. ASP.NET Core documentation also distinguishes state-changing request protection from ordinary page rendering; adding an authenticated page does not by itself settle the security of a JavaScript write request.
Implementation context: The examples use a .NET 10 MVC application organized into feature folders (Vertical Slice Architecture). Basic C# classes and async/await are assumed. Reference excerpts show selected parts of that application; separately labeled teaching adaptations explain alternatives. They are not complete standalone projects.
Locate the two halves of the screen
The Country Create view selects /Areas/_LayoutArea.cshtml and defines a Vue mount point named app-create. Its sibling Create.cshtml.js owns reactive fields such as form, errors, submitting, and created. This is a useful arrangement for a beginner because the page and its behavior are adjacent, yet their execution environments remain distinct.
Razor expressions such as ViewData run while the server builds the response. Vue directives such as v-model are interpreted in the browser. Reading a cshtml file as though all its expressions are C# will make the form seem contradictory. First identify which renderer owns each expression.
Reference excerpt: Areas/Admin/Country/Views/Create.cshtml
@section Scripts {
<script src="~/areas/Admin/Country/Views/Create.cshtml.js" asp-append-version="true"></script>
}
Understand how the collocated script is served
The script URL begins with ~/areas/, while the file lives under the project Areas directory. The reference startup adds a physical file provider for collocated scripts. This is additional serving configuration; placing arbitrary JavaScript beside a Razor view does not prove that a default MVC app will serve it at that URL.
If a page stays blank behind v-cloak, inspect the script request and the first console error. A 404 HTML response fetched as JavaScript can produce an unexpected-token error. That is a path or asset-serving problem, not a failure of the Country database query. A successful C# build does not check this browser boundary.
Follow the form state transitions
The reference form begins with empty code, name, and description values. Client validation populates errors before any POST. While saving, submitting disables repeat interaction. On success, created switches the screen to a confirmation view with links that use the returned identifier. These state variables make the UI behavior explicit rather than relying on a full page reload.
The important transition is not simply button clicked to success. It is input validated, request pending, response interpreted, then success or failure. The failure path must leave the user able to correct input or try again.
Reference excerpt: Areas/Admin/Country/Views/Create.cshtml.js
const response = await fetch('/api/country', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code: form.code,
name: form.name,
description: form.description || null
})
});
const result = await response.json();
Interpret HTTP and application outcomes separately
The reference code parses the body and checks result.success. For a more defensive adaptation, first inspect the content type and HTTP response before assuming every failure has the expected JSON envelope. This matters in this app because custom error middleware can redirect some failures to an HTML page.
Here is an isolated reader-friendly helper for the existing submit flow. It does not change the endpoint or add authentication. Its only responsibility is to make an unexpected response diagnosable. Use the returned envelope for the existing field-error and success-state logic.
Teaching adaptation: response parsing helper
async function readApiResponse(response) {
const contentType = response.headers.get('content-type') ?? '';
if (!contentType.includes('application/json')) {
throw new Error(`Expected JSON, received HTTP ${response.status}`);
}
const result = await response.json();
if (!response.ok || !result.success) {
return { ...result, success: false };
}
return result;
}
Keep output and request security explicit
The reference confirmation uses text-oriented bindings for the country code. Preserve that behavior for user-supplied values rather than inserting raw HTML. Treat the write endpoint authorization independently from the visibility of a Save button. If cookies authenticate browser writes, review and implement the appropriate antiforgery protection for the endpoint contract; a JSON fetch snippet is not a complete security setup.
Do not add a hidden token to a form and assume fetch automatically sends it as an accepted request token. The client and server need matching transmission and validation behavior. The excerpt documents the existing form flow, while the official security reference explains the protection that must be considered for a deployment.
Check the complete interaction
In a local learning environment, confirm that the script loads, the page becomes visible, a blank name displays feedback, a valid submission produces one request, and an API failure leaves the form usable. Inspect narrow-screen layout as well: a technically correct response is not useful if the error message is outside the visible form.
When diagnosing, start with the earliest failure. A script that never loads cannot submit a request. A request that is rejected before the handler cannot save a row. A successful save with an incorrect confirmation link is a response-to-UI mapping issue. This order avoids unnecessary backend changes.
Key Takeaways
- Razor and JavaScript execute at different stages and in different places.
- Collocated files require a reachable script URL and serving configuration.
- A form needs clear pending, success, and failure states.
FAQ
Does Razor execute v-model?
No. Vue interprets it in the browser after Razor has produced HTML.
Why can a page be blank after a successful build?
A missing or invalid JavaScript file can prevent the client app from mounting; C# compilation does not verify browser execution.
Does fetch automatically add an antiforgery request token?
No. Token transmission and server validation must be deliberately connected when that protection is used.