# ASP.NET (Core) MVC Web App ## Project Structure ```txt Project |-Properties | |- launchSettings.json | |-wwwroot --> location of static files | |-css | | |- site.css | | | |-js | | |- site.js | | | |-lib | | |- bootstrap | | |- jquery | | |- ... | | | |- favicon.ico | |-Model | |-ErrorViewModel.cs | |- Index.cs | |-... | |-Views | |-Home | | |- Index.cshtml | | | |-Shared | | |- _Layout.cshtml --> reusable default page layout | | |- _ValidationScriptsPartial --> jquery validation script imports | | | |- _ViewImports.cshtml --> shared imports and tag helpers for all views | |- _ViewStart.cshtml --> shared values for all views | |- ... | |-Controllers | |-HomeController.cs | |- appsettings.json |- Program.cs --> App entrypoiny |- Startup.cs --> App config ``` **Note**: `_` prefix indicates page to be imported. ## Important Files ### `Program.cs` ```cs using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace App { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); // start and config ASP.NET App } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); // config handled in Startup.cs }); } } ``` ### `Startup.cs` ```cs using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace App { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // set db context for the app using the connection string services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // Captures synchronous and asynchronous Exception instances from the pipeline and generates HTML error responses. services.AddDatabaseDeveloperPageExceptionFilter(); services.AddControllersWithViews(); // MVC Services } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/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.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); // URL => www.site.com/Controller/Action/id // id optional, action is a method of the controller returnig an ActionResult }); } } } ``` ### `appsettings.json` Connection Strings & Secrets. ```json { // WARNING: not for production (use appsecrets.json) "ConnectionStrings": { "DefaultConnection": "Server=;Database=;Trusted_Connection=true;MultipleActiveResultSets=true;" } "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ``` ### `launchsettings.json` Launch Settings & Deployement Settings. ```json { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:55045", "sslPort": 44301 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" } }, "App": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" } } } } ``` ## Controllers ```cs using Microsoft.AspNetCore.Mvc; using App.Models; using System.Collections.Generic; namespace App.Controllers { public class CategoryController : Controller { private readonly AppDbContext _db; // get db context through dependency injection public CategoryController(AppDbContext db) { _db = db; } // GET /Controller/Index public IActionResult Index() { IEnumerable enities = _db.Entities; return View(Entities); // pass data to the @model } // GET /Controller/Create public IActionResult Create() { return View(); } // POST /Controller/Create [HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(Entity entity) // recieve data from the @model { _db.Entities.Add(entity); _db.SaveChanges(); return RedirectToAction("Index"); // redirection } // GET - /Controller/Edit public IActionResult Edit(int? id) { if(id == null || id == 0) { return NotFound(); } Entity entity = _db.Entities.Find(id); if (entity == null) { return NotFound(); } return View(entity); // return pupulated form for updating } // POST /Controller/Edit [HttpPost] [ValidateAntiForgeryToken] public IActionResult Edit(Entity entity) { if (ModelState.IsValid) // all rules in model have been met { _db.Entities.Update(entity); _db.SaveChanges(); return RedirectToAction("Index"); } return View(entity); } // GET /controller/Delete public IActionResult Delete(int? id) { if (id == null || id == 0) { return NotFound(); } Entity entity = _db.Entities.Find(id); if (entity == null) { return NotFound(); } return View(entity); // return pupulated form for confirmation } // POST /Controller/Delete [HttpPost] [ValidateAntiForgeryToken] public IActionResult Delete(Entity entity) { if (ModelState.IsValid) // all rules in model have been met { _db.Entities.Remove(entity); _db.SaveChanges(); return RedirectToAction("Index"); } return View(entity); } } } ``` ## Data Validation ### Model Annotations In `Entity.cs`: ```cs using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; namespace App.Models { public class Entity { [DisplayName("Integer Number")] [Required] [Range(1, int.MaxValue, ErrorMessage = "Error Message")] public int IntProp { get; set; } } } ``` ### Tag Helpers & Client Side Validation In `View.cshtml`; ```cs
// error message displyed here
// client side validation @section Scripts{ @{ } } ``` ### Server Side Validation ```cs using Microsoft.AspNetCore.Mvc; using App.Models; using System.Collections.Generic; namespace App.Controllers { public class CategoryController : Controller { private readonly AppDbContext _db; // get db context through dependency injection public CategoryController(AppDbContext db) { _db = db; } // GET /Controller/Index public IActionResult Index() { IEnumerable enities = _db.Entities; return View(Entities); // pass data to the @model } // GET /Controller/Create public IActionResult Create() { return View(); } // POST /Controller/Create [HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(Entity entity) // recieve data from the @model { if (ModelState.IsValid) // all rules in model have been met { _db.Entities.Add(entity); _db.SaveChanges(); return RedirectToAction("Index"); } return View(entity); // return model and display error messages } } } ```