European ASP.NET MVC 4 and MVC 5 Hosting

BLOG about ASP.NET MVC 3, ASP.NET MVC 4, and ASP.NET MVC 5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

ASP.NET MVC Hosting - HostForLIFE.eu :: Full-Stack Web Development in ASP.NET Core 8 MVC

clock July 26, 2024 08:55 by author Peter

Complete-Stack Development
The process of designing, creating, and implementing a web application's client-side (or front-end) and server-side (or back-end) components is known as full-stack web development. Developers handle all aspects of this comprehensive method, including database management, server configuration, and user interface design and interaction.

Components of a Full-Stack Web UI
Front-End Development
HTML, CSS, and JavaScript
Front-end development involves creating the visual elements and user experience of a website or web application. HTML (HyperText Markup Language), CSS (Cascading Style Sheets), and JavaScript form the core technologies used in front-end development. HTML provides the structure, CSS dictates the presentation and layout, and JavaScript adds interactivity and dynamic behavior to the user interface.

Front-End Frameworks and Libraries

To streamline development and enhance functionality, front-end developers often leverage frameworks and libraries such as React, Angular, Vue.js, and Bootstrap. These tools provide pre-built components, responsive design capabilities, and state management features, enabling developers to build interactive and visually appealing user interfaces more efficiently.

Back-End Development
Server-Side Programming Languages
Back-end development focuses on the server-side logic and functionality of a web application. Developers use programming languages like JavaScript (Node.js), Python (Django, Flask), Ruby (Ruby on Rails), Java (Spring Boot), and C# (ASP.NET Core) to implement server-side operations, data processing, and communication with databases.

Databases and Data Storage
Back-end developers work with databases to store, retrieve, and manipulate data required by the web application. Common types of databases include relational databases like MySQL, PostgreSQL, and SQL Server, as well as NoSQL databases like MongoDB and Firebase Firestore. Developers also utilize Object-Relational Mapping (ORM) libraries to simplify database interactions and manage data models.

APIs and Web Services
Application Programming Interfaces (APIs) play a crucial role in full-stack development by facilitating communication between the front-end and back-end components of a web application. Developers design and implement RESTful APIs or GraphQL endpoints to define how data is exchanged between the client and server. Web services, such as authentication services, payment gateways, and third-party integrations, are also integrated into the back-end architecture to extend the functionality of the web application.

DevOps and Deployment
Continuous Integration and Deployment (CI/CD)

DevOps practices are essential for streamlining the development, testing, and deployment processes of a full-stack web application. Developers utilize CI/CD pipelines to automate build, test, and deployment tasks, ensuring rapid and reliable delivery of updates and new features to production environments.

Cloud Platforms and Hosting Services
Full-stack developers deploy web applications on cloud platforms like Amazon Web Services (AWS), Microsoft Azure, and Google Cloud Platform (GCP), or deploy them to managed hosting services such as Heroku, Netlify, or Vercel. These platforms provide scalable infrastructure, serverless computing, and management tools that simplify deployment, monitoring, and maintenance tasks.

Introduction to ASP.NET Core 8 MVC

ASP.NET Core 8 MVC (Model-View-Controller) is a powerful framework for building modern, scalable, and feature-rich web applications. With its robust architecture, flexible programming model, and extensive tooling support, ASP.NET Core 8 MVC empowers developers to create full-stack web applications that meet the demands of today's digital landscape.

Components of ASP.NET Core 8 MVC

Model
In ASP.NET Core 8 MVC, the model represents the data and business logic of the application. Developers define model classes to encapsulate data entities, define relationships, and implement business rules. Models interact with the database through Entity Framework Core or other data access libraries, enabling seamless data manipulation and persistence.

View

Views in ASP.NET Core 8 MVC are responsible for presenting the user interface and rendering dynamic content to the client. Developers create view templates using Razor syntax, which combines HTML markup with C# code to generate dynamic web pages. Views leverage model data to display information to users and incorporate client-side scripting languages like JavaScript for enhanced interactivity.

Controller
Controllers act as intermediaries between the model and view components in ASP.NET Core 8 MVC. They handle user requests, execute business logic, and orchestrate data retrieval and manipulation operations. Controllers receive input from the user through the browser, process it, and return an appropriate response, such as rendering a view or redirecting to another page.

Full Stack Development in ASP.NET Core 8 MVC

Front-End Development
HTML, CSS, and JavaScript

Front-end development in ASP.NET Core 8 MVC involves creating responsive and visually appealing user interfaces using HTML, CSS, and JavaScript. Developers leverage HTML for structure, CSS for styling, and JavaScript for client-side interactivity and dynamic behavior.

Razor Pages and Views
ASP.NET Core 8 MVC utilizes Razor Pages and Views to generate dynamic web content. Developers use Razor syntax within HTML markup to incorporate server-side logic, access model data, and render dynamic content. Razor Pages provide a streamlined approach to building web pages, while Views offer more flexibility and customization options.

Back-End Development
C# Programming Language
Back-end development in ASP.NET Core 8 MVC revolves around the C# programming language, which serves as the primary language for implementing server-side logic, business rules, and data access operations. C# offers a robust and type-safe development environment, enabling developers to build scalable and maintainable back-end components.

Entity Framework Core

Entity Framework Core is a powerful Object-Relational Mapping (ORM) framework that simplifies data access and manipulation in ASP.NET Core 8 MVC applications. Developers use Entity Framework Core to define data models, perform database migrations, execute LINQ queries, and interact with the underlying database seamlessly.

ASP.NET Core Identity

ASP.NET Core Identity provides robust authentication and authorization features for securing ASP.NET Core 8 MVC applications. Developers leverage ASP.NET Core Identity to implement user authentication, role-based access control, password hashing, and account management functionalities, ensuring the security and integrity of web applications.

Deployment and DevOps
Continuous Integration and Deployment (CI/CD)
DevOps practices play a crucial role in the deployment and maintenance of ASP.NET Core 8 MVC applications. Developers implement CI/CD pipelines to automate build, test, and deployment processes, ensuring rapid and reliable delivery of updates to production environments. Tools like Azure DevOps, GitHub Actions, and Jenkins streamline the CI/CD workflow, enabling efficient collaboration and deployment.

Nuget Packages for this application
Microsoft.AspNetCore.Mvc
Microsoft.EntityFrameworkCore
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.Extensions.Logging
Microsoft.Extensions.Configuration
Microsoft.AspNetCore.Authentication.Cookies
Microsoft.Extensions.DependencyInjection
Newtonsoft.Json
Microsoft.AspNetCore.StaticFiles
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

SQLite Database
We will use SQL Lite database for this application

Nuget Package for SQL Lite Database
Microsoft.EntityFrameworkCore.Sqlite

Student Model
namespace FullStackDevelopmentUsingAspnetCore8MVC.Models
{
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string RollNo { get; set; }
        public string Section { get; set; }
        public string Program { get; set; }
    }
}


Application Database Context

using FullStackDevelopmentUsingAspnetCore8MVC.Models;
using Microsoft.EntityFrameworkCore;

namespace FullStackDevelopmentUsingAspnetCore8MVC.ApplicationDbContext
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<Student> Students { get; set; }
    }
}


Application Setting File

// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=FullStackDevlopment.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}


Program.cs Class
using FullStackDevelopmentUsingAspnetCore8MVC.ApplicationDbContext;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;

// Add services to the container.
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite(configuration.GetConnectionString("DefaultConnection")));
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.UseAuthorization();

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");


app.MapRazorPages();

app.Run();

Student Controller
using FullStackDevelopmentUsingAspnetCore8MVC.ApplicationDbContext;
using FullStackDevelopmentUsingAspnetCore8MVC.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace FullStackDevelopmentUsingAspnetCore8MVC.Controllers
{
    public class StudentsController : Controller
    {
        private readonly AppDbContext _context;

        public StudentsController(AppDbContext context)
        {
            _context = context;
        }

        // GET: Students
        public async Task<IActionResult> Index()
        {
            return View(await _context.Students.ToListAsync());
        }

        // GET: Students/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if(id == null)
            {
                return NotFound();
            }

            var student = await _context.Students
                .FirstOrDefaultAsync(m => m.Id == id);
            if(student == null)
            {
                return NotFound();
            }

            return View(student);
        }

        // GET: Students/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Students/Create
        // To protect from overposting attacks, enable the specific properties you want to bind to.
        // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,Name,RollNo,Section,Program")] Student student)
        {
            if(ModelState.IsValid)
            {
                _context.Add(student);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(student);
        }

        // GET: Students/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if(id == null)
            {
                return NotFound();
            }

            var student = await _context.Students.FindAsync(id);
            if(student == null)
            {
                return NotFound();
            }
            return View(student);
        }

        // POST: Students/Edit/5
        // To protect from overposting attacks, enable the specific properties you want to bind to.
        // For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("Id,Name,RollNo,Section,Program")] Student student)
        {
            if(id != student.Id)
            {
                return NotFound();
            }

            if(ModelState.IsValid)
            {
                try
                {
                    _context.Update(student);
                    await _context.SaveChangesAsync();
                }
                catch(DbUpdateConcurrencyException)
                {
                    if(!StudentExists(student.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(student);
        }

        // GET: Students/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if(id == null)
            {
                return NotFound();
            }

            var student = await _context.Students
                .FirstOrDefaultAsync(m => m.Id == id);
            if(student == null)
            {
                return NotFound();
            }

            return View(student);
        }

        // POST: Students/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var student = await _context.Students.FindAsync(id);
            if(student != null)
            {
                _context.Students.Remove(student);
            }

            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool StudentExists(int id)
        {
            return _context.Students.Any(e => e.Id == id);
        }
    }
}


Front-End Development Using CSHTML Pages
INDEX Page For showing Details
@model IEnumerable<FullStackDevelopmentUsingAspnetCore8MVC.Models.Student>

@{
    ViewData["Title"] = "Index";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.RollNo)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Section)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Program)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.RollNo)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Section)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Program)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>


CREATE Page for creating student
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Create";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Create</h1>

<h4>Student</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="RollNo" class="control-label"></label>
                <input asp-for="RollNo" class="form-control" />
                <span asp-validation-for="RollNo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Section" class="control-label"></label>
                <input asp-for="Section" class="form-control" />
                <span asp-validation-for="Section" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Program" class="control-label"></label>
                <input asp-for="Program" class="form-control" />
                <span asp-validation-for="Program" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


DELETE Page for Deleting Student
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Delete";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.RollNo)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.RollNo)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Section)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Section)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Program)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Program)
        </dd>
    </dl>

    <form asp-action="Delete">
        <input type="hidden" asp-for="Id" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>


DETAILS Page for showing individual Student Details
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Details";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Details</h1>

<div>
    <h4>Student</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.RollNo)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.RollNo)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Section)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Section)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Program)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Program)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model?.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>


Edit Page for Editing the Student Details
@model FullStackDevelopmentUsingAspnetCore8MVC.Models.Student

@{
    ViewData["Title"] = "Edit";
    Layout = "~/Pages/Shared/_Layout.cshtml";
}

<h1>Edit</h1>

<h4>Student</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="RollNo" class="control-label"></label>
                <input asp-for="RollNo" class="form-control" />
                <span asp-validation-for="RollNo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Section" class="control-label"></label>
                <input asp-for="Section" class="form-control" />
                <span asp-validation-for="Section" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Program" class="control-label"></label>
                <input asp-for="Program" class="form-control" />
                <span asp-validation-for="Program" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


Conclusion
ASP.NET Core 8 MVC empowers developers to build full-stack web applications with unparalleled flexibility, scalability, and performance. By mastering the components of ASP.NET Core 8 MVC, including models, views, controllers, front-end and back-end development tools, and deployment practices, developers can create robust and feature-rich web applications that meet the evolving needs of users and businesses.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Exception Handling in MVC

clock July 18, 2024 10:07 by author Peter

The practice of reacting to unusual circumstances that call for extra processing is known as exception handling. In any program, addressing exceptions is crucial. There are two methods we can deal with exceptions in ASP.NET.

  • Try-catch-finally block at method level
  • Using Application_Error

Exception Handling in MVC
In ASP.NET MVC we have a larger list of ways to handle exceptions such as,

  • Try-catch-finally
  • Overriding OnException method
  • Using the [HandleError] attribute on actions and controllers
  • Setting a global exception-handling filter
  • Handling Application_Error event
  • Extending HandleErrorAttribute

To begin with, create a new empty ASP.NET MVC application. Add a controller as "ExceptionTestingController". Add a view for Index. Add another view at "Views\Shared" as "Error. cshtml".

For testing purposes, we would cause some kind of exception.

try-catch-finally

Change the Index as in the following.
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    try
    {
        c = a / b; // This line would cause an exception.
    }
    catch (Exception ex)
    {
        return View("Error");
    }
    finally
    {
        // Optional code to execute after try block (e.g., cleanup)
    }
    return View();
}


Here we are generating an exception at "c = a / b;" inside the try block. In the catch, we are returning a different view "Error". If you run the application you will get an Error page in the browser. In an actual project, we can also use the catch block to log the error information.

Overriding OnException method

For this, remove the try block from the preceding code. We need to overwrite OnException as in the following.
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    c = a / b; // This line would cause an exception.
    return View();
}

protected override void OnException(ExceptionContext filterContext)
{
    string action = filterContext.RouteData.Values["action"].ToString();
    Exception e = filterContext.Exception;
    filterContext.ExceptionHandled = true;
    filterContext.Result = new ViewResult()
    {
        ViewName = "Error"
    };
}


OnException is a void method that takes an argument as an object of ExceptionContext that has all the information about the exception that can be used to log. We need to set ExceptionHandled = true for the ExceptionContext object. We can handle the exception generated from all the actions from a specific controller. We can get the action name from ExceptionContext as in.
string action = filterContext.RouteData.Values["action"].ToString();

There is a problem with this approach that we cannot reuse the exception handling logic across multiple controllers. That is where global error handling is useful.

Using HandleError Attribute

This is a simple way to handle exceptions in MVC. Remove the OnException implementation from the previous code. Use the following procedure to do that.

Step 1. Add <customErrors mode="On" ></customErrors> in web.config under <system.web>.
Step 2. Decorate the action with [HandleError] as,
[HandleError]
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    c = a / b; // This line will cause an exception.
    return View();
}

We can handle a different exception with a different view with [HandleError] as in the following.
[HandleError]
[HandleError(ExceptionType = typeof(DivideByZeroException), View = "Error1")]
[HandleError(ExceptionType = typeof(ArgumentOutOfRangeException), View = "Error2")]
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;
    c = a / b; // This would cause an exception.
    return View();
}

Here we should place a specific HandleError at the bottom if we reverse the order of the HandleError attribute then the Error. cshtml will always be displayed.

In the same way, we can decorate our controller with HandleError. This would handle the exception generated from all the actions from a specific controller.

Limitations

  • Does not support logging exceptions.
  • Doesn't catch HTTP exceptions other than 500.
  • Doesn't catch exceptions that are raised outside controllers.
  • Returns an error view even for exceptions raised in AJAX calls.

Setting a global exception-handling filter
For this, we need to ensure that HandleErrorAttribute is added in RegisterGlobalFilters of the FilterConfig file of App_start and registered in Application_Start. No need to decorate our action and controller with [HandleError].

Extending HandleErrorAttribute

We can also create our own Exception Handler by inheriting from HandleErrorAttribute as in the following.
public class MyExceptionHandler : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled || filterContext.HttpContext.IsCustomErrorEnabled)
        {
            return;
        }

        Exception e = filterContext.Exception;
        filterContext.ExceptionHandled = true;
        filterContext.Result = new ViewResult()
        {
            ViewName = "Error2"
        };
    }
}


We need to set ExceptionHandled = true for the ExceptionContext object. We can handle the exception generated from all the actions from a specific controller. We can also get the action name from ExceptionContext as in the following.
string action = filterContext.RouteData.Values["action"].ToString();

Now we can decorate our action/controller with [MyExceptionHandler] to use this.
[MyExceptionHandler]
public ActionResult Index()
{
    int a = 1;
    int b = 0;
    int c = 0;

    // This operation would cause an exception (division by zero).
    c = a / b;

    return View();
}



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Using Detailed Views for Global Search in ASP.NET Core MVC

clock July 11, 2024 08:06 by author Peter

We'll go over how to add a global search function to an ASP.NET Core MVC application in this article. This feature will provide search results with photographs and have a drop-down filter to search through all categories, products, and particular categories or goods. To ensure full end-to-end operation, we'll also develop detailed views for the search results.

Step 1: Configure Models and Database
We will first establish our models and database context. We'll use two models in this example: Product and Category.

Models

In the Models folder, create the Product and Category models.
namespace GlobalSearchInAspNetCoreMVC.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string? Description { get; set; }
        public string ImageUrl { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }
}


namespace GlobalSearchInAspNetCoreMVC.Models
{
    public class Category
    {
        public int Id { get; set; }
        public string? Name { get; set; }
    }
}

namespace GlobalSearchInAspNetCoreMVC.Models
{
    public class SearchResultItem
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Description { get; set; }
        public string? ImageUrl { get; set; }
    }
}

Database Context
Create the ApplicationDbContext in the Data folder.
using GlobalSearchInAspNetCoreMVC.Models;
using Microsoft.EntityFrameworkCore;
namespace GlobalSearchInAspNetCoreMVC.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
    }

}


Step 2. Create and Seed the Database
Configure the database connection string in appsettings.json.
{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=SQLLiteDatabase.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}


Update the Startup. cs or Program. cs to use the ApplicationDbContext.
using GlobalSearchInAspNetCoreMVC.AppService;
using GlobalSearchInAspNetCoreMVC.Data;
using GlobalSearchInAspNetCoreMVC.IAppServices;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<ISearchService, SearchService>();
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.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    endpoints.MapRazorPages();
});
app.Run();

Create a migration and update the database.
add-migration GlobalSearch
update-database


Seed the Database
Create a DataSeeder class to seed the database with initial data.
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
namespace GlobalSearchInAspNetCoreMVC.Data
{
    public static class DataSeeder
    {
        public static void Seed(IServiceProvider serviceProvider)
        {
            using (var context = new ApplicationDbContext(
                serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
            {
                if (!context.Products.Any())
                {
                    context.Products.AddRange(
                        new Product { Name = "Product 1", Description = "Description 1", ImageUrl = "/images/product1.jpg" },
                        new Product { Name = "Product 2", Description = "Description 2", ImageUrl = "/images/product2.jpg" }
                    );
                    context.SaveChanges();
                }
                if (!context.Categories.Any())
                {
                    context.Categories.AddRange(
                        new Category { Name = "Category 1" },
                        new Category { Name = "Category 2" }
                    );
                    context.SaveChanges();
                }
            }
        }
    }
}


Or save the data using a database query.
-- Insert dummy data into Categories table
INSERT INTO Categories (Name)
VALUES
    ('Electronics'),
    ('Books'),
    ('Clothing'),
    ('Home & Kitchen'),
    ('Sports & Outdoors');
-- Insert dummy data into Products table
INSERT INTO Products (Name, Description, ImageUrl, CategoryId)
VALUES
    ('Smartphone', 'Latest model smartphone with advanced features', 'https://via.placeholder.com/150/0000FF/808080?text=Smartphone', 1),
    ('Laptop', 'High performance laptop suitable for gaming and work', 'https://via.placeholder.com/150/FF0000/FFFFFF?text=Laptop', 1),
    ('Headphones', 'Noise-cancelling over-ear headphones', 'https://via.placeholder.com/150/FFFF00/000000?text=Headphones', 1),
    ('Science Fiction Book', 'A gripping science fiction novel', 'https://via.placeholder.com/150/00FF00/000000?text=Science+Fiction+Book', 2),
    ('Cookbook', 'Delicious recipes for home cooks', 'https://via.placeholder.com/150/FF69B4/000000?text=Cookbook', 2),
    ('T-shirt', 'Comfortable cotton T-shirt', 'https://via.placeholder.com/150/800080/FFFFFF?text=T-shirt', 3),
    ('Jeans', 'Stylish denim jeans', 'https://via.placeholder.com/150/FFA500/FFFFFF?text=Jeans', 3),
    ('Blender', 'High-speed blender for smoothies and soups', 'https://via.placeholder.com/150/000080/FFFFFF?text=Blender', 4),
    ('Coffee Maker', 'Programmable coffee maker with timer', 'https://via.placeholder.com/150/FFC0CB/000000?text=Coffee+Maker', 4),
    ('Yoga Mat', 'Non-slip yoga mat for all types of exercise', 'https://via.placeholder.com/150/008080/FFFFFF?text=Yoga+Mat', 5),
    ('Dumbbells', 'Set of adjustable dumbbells', 'https://via.placeholder.com/150/000000/FFFFFF?text=Dumbbells', 5);


Call the seeder in the Program. cs.
public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();
    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        var context = services.GetRequiredService<ApplicationDbContext>();
        context.Database.Migrate();
        DataSeeder.Seed(services);
    }
    host.Run();
}

Step 3. Implement the Search Service
Create a search service to handle the search logic.

ISearchService.cs

using GlobalSearchInAspNetCoreMVC.Models;
namespace GlobalSearchInAspNetCoreMVC.IAppServices
{
    public interface ISearchService
    {
        Task<IEnumerable<object>> SearchAsync(string searchTerm,string filter);
        Task<SearchResultItem> GetItemByIdAsync(int id);
    }
}

SearchService.cs
using GlobalSearchInAspNetCoreMVC.Data;
using GlobalSearchInAspNetCoreMVC.IAppServices;
using GlobalSearchInAspNetCoreMVC.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace GlobalSearchInAspNetCoreMVC.AppService
{
    public class SearchService : ISearchService
    {
        private readonly ApplicationDbContext _context;
        public SearchService(ApplicationDbContext context)
        {
            _context = context;
        }
        public async Task<IEnumerable<object>> SearchAsync(string searchTerm, string filter)
        {
            List<object> results = new List<object>();

            if (filter == "all" || filter == "products")
            {
                var products = await _context.Products
                    .Where(p => p.Name.Contains(searchTerm) || p.Description.Contains(searchTerm))
                    .Select(p => new { p.Id, p.Name, p.Description, p.ImageUrl })
                    .ToListAsync();

                results.AddRange(products);
            }
            if (filter == "all" || filter == "categories")
            {
                var categories = await _context.Categories
                    .Where(c => c.Name.Contains(searchTerm))
                    .Select(c => new { c.Id, c.Name })
                    .ToListAsync();

                results.AddRange(categories);
            }
            return results;
        }
        public async Task<SearchResultItem> GetItemByIdAsync(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product != null)
            {
                return new SearchResultItem
                {
                    Id = product.Id,
                    Name = product.Name,
                    Description = product.Description,
                    ImageUrl = product.ImageUrl
                };
            }
            var category = await _context.Categories.FindAsync(id);
            if (category != null)
            {
                return new SearchResultItem
                {
                    Id = category.Id,
                    Name = category.Name,
                    Description = "Category"
                };
            }
            return null;
        }
    }
}

Step 4. Create the HomeController
Create the HomeController to handle the search and detail views.

HomeController.cs

using GlobalSearchInAspNetCoreMVC.IAppServices;
using Microsoft.AspNetCore.Mvc;
namespace GlobalSearchInAspNetCoreMVC.Controllers
{
    public class HomeController : Controller
    {
        private readonly ISearchService _searchService;

        public HomeController(ISearchService searchService)
        {
            _searchService = searchService;
        }
        public IActionResult Index()
        {
            return View();
        }
        public async Task<IActionResult> Details(int id)
        {
            var item = await _searchService.GetItemByIdAsync(id);
            if (item == null)
            {
                return NotFound();
            }
            return View(item);
        }
    }
}


Step 5. Create the Views
Views/Home/Index.cshtml
<div class="text-center">
    <h1 class="display-4">Search</h1>
    <div class="form-group">
        <select id="search-filter" class="form-control">
            <option value="all">All</option>
            <option value="products">Products</option>
            <option value="categories">Categories</option>
        </select>
    </div>
    <input type="text" id="search-input" placeholder="Search..." class="form-control" />
    <div id="search-results" class="dropdown-menu" style="display: block; position: absolute; width: 100%;"></div>
</div>
@section Scripts {
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script>
        $(document).ready(function () {
            function performSearch() {
                let query = $('#search-input').val();
                let filter = $('#search-filter').val();
                if (query.length >= 3) {
                    $.ajax({
                        url: '/api/search',
                        type: 'GET',
                        data: { query: query, filter: filter },
                        success: function (data) {
                            let results = $('#search-results');
                            results.empty();
                            if (data.length > 0) {
                                data.forEach(item => {
                                    if (item.imageUrl) { // Check if item has an image
                                        results.append(`
                                                        <a class="dropdown-item" href="/Home/Details/${item.id}">
                                                    <img src="${item.imageUrl}" alt="${item.name}" style="width:50px; height:50px;"/>
                                                    ${item.name || item.description}
                                                </a>
                                               `);
                                    } else {
                                        results.append(`<a class="dropdown-item" href="/Home/Details/${item.id}">${item.name || item.description}</a>`);
                                    }
                                });
                            } else {
                                results.append('<a class="dropdown-item">No results found</a>');
                            }
                        }
                    });
                } else {
                    $('#search-results').empty();
                }
            }
            $('#search-input').on('input', performSearch);
            $('#search-filter').on('change', performSearch);
        });
    </script>
}

Views/Home/Details.cshtml
@model SearchResultItem
@{
    ViewData["Title"] = "Details";
}
<div class="text-center">
    <h1 class="display-4">Details</h1>
    @if (Model != null)
    {
        <div class="card" style="width: 18rem;">
            @if (!string.IsNullOrEmpty(Model.ImageUrl))
            {
                <img class="card-img-top" src="@Model.ImageUrl" alt="Card image cap">
            }
            <div class="card-body">
                <h5 class="card-title">@Model.Name</h5>
                <p class="card-text">@Model.Description</p>
            </div>
        </div>
    }
    else
    {
        <p>Item not found.</p>
    }
</div>


Step 6. Create the Search Controller
using GlobalSearchInAspNetCoreMVC.IAppServices;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace GlobalSearchInAspNetCoreMVC.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class SearchController : ControllerBase
    {
        private readonly ISearchService _searchService;

        public SearchController(ISearchService searchService)
        {
            _searchService = searchService;
        }
        [HttpGet]
        public async Task<IActionResult> Get(string query, string filter)
        {
            if (string.IsNullOrEmpty(query))
            {
                return BadRequest("Query cannot be empty");
            }
            var results = await _searchService.SearchAsync(query, filter);
            return Ok(results);
        }
    }
}

Step 7. Run and Test
Now that everything is set up, run your application. You should be able to search for products and categories using the dropdown filter, view the search results with images, and click on each result to see detailed information.

Step 8. Output

Conclusion
We've added a global search function to an ASP.NET Core MVC application in this article. Creating models, establishing the database context, populating the database with initial information, putting the search service into operation, and generating the required views and controllers were all addressed. This complete implementation ought to offer a thorough grasp of how to create a search feature in ASP.NET Core MVC applications.

 



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Using Gmail Server to Send Email in ASP.NET Core MVC

clock July 2, 2024 07:11 by author Peter

This tutorial will show you how to use the Gmail server in an ASP.NET Core MVC application to send emails. For handling the email functionality, we will make use of the MailKit and MimeKit libraries.

Step 1: Establish an MVC ASP.NET Core project

  • Make a new project in Visual Studio by opening it.
  • Click "Next" after selecting "ASP.NET Core Web Application".
  • Click "Create" after giving your project a name.
  • Make sure ".NET Core" and "ASP.NET Core 3.1 or later" are selected when you choose "Web Application (Model-View-Controller)". Press "Create."

Step 2 Install the necessary NuGet packages
To facilitate emailing, install the MailKit and MimeKit packages.

  • Navigating to Tools > NuGet Package Manager > Package Manager Console will launch the NuGet Package Manager Console.
  • Execute the subsequent commands.
Install-Package MailKit
Install-Package MimeKit


Step 3. Configure SMTP Settings
Add your Gmail SMTP settings in the appsettings.json file.
{
  "SmtpSettings": {
    "Server": "smtp.gmail.com",
    "Port": 587,
    "SenderName": "Your Name",
    "SenderEmail": "[email protected]",
    "Username": "[email protected]",
    "Password": "your-app-specific-password"
  }
}


To generate an app-specific password, follow these steps.
Go to your Google Account
  • Visit myaccount.google.com.
Security Settings
  • Navigate to the "Security" section.
App Passwords
  • Under "Signing in to Google," find "App passwords."
  • You might need to sign in again.
  • Select "App passwords."
Generate an app password
  • Select the app (e.g., Mail) and the device (e.g., Windows Computer) from the dropdown.
  • Click "Generate."
  • Google will provide a 16-character password.
Update Your Configuration
  • Use this 16-character password in your appsettings.json instead of your normal Google account password.
Step 4. Create the Email Service
Create a service to handle the email-sending logic. Add a new class EmailService.cs to your project.
using MimeKit;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
namespace EmailSendingInAspNetCoreMVC.Services
{
    public class EmailService
    {
        private readonly IConfiguration _configuration;
        public EmailService(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public async Task SendEmailAsync(string toEmail, string subject, string message)
        {
            var emailMessage = new MimeMessage();
            emailMessage.From.Add(new MailboxAddress(_configuration["SmtpSettings:SenderName"], _configuration["SmtpSettings:SenderEmail"]));
            emailMessage.To.Add(new MailboxAddress("", toEmail));
            emailMessage.Subject = subject;
            emailMessage.Body = new TextPart("plain") { Text = message };
            using (var client = new SmtpClient())
            {
                await client.ConnectAsync(_configuration["SmtpSettings:Server"], int.Parse(_configuration["SmtpSettings:Port"]), MailKit.Security.SecureSocketOptions.StartTls);
                await client.AuthenticateAsync(_configuration["SmtpSettings:Username"], _configuration["SmtpSettings:Password"]);
                await client.SendAsync(emailMessage);
                await client.DisconnectAsync(true);
            }
        }
    }
}


Step 5. Register for the Email Service

Register the EmailService in the Startup.cs file.
using EmailSendingInAspNetCoreMVC.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<EmailService>();
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.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();


Step 6. Create the EmailViewModel
Add a new class EmailViewModel.cs to your Models folder.
namespace EmailSendingInAspNetCoreMVC.ViewModels
{
    public class EmailViewModel
    {
        public string? ToEmail { get; set; }
        public string? Subject { get; set; }
        public string? Message { get; set; }
    }
}


Step 7. Create the Email Controller
Add a new controller EmailController.cs to handle the email sending requests.
using EmailSendingInAspNetCoreMVC.Sevices;
using EmailSendingInAspNetCoreMVC.ViewModels;
using Microsoft.AspNetCore.Mvc;
namespace EmailSendingInAspNetCoreMVC.Controllers
{
    public class EmailController : Controller
    {
        private readonly EmailService _emailService;

        public EmailController(EmailService emailService)
        {
            _emailService = emailService;
        }
        [HttpGet]
        public IActionResult SendEmail()
        {
            return View();
        }
        [HttpPost]
        public async Task<IActionResult> SendEmail(EmailViewModel model)
        {
            if (ModelState.IsValid)
            {
                await _emailService.SendEmailAsync(model.ToEmail, model.Subject, model.Message);
                ViewBag.Message = "Email sent successfully!";
            }
            return View(model);
        }
    }
}


Step 8. Create the Email View

Create a new view SendEmail.cshtml in the Views/Email folder.
@model EmailViewModel
@{
    ViewData["Title"] = "Send Email";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<form asp-action="SendEmail" method="post">
    <div>
        <label asp-for="ToEmail"></label>
        <input asp-for="ToEmail" type="email" required />
        <span asp-validation-for="ToEmail"></span>
    </div>
    <div>
        <label asp-for="Subject"></label>
        <input asp-for="Subject" type="text" required />
        <span asp-validation-for="Subject"></span>
    </div>
    <div>
        <label asp-for="Message"></label>
        <textarea asp-for="Message" required></textarea>
        <span asp-validation-for="Message"></span>
    </div>
    <div>
        <input type="submit" value="Send Email" />
    </div>
</form>
@if (ViewBag.Message != null)
{
    <p>@ViewBag.Message</p>
}

Step 9. Add Validation Scripts
Ensure that validation scripts are included in your _Layout.cshtml.
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js"></script>


Step 10. Run the Application
Build and run your application.
Navigate to /Email/SendEmail.
Fill out the form and send an email.

Conclusion
By following this guide, you will successfully integrate email-sending functionality into your ASP.NET Core MVC application using the Gmail SMTP server. Utilizing `MailKit` and `MimeKit` libraries ensures secure and efficient handling of emails. Setting up an `EmailService`, creating an `EmailViewModel`, and configuring an `EmailController` with a corresponding view will provide a complete solution for sending emails. Generating an application-specific password for Gmail ensures compatibility with accounts that have two-factor authentication enabled. This setup enhances your application's functionality and enables effective communication with users.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Using AJAX to Integrate Third-Party API in ASP.NET Core MVC

clock June 24, 2024 07:09 by author Peter

Your web application's functionality can be substantially increased by integrating third-party APIs, which give it access to and usage of outside resources. We will look at how to use AJAX to incorporate a third-party API into an ASP.NET Core Razor Pages application in this article. Using a sample in which we retrieve public holidays from an API and present them in a Bootstrap table, we will illustrate this procedure.

Step 1. Setting up the ASP.NET Core Razor Pages project
First, create a new ASP.NET Core Razor Pages project in Visual Studio:

  • Open Visual Studio and create a new project.
  • Select ASP.NET Core Web Application and click Next.
  • Name your project and click Create.
  • Choose NET Core" and ASP.NET Core 5.0 or later.
  • Select Web Application (Model-View-Controller) and click Create.

Step 2. Creating the Razor Page
Next, add a new Razor Page to your project:

  • Right-click on the Pages folder and select Add > New Item
  • Choose Razor Page and name it Index.

Step 3. Designing the Razor Page
Edit the Index.cshtml file to include a form for user input and a Bootstrap table to display the fetched data.
@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}
<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <form id="inputForm">
        <input type="text" id="yearValue" name="yearValue" placeholder="Enter Year value">
        <input type="text" id="countryCode" name="countryCode" placeholder="Enter countryCode value">
        <button type="submit">Submit</button>
    </form>
</div>
<div class="container mt-4">
    <div class="row">
        <div class="col-md-12">
            <table id="holidayTable" class="table table-striped">
                <thead>
                    <tr>
                        <th>Date</th>
                        <th>Local Name</th>
                        <th>Name</th>
                        <th>Country Code</th>
                        <th>Fixed</th>
                        <th>Global</th>
                        <th>Types</th>
                    </tr>
                </thead>
                <tbody id="tableBody"></tbody>
            </table>
        </div>
    </div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        $("#inputForm").submit(function (e) {
            debugger;
            e.preventDefault();
            var yearValue = $("#yearValue").val();
            var countryCode = $("#countryCode").val();

            $.ajax({
                type: "GET",
                url: "https://date.nager.at/api/v3/publicholidays/" + yearValue + "/" + countryCode,
                success: function (data) {
                    console.log(data);
                    populateTable(data);
                },
                error: function (error) {
                    console.log(error);
                }
            });
        });
    });
    function populateTable(data) {
        debugger;
        var tableBody = $("#tableBody");
        tableBody.empty();

        data.forEach(function (holiday) {
            var row = $("<tr>");
            row.append($("<td>").text(holiday.date));
            row.append($("<td>").text(holiday.localName));
            row.append($("<td>").text(holiday.name));
            row.append($("<td>").text(holiday.countryCode));
            row.append($("<td>").text(holiday.fixed));
            row.append($("<td>").text(holiday.global));
            row.append($("<td>").text(holiday.types.join(', ')));
            tableBody.append(row);
        });
    }
</script>


Step 4. Adding jQuery and AJAX Script
In the Index.cshtml file, include jQuery, and write the AJAX call to fetch data from the third-party API. Make sure to include the security token if required.
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
    $(document).ready(function () {
        $("#inputForm").submit(function (e) {
            debugger;
            e.preventDefault();
            var yearValue = $("#yearValue").val();
            var countryCode = $("#countryCode").val();
            $.ajax({
                type: "GET",
                url: "https://date.nager.at/api/v3/publicholidays/" + yearValue + "/" + countryCode,
                success: function (data) {
                    console.log(data);
                    populateTable(data);
                },
                error: function (error) {
                    console.log(error);
                }
            });
        });
    });
    function populateTable(data) {
        debugger;
        var tableBody = $("#tableBody");
        tableBody.empty();

        data.forEach(function (holiday) {
            var row = $("<tr>");
            row.append($("<td>").text(holiday.date));
            row.append($("<td>").text(holiday.localName));
            row.append($("<td>").text(holiday.name));
            row.append($("<td>").text(holiday.countryCode));
            row.append($("<td>").text(holiday.fixed));
            row.append($("<td>").text(holiday.global));
            row.append($("<td>").text(holiday.types.join(', ')));
            tableBody.append(row);
        });
    }
</script>


In this script

  • The form submission is handled by preventing the default action and getting the input values.
  • An AJAX GET request is made to the third-party API with the year and country code values.
  • The security token is included in the request headers.
  • On a successful response, the populateTable function dynamically creates rows and cells in the table to display the fetched data.

Step 5. Running the Application

  • Build and run the application.
  • Navigate to the /Index page.
  • Enter the year and country code values and submit the form.
  • The public holidays data will be fetched from the API and displayed in the Bootstrap table.

Output

\

Conclusion

Setting up the form and table on the Razor page, performing the AJAX request with the required arguments and headers, and dynamically updating the table with the retrieved data are all part of integrating a third-party API using AJAX in an ASP.NET Core Razor Pages application. Without refreshing the website, this method enables a smooth and interactive user experience.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: How to Connect a Database with C#, ADO.NET, and ASP.NET MVC?

clock June 19, 2024 09:14 by author Peter

The seamless integration of databases is essential for current web development's data management and retrieval processes. This article explains how to create a robust C# Data Access Layer (DAL) that can interface with a MySQL database. This code provides an example of how to implement such a DAL. Using a MySQL database is the responsibility of the ClsDal class. It has functions to manage connection strings and get data out of the database.

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using MySql.Data.MySqlClient;

namespace DemoAPI.DB_Logic
{
    public class ClsDal
    {
        private static string mstrPath = null;
        static ClsCommonCryptography mobjCryptography = new ClsCommonCryptography();

        public static DataSet Qry_WithDataSet(string ProcWithParameter)
        {
            IErrorRepository error = new ErrorRepository();
            DataSet ds = new DataSet();
            try
            {
                if (GetConnStr() != null)
                {
                    MySqlConnection sqlCon = new MySqlConnection(mstrPath);

                    sqlCon.Open();

                    MySqlCommand sqlCmd = new MySqlCommand(ProcWithParameter, sqlCon);

                    sqlCmd.CommandTimeout = 0;

                    MySqlDataAdapter sqlDataAdapter = new MySqlDataAdapter
                    {
                        SelectCommand = sqlCmd
                    };

                    DataSet dtReturn = new DataSet();

                    sqlDataAdapter.Fill(dtReturn);

                    sqlCmd.Dispose();
                    sqlDataAdapter.Dispose();
                    sqlCon.Dispose();

                    return dtReturn;
                }
                else
                {
                    return null;
                }
            }
            catch (MySqlException sqlEx)
            {
                error.DBlogError("Qry_WithDataSet", ProcWithParameter, "Output" + "\n" + sqlEx.ToString());
                return null;
            }
            catch (Exception ex)
            {
                error.DBlogError("DBError Method : Qry_WithDataSet", ProcWithParameter, "Output" + "\n" + ex.ToString());
                return null;
            }
        }

        private static string GetConnStr()
        {
            IErrorRepository error = new ErrorRepository();
            try
            {
                mstrPath = System.Configuration.ConfigurationManager.ConnectionStrings["DbConn"].ConnectionString.ToString();
                mstrPath = mobjCryptography.StringDecrypt(mstrPath);
                return mstrPath;
            }
            catch (Exception ex)
            {
                error.DBlogError("DBError Method : GetConnStr", mstrPath, "Output" + "\n" + ex.ToString());
                return null;
            }
        }
    }
}


Data Access Layer (DAL) for MySQL database operations is defined by the supplied C# code as the ClsDal class in the DemoAPI.DB_Logic namespace. It offers techniques to safely manage connections and query the database. A decrypted connection string that is obtained from the configuration file is used by the Qry_WithDataSet method to run SQL queries or stored procedures against the database. Ensuring strong error management, it manages exceptions through organized error logging. While both methods use an `IErrorRepository` interface to log database-related issues, the GetConnStr method decrypts and obtains the database connection string. Generally speaking, this code promotes security and dependability by encapsulating best practices for database interaction in the context of web applications.

Conclusion  

For MySQL databases, implementing a Data Access Layer in C# requires connection management, query execution, and error handling done right. These best practices are demonstrated by the given code, which shows how to safely connect to a MySQL database, run queries, and log problems for maintenance and troubleshooting.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: Stopping XSS Attacks in MVC ASP.NET

clock June 13, 2024 07:45 by author Peter

When malicious scripts are inserted into web sites that other users are seeing, it can lead to cross-site scripting (XSS) attacks, which pose a serious security risk. This article shows how to use best practices and built-in security capabilities in ASP.NET Core MVC to develop a basic blog application while guarding against XSS attacks.

Step 1. Create the ASP.NET Core MVC Project
Create a new ASP.NET Core MVC project
dotnet new mvc -n BlogApp
cd BlogApp


Add Entity Framework Core Packages
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.Security.Application

Step 2. Set Up Entity Framework Core
Create the database context and models
Create a Data folder and add ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
using XSSAttackInAspNetCoreMVC.Model;
namespace XSSAttackInAspNetCoreMVC.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
        public DbSet<BlogPost> BlogPosts { get; set; }
    }
}

Configure the Database Context in Startup.cs
using Microsoft.EntityFrameworkCore;
using XSSAttackInAspNetCoreMVC.Data;
namespace XSSAttackInAspNetCoreMVC
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // Add services to the container.
            builder.Services.AddRazorPages();
            builder.Services.AddControllersWithViews();
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
            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.UseEndpoints(routes =>
            {
                routes.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
            app.Run();
        }
    }
}


Add Connection String in appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=BlogAppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Create the Initial Migration and Update the Database
dotnet ef migrations add InitialCreate
dotnet ef database update


Step 3. Create Controllers and Views
Create the BlogPost Model
Create a Models folder and add BlogPost.cs
using System.ComponentModel.DataAnnotations;
namespace XSSAttackInAspNetCoreMVC.Model
{
    public class BlogPost
    {
        public int Id { get; set; }
        [Required]
        [StringLength(100)]
        public string? Title { get; set; }
        [Required]
        public string? Content { get; set; }
        public DateTime Created { get; set; }
    }
}


Create the BlogController
Create a Controllers folder and add BlogController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Security.Application;
using XSSAttackInAspNetCoreMVC.Data;
using XSSAttackInAspNetCoreMVC.Model;
namespace XSSAttackInAspNetCoreMVC.Controllers
{
    public class BlogController : Controller
    {
        private readonly ApplicationDbContext _context;
        public BlogController(ApplicationDbContext context)
        {
            _context = context;
        }
        [HttpGet]
        public IActionResult Index()
        {
            var posts = _context.BlogPosts.ToList();
            return View(posts);
        }
        [HttpGet]
        public IActionResult Create()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(BlogPost model)
        {
            if (ModelState.IsValid)
            {
                // Sanitize inputs before saving to the database
                model.Title = Sanitizer.GetSafeHtmlFragment(model.Title);
                model.Content = Sanitizer.GetSafeHtmlFragment(model.Content);
                _context.BlogPosts.Add(model);
                _context.SaveChanges();
                return RedirectToAction(nameof(Index));
            }
            return View(model);
        }
    }
}

Create the Views
Create a Views/Blog folder and add the following views
Index.cshtml
@model IEnumerable<XSSAttackInAspNetCoreMVC.Model.BlogPost>
<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
</head>
<body>
    <h1>Blog Posts</h1>
    <a asp-controller="Blog" asp-action="Create">Create New Post</a>
    <ul>
        @foreach (var post in Model)
        {
            <li>
                <h2>@post.Title</h2>
                <p>@Html.Raw(@post.Content)</p>
                <p><small>@post.Created</small></p>
            </li>
        }
    </ul>
</body>
</html>


Create.cshtml
@model XSSAttackInAspNetCoreMVC.Model.BlogPost
<!DOCTYPE html>
<html>
<head>
    <title>Create Blog Post</title>
</head>
<body>
    <h1>Create Blog Post</h1>
    <form asp-action="Create" method="post" asp-antiforgery="true">
        <div class="form-group">
            <label asp-for="Title"></label>
            <input asp-for="Title" class="form-control" />
            <span asp-validation-for="Title" class="text-danger"></span>
        </div>
        <div class="form-group">
            <label asp-for="Content"></label>
            <textarea asp-for="Content" class="form-control"></textarea>
            <span asp-validation-for="Content" class="text-danger"></span>
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
    <a asp-controller="Blog" asp-action="Index">Back to List</a>
</body>
</html>


Step 4. Add Client-Side Validation

Enable Client-Side Validation
Add the necessary scripts to _Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - XSSAttackInAspNetCoreMVC</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/XSSAttackInAspNetCoreMVC.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">XSSAttackInAspNetCoreMVC</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-action="Index" asp-controller="Blog">Blog</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2024 - XSSAttackInAspNetCoreMVC - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>


Step 5. Run the Application
dotnet run

Output
If we want to submit the script in the form after doing the above steps the site will sanatize the input if the script is found the site will not accept the data.

After submitting this data, we will debug the inputs in the code, and we will not receive the script attack on my website.


You can see from the result that we don't receive any output pertaining to the scripts in the view after sanitization.

Conclusion

By following these instructions, you have developed a basic ASP.NET Core MVC application that shields users against XSS threats while enabling them to make and read blog posts. In order to display content properly, the application employs built-in encoding features and sanitizes user inputs before saving them to the database. By using this method, you may be sure that your application is safe against frequent XSS attacks.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: How to Mastering ASP.NET Core Identity in ASP.NET Core MVC?

clock June 7, 2024 10:30 by author Peter

Any web application that needs user authorization and authentication must include ASP.NET Core Identity. It provides a smooth method for controlling roles, tokens, claims, passwords, and users. You may use this guide to set up and use ASP.NET Core Identity in your application in an efficient manner.


ASP.NET Core Identity Installation
Install the ASP.NET Core Identity required NuGet package.
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore

Configuring Identity Services
builder.Services.AddIdentity<AppUser, IdentityRole>(options =>
{
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = 7;
    options.Password.RequireUppercase = true;

    options.User.RequireUniqueEmail = true;
})
             .AddEntityFrameworkStores<AppDbContext>()
             .AddTokenProvider<DataProtectorTokenProvider<AppUser>>(TokenOptions.DefaultProvider);


Configure Middleware
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});


Models for the Application
AppUser

using Microsoft.AspNetCore.Identity;

namespace A_MicrosoftAspNetCoreIdentityManagement.Models
{
    public class AppUser : IdentityUser
    {
        [PersonalData]
        public string FirstName { get; set; }

        [PersonalData]
        public string LastName { get; set; }
    }
}


Profile
namespace A_MicrosoftAspNetCoreIdentityManagement.Models
{
        public class Profile
        {
            public int ProfileId { get; set; }
            public string? FirstName { get; set; }
            public string? LastName { get; set; }
            public string? FullName { get; set; }

            public string? TagLine { get; set; }
            public string? About { get; set; }
            public string? Country { get; set; }
            public string? City { get; set; }

            public string? UserName { get; set; }
            public string? PhoneNumber { get; set; }

            public string? FacebookLink { get; set; }
            public string? TwitterLink { get; set; }
            public DateTime MemberSince { get; set; }
            public string? Website { get; set; }
            public string? ContactVerified { get; set; }

            public string? HeaderImageUrl { get; set; }
            public string? DisplayImageUrl { get; set; }
            public string? ProfileUrl { get; set; }

            public int ProfileBadgeId { get; set; }
            //Navigational Properties
            public virtual ICollection<ProfileBadge>? ProfileBadges { get; set; }
    }
}


Creating the Application Database Context
using A_MicrosoftAspNetCoreIdentityManagement.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace A_MicrosoftAspNetCoreIdentityManagement.Data
{
    public class AppDbContext : IdentityDbContext<AppUser>
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {

        }

        public DbSet<Profile> Profiles { get; set; }
        public DbSet<ProfileBadge> ProfileBadges { get; set; }
        public DbSet<Badge> badges { get; set; }
        public DbSet<DeactivatedProfile> DeactivatedProfiles { get; set; }
    }
}

C#
Setting Up Middleware

app.UseAuthentication();
app.UseAuthorization();


Configuring Identity Options
services.Configure<IdentityOptions>(options =>
{
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = 8;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = true;
    options.Password.RequireLowercase = false;
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.User.RequireUniqueEmail = true;
});

Creating Controllers and Views for Authentication
Account Controller
Create an AccountController to handle registration, login, and logout.
using A_MicrosoftAspNetCoreIdentityManagement.Data;
using A_MicrosoftAspNetCoreIdentityManagement.Models;
using A_MicrosoftAspNetCoreIdentityManagement.Services;
using A_MicrosoftAspNetCoreIdentityManagement.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace A_MicrosoftAspNetCoreIdentityManagement.Controllers
{
    public class AccountController : Controller
    {
        private SignInManager<AppUser> _signInManager;
        private UserManager<AppUser> _userManager;
        private RoleManager<IdentityRole> _roleManager;
        private readonly AppDbContext _context;

        public AccountController(AppDbContext context, SignInManager<AppUser> signInManager, UserManager<AppUser> userManager, RoleManager<IdentityRole> roleManager)
        {
            _context = context;
            _signInManager = signInManager;
            _userManager = userManager;
            _roleManager = roleManager;
        }

        public IActionResult Login()
        {
            if (this.User.Identity.IsAuthenticated)
            {
                return RedirectToAction("Index", "Roles");
            }
            return View();
        }

        [HttpPost, ActionName("Login")]
        public async Task<IActionResult> LoginPost(LoginViewModel loginModel)
        {

            if (ModelState.IsValid)
            {
                var profile = _context.Profiles.Where(p => p.UserName == loginModel.Email).SingleOrDefault();
                var deactivated = _context.DeactivatedProfiles.Where(e => e.ProfileId == profile.ProfileId).FirstOrDefault();
                if (deactivated != null)
                {
                    ModelState.AddModelError("", $"Your profile has been blocked:{deactivated.Reason}. Please contact administrator [email protected]");
                    return View();
                }
                var result = await _signInManager.PasswordSignInAsync(loginModel.Email, loginModel.Password, loginModel.RememberMe, false);

                if (result.Succeeded)
                {
                    var user = await _userManager.FindByEmailAsync(loginModel.Email);
                    var roles = await _userManager.GetRolesAsync(user);
                    var primaryRole = roles.FirstOrDefault();

                    HttpContext.Session.SetString("ProfileId", profile.ProfileId.ToString());
                    HttpContext.Session.SetString("ProfileRole", primaryRole ?? "Member");
                    if (!string.IsNullOrWhiteSpace(profile.DisplayImageUrl))
                    {
                        HttpContext.Session.SetString("ProfileImage", profile.DisplayImageUrl);

                    }
                    else
                    {
                        HttpContext.Session.SetString("ProfileImage", "favicon.ico");
                    }

                    if(primaryRole == "Vendor")
                    {
                        return RedirectToAction("Index", "Roles");
                    }
                    else if (primaryRole == "Artist")
                    {
                        return RedirectToAction("BuyerDashBoard", "Roles");
                    }
                    else if (primaryRole == "Buyer")
                    {
                        return RedirectToAction("BuyerDashBoard", "Roles");
                    }
                    else if (primaryRole == "Admin")
                    {
                        return RedirectToAction("Index", "Roles");
                    }
                    else if (primaryRole == "Member")
                    {
                        return RedirectToAction("Index", "Home");
                    }
                }
            }
            ModelState.AddModelError("", "Faild to Login");
            return View();
        }

        public async Task<IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            return RedirectToAction("Index", "Home");
        }

        public IActionResult Register()
        {
            return View();
        }

        [HttpPost, ActionName("Register")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> RegisterPost(RegisterViewModel registerModel)
        {
            if (ModelState.IsValid)
            {
                AppUser user = new AppUser
                {
                    FirstName = registerModel.FirstName,
                    LastName = registerModel.LastName,
                    UserName = registerModel.Email,
                    PhoneNumber = registerModel.PhoneNumber,
                    Email = registerModel.Email
                };

                Profile profile = new Profile
                {
                    UserName = registerModel.Email,
                    FirstName = registerModel.FirstName,
                    LastName = registerModel.LastName,
                    FullName = registerModel.FirstName + " " + registerModel.LastName,
                    PhoneNumber = registerModel.PhoneNumber
                };

                var result = await _userManager.CreateAsync(user, registerModel.Password);
                if (result.Succeeded)
                {
                    bool roleExists = await _roleManager.RoleExistsAsync(registerModel.RoleName);
                    if (!roleExists)
                    {
                        await _roleManager.CreateAsync(new IdentityRole(registerModel.RoleName));
                    }

                    if (!await _userManager.IsInRoleAsync(user, registerModel.RoleName))
                    {
                        await _userManager.AddToRoleAsync(user, registerModel.RoleName);
                    }

                    if (!string.IsNullOrWhiteSpace(user.Email))
                    {
                        // Claim[] claim = new Claim(ClaimTypes.GivenName, user.FirstName);
                        Claim[] claims = new Claim[]
                        {
                          new Claim(ClaimTypes.Email, user.Email),
                          new Claim(ClaimTypes.GivenName, user.FirstName),
                          new Claim(ClaimTypes.Surname, user.LastName)
                        };
                        await _userManager.AddClaimsAsync(user, claims);
                    }

                    //Add profile data
                    _context.Profiles.Add(profile);
                    await _context.SaveChangesAsync();

                    var resultSignIn = await _signInManager.PasswordSignInAsync(registerModel.Email, registerModel.Password, registerModel.RememberMe, false);
                    if (resultSignIn.Succeeded)
                    {
                        HttpContext.Session.SetString("ProfileId", profile.ProfileId.ToString());
                        HttpContext.Session.SetString("ProfileImage", "favicon.ico");
                        return RedirectToAction("Index", "Roles");
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError("", error.Description);
                }
            }
            return View();
        }

        public IActionResult ChangePassword()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> ChangePassword(ChangePassworViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);

            }
            AppUser user = await _userManager.FindByNameAsync(User.Identity.Name);

            var result = await _userManager.ChangePasswordAsync(user, model.CurrentPassword, model.NewPassword);

            if (result.Succeeded)
            {
                ViewBag.Message = "Your password has been updated";
                return View();
            }
            return View();

        }

        public DeactivatedProfile DeactivatedCheck(int id)
        {
            return _context.DeactivatedProfiles.Where(e => e.ProfileId == id).FirstOrDefault();
        }

        public IActionResult VerifyContact()
        {
            var profile = _context.Profiles.Where(u => u.UserName == User.Identity.Name).FirstOrDefault();

            VerifyContactViewModel model = new VerifyContactViewModel
            {
                Email = profile.UserName,
                PhoneNumber = profile.PhoneNumber,
                Status = profile.ContactVerified
            };

            return View(model);
        }

        [HttpPost]
        public IActionResult VerifyContact(VerifyContactViewModel model)
        {
            if (ModelState.IsValid)
            {
                if (model.VerificationCode == "5186")
                {
                    var profile = _context.Profiles.Where(u => u.UserName == User.Identity.Name).FirstOrDefault();

                    profile.ContactVerified = "Verified";
                    _context.Profiles.Update(profile);
                    _context.SaveChanges();
                    ViewBag.Message = "Contact Verified";
                    return View(model);
                }
                else
                {
                    ModelState.AddModelError("", $"You entered wrong code.Please enter code sent on your email");
                    return View(model);
                }

            }

            return View(model);
        }

        public async Task<string> ConfirmContact()
        {
            var email = User.Identity.Name;
            await EmailService.SendEmailAsync(new MailRequest() { ToEmail = email, Subject = "Verification Code", Body = "Your Verification Code is:5186" });
            //Send verification code
            return "Verification Code Sent to your email";
        }

        public IActionResult ForgotPassword()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = await _userManager.FindByNameAsync(model.Email);
                if (user == null)
                {
                    // Don't reveal that the user does not exist or is not confirmed
                    return View("ForgotPasswordConfirmation");
                }

                // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
                // Send an email with this link
                string code = await _userManager.GeneratePasswordResetTokenAsync(user);
                var callbackUrl = Url.Action("ResetPassword", "Account", new { Email = user.Email, Code = code }, protocol: Request.Scheme);
                // await _userManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
                await EmailService.SendEmailAsync(new MailRequest() { ToEmail = user.Email, Subject = "Reset Password", Body = "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>" });
                return RedirectToAction("ForgotPasswordConfirmation", "Account");
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        // GET: /Account/ForgotPasswordConfirmation
        [AllowAnonymous]
        public IActionResult ForgotPasswordConfirmation()
        {
            return View();
        }

        //
        // GET: /Account/ResetPassword
        [AllowAnonymous]
        public IActionResult ResetPassword(string email, string code)
        {
            return code == null ? View("Error") : View();
        }

        // POST: /Account/ResetPassword
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            var user = await _userManager.FindByNameAsync(model.Email);
            if (user == null)
            {
                // Don't reveal that the user does not exist
                return RedirectToAction("ResetPasswordConfirmation", "Account");
            }
            var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("ResetPasswordConfirmation", "Account");
            }

            AddErrors(result);
            return View();
        }

        private void AddErrors(IdentityResult result)
        {
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError("", error.ToString());
            }
        }

        [AllowAnonymous]
        public IActionResult ResetPasswordConfirmation()
        {
            return View();
        }
    }
}


ViewModel Classes
LoginViewModel
using System.ComponentModel.DataAnnotations;

namespace A_MicrosoftAspNetCoreIdentityManagement.ViewModels
{
    public class LoginViewModel
    {
        [Required(ErrorMessage = "Please enter your email")]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }

        [Display(Name = "Password")]
        [Required(ErrorMessage = "Please enter your password.")]
        public string Password { get; set; }

        [Display(Name = "Remember Me")]
        public bool RememberMe { get; set; }
    }
}

RegisterViewModel
using System.ComponentModel.DataAnnotations;

namespace A_MicrosoftAspNetCoreIdentityManagement.ViewModels
{
    public class RegisterViewModel: LoginViewModel
    {
        [Display(Name = "First Name")]
        [Required(ErrorMessage = "Please enter your first name")]
        public string FirstName { get; set; }

        [Display(Name = "Last Name")]
        [Required(ErrorMessage = "Please enter your last name")]
        public string LastName { get; set; }

        [Display(Name = "Role Name")]
        [Required(ErrorMessage = "Please select a role")]
        public string RoleName { get; set; }

        [Display(Name = "Phone Number")]
        [Required(ErrorMessage = "Please enter your phone number")]
        [DataType(DataType.PhoneNumber)]
        public string PhoneNumber { get; set; }
    }
}

Register and Login Views
Create views for registration and login.

Register.cshtml
@model RegisterViewModel

@{
    ViewData["Title"] = "Register";
    Layout = "/Pages/Shared/_Layout.cshtml";
}

<!-- FORM POPUP -->
<div class="form-popup">

    <!-- /CLOSE BTN -->
    <!-- FORM POPUP HEADLINE -->
    <div class="form-popup-headline primary">
        <h2>Register Account</h2>
        <p>Register now and start making money from home!</p>
    </div>
    <!-- /FORM POPUP HEADLINE -->
    <!-- FORM POPUP CONTENT -->
    <div class="form-popup-content">
        <form id="register-form4" method="post" enctype="multipart/form-data" asp-action="Register">
            <div asp-validation-summary="ModelOnly" class="Error-Message"></div>
            <label asp-for="FirstName" class="rl-label"></label>
            <input asp-for="FirstName" type="text" placeholder="First Name">
            <span asp-validation-for="FirstName" class="Error-Message"></span>

            <label asp-for="LastName" class="rl-label"></label>
            <input asp-for="LastName" type="text" placeholder="Last Name">
            <span asp-validation-for="LastName" class="Error-Message"></span>

            <label asp-for="PhoneNumber" class="rl-label"></label>
            <input asp-for="PhoneNumber" type="text" placeholder="Phone Number">
            <span asp-validation-for="PhoneNumber" class="Error-Message"></span>

            <label asp-for="Email" class="rl-label"></label>
            <input asp-for="Email" type="email" placeholder="[email protected]">
            <span asp-validation-for="Email" class="Error-Message"></span>

            <label asp-for="Password" class="rl-label"></label>
            <input asp-for="Password" type="password" placeholder="Enter your password here...">
            <span asp-validation-for="Password" class="Error-Message"></span>

            <label asp-for="RoleName" class="rl-label"></label>
            <select asp-for="RoleName" class="form-control">
                <option value="">Select Role</option>
                <option value="Member">Member</option>
                <option value="Vendor">Vendor</option>
                <option value="Artist">Artist</option>
            </select>
            <span asp-validation-for="RoleName" class="Error-Message"></span>

            <button class="button mid dark">Register <span class="primary">Now!</span></button>
        </form>
    </div>
    <!-- /FORM POPUP CONTENT -->
</div>
<!-- /FORM POPUP -->
@section Scripts {
    <!-- jQuery -->
    <script src="~/js/vendor/jquery-3.1.0.min.js"></script>
    <script src="~/js/vendor/twitter/jquery.tweet.min.js"></script>
    <!-- Side Menu -->
    <script src="~/js/side-menu.js"></script>
    <!-- User Quickview Dropdown -->
    <script src="~/js/user-board.js"></script>
    <!-- Footer -->
    <script src="~/js/footer.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
}


Login.cshtml
@model LoginViewModel

@{
    ViewData["Title"] = "Login";
    Layout = "/Pages/Shared/_Layout.cshtml";
}

<!-- FORM POPUP -->
<div class="form-popup">

    <!-- FORM POPUP HEADLINE -->
    <div class="form-popup-headline secondary">
        <h2>Login to your Account</h2>
        <p>Enter now to your account and start buying and selling!</p>
    </div>
    <!-- /FORM POPUP HEADLINE -->
    <!-- FORM POPUP CONTENT -->
    <div class="form-popup-content">
        <form id="login-form2" method="post" enctype="multipart/form-data" asp-action="Login">
            <div asp-validation-summary="ModelOnly" class="Error-Message"></div>
            <label asp-for="Email" class="rl-label"></label>
            <input asp-for="Email" type="text" placeholder="[email protected]" required>
            <span asp-validation-for="Email" class="Error-Message"></span>
            <label asp-for="Password" class="rl-label"></label>
            <input asp-for="Password" type="password" placeholder="Enter your password here..." required>
            <span asp-validation-for="Password" class="Error-Message"></span>
            <!-- CHECKBOX -->
            <input asp-for="RememberMe" type="checkbox" checked>
            <label asp-for="RememberMe" class="label-check">
                <span class="checkbox primary primary"><span></span></span>
                Remember username and password
            </label>
            <span asp-validation-for="RememberMe" class="Error-Message"></span>
            <!-- /CHECKBOX -->
            <p>Forgot your password? <a asp-action="ForgotPassword" asp-controller="Account" class="primary">Click here!</a></p>
            <button class="button mid dark">Login <span class="primary">Now!</span></button>
        </form>
        <!-- LINE SEPARATOR -->
        <hr class="line-separator double">

    </div>
    <!-- /FORM POPUP CONTENT -->
</div>
<!-- /FORM POPUP -->
@section Scripts {
    <!-- jQuery -->
    <script src="~/js/vendor/jquery-3.1.0.min.js"></script>
    <script src="~/js/vendor/twitter/jquery.tweet.min.js"></script>
    <!-- Side Menu -->
    <script src="~/js/side-menu.js"></script>
    <!-- User Quickview Dropdown -->
    <script src="~/js/user-board.js"></script>
    <!-- Footer -->
    <script src="~/js/footer.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
}


Adding Email Confirmation and Password Recovery
Enable email confirmation and password recovery to enhance security.

Email Configuration

namespace A_MicrosoftAspNetCoreIdentityManagement.Configurations
{
    public class EmailConfiguration
    {
        public static string SenderEmail = "[email protected]";
        public static string Password = "test123/";
        public static string Host = "smtp.gmail.com";
        public static int Port = 465;
        public static bool UseSsl = true;
        public static string DisplayName = "MVC Support";
    }
}

Email Service

using A_MicrosoftAspNetCoreIdentityManagement.Configurations;
using MailKit.Net.Smtp;
using MimeKit;

namespace A_MicrosoftAspNetCoreIdentityManagement.Services
{
    public static class EmailService
    {
        public async static Task SendEmailAsync(MailRequest mailRequest)
        {
            var email = new MimeMessage();
            email.Sender = MailboxAddress.Parse(EmailConfiguration.SenderEmail);
            email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
            email.Subject = mailRequest.Subject;
            var builder = new BodyBuilder();
            if (mailRequest.Attachments != null)
            {
                byte[] fileBytes;
                foreach (var file in mailRequest.Attachments)
                {
                    if (file.Length > 0)
                    {
                        using (var ms = new MemoryStream())
                        {
                            file.CopyTo(ms);
                            fileBytes = ms.ToArray();
                        }
                        builder.Attachments.Add(file.FileName, fileBytes, ContentType.Parse(file.ContentType));
                    }
                }
            }
            builder.HtmlBody = mailRequest.Body;
            email.Body = builder.ToMessageBody();
            using var smtp = new SmtpClient();
            smtp.Connect(EmailConfiguration.Host, EmailConfiguration.Port, EmailConfiguration.UseSsl);
            smtp.Authenticate(EmailConfiguration.SenderEmail, EmailConfiguration.Password);
            await smtp.SendAsync(email);
            smtp.Disconnect(true);
        }
    }
    public class MailRequest
    {
        public string ToEmail { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
        public List<IFormFile> Attachments { get; set; }
    }
}

Enable Password Recovery
Add actions for password recovery.

[HttpPost]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await _userManager.FindByNameAsync(model.Email);
        if (user == null)
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }

        // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
        // Send an email with this link
        string code = await _userManager.GeneratePasswordResetTokenAsync(user);
        var callbackUrl = Url.Action("ResetPassword", "Account", new { Email = user.Email, Code = code }, protocol: Request.Scheme);
        // await _userManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
        await EmailService.SendEmailAsync(new MailRequest() { ToEmail = user.Email, Subject = "Reset Password", Body = "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>" });
        return RedirectToAction("ForgotPasswordConfirmation", "Account");
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}


Output

If our role is vendor, then the vendor dashboard will be shown on the screen.

If our role is buyer, then the buyer dashboard will be shown on the screen.

 



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: ASP.NET MVC: Transferring Values from Partial Views to Parent Views

clock May 31, 2024 09:09 by author Peter

In ASP.NET MVC applications, partial views are often used to simplify and modularize the user experience. On the other hand, there are times when communication between partial views and their parent views might be challenging, particularly when transmitting values or data. This article explores effective ways to transfer values from partial views to their parent views in ASP.NET MVC, hence enhancing interactivity and user experience.

What is partial views?
Partial views, denoted by the _ prefix convention in ASP.NET MVC, are reusable components that allow developers to encapsulate specific pieces of HTML and Razor code. These partial views can be shown inside of parent views through modularization and code reuse, which promotes a more structured and manageable codebase.

Challenges in passing values
Passing values or data from a partial view back to its parent view is a typical requirement in web development. This can be challenging since partial views are divided and do not offer direct access to the components or features of the parent view. However, there are several approaches that can break through this barrier and allow parent views and partial views to communicate with each other without difficulty.

Example
Let's look at an example where there is a form input field in a partial view, and the value entered has to be sent back to the parent view for processing.
Partial view (_MyPartialView.cshtml)

<div>
    <input type="text" id="inputValue" />
</div>
<script>
    var value = document.getElementById('inputValue').value;
    window.parent.updateMainView(value);
</script>


Parent view (Index.cshtml)

<h2>Main View</h2>
<div id="displayValue"></div>
<script>
    function updateMainView(value) {
        document.getElementById('displayValue').innerText = "Value from Partial View: " + value;
    }
</script>


Conclusion
In ASP.NET MVC development, variables from partial views must be passed to parent views in order to improve user experience and interactivity. Using JavaScript messaging or direct function invocation, developers can establish seamless communication channels across views, enabling dynamic data interchange and interactive user interfaces. By learning these techniques, developers may produce more dependable and engaging ASP.NET MVC web applications.



ASP.NET MVC 6 Hosting - HostForLIFE.eu :: How to Building an Interactive Location Selector with Google Maps in ASP.NET MVC?

clock May 16, 2024 10:05 by author Peter

Discover how to build a fully functional location selector feature for your ASP.NET MVC application using Google Maps API.

Introduction
This article provides a comprehensive, step-by-step guide to integrating Google Maps, allowing users to click on a map, select a location, and store it for future reference. From setting up Google Maps API to implementing JavaScript for map integration and handling user interactions, you'll find detailed instructions to create an interactive mapping feature. Additionally, learn how to seamlessly integrate database operations to store selected locations and retrieve them for display.

To achieve the functionality in ASP.NET MVC using Google Maps API, you can follow these steps.

1. Set up Google Maps API

Obtain a Google Maps API key from the Google Cloud Console and enable the necessary APIs (Maps JavaScript API, Geocoding API) for your project.

2. Create MVC Controller and Views
Create a controller with actions to handle the requests.
Create views for displaying the map, capturing the location, and displaying stored locations.

3. Implement JavaScript for Maps Integration

  • Use JavaScript to integrate Google Maps into your view.
  • Allow users to click on the map to select a location.
  • Retrieve the latitude and longitude of the selected location.
  • Display the selected location on the map.

4. Store Selected Location

  • When the user submits the selected location, send the latitude and longitude to the server.
  • Store the location in your database along with any additional information you want to associate with it.

5. Retrieve and Display Stored Locations

  • When the user wants to view stored locations, retrieve them from the database.
  • Display the stored locations as clickable links.
  • When a user clicks on a location link, display the location on the map.

Here's a basic example of how you can implement this.

Controller

public class LocationController : Controller
{
    public ActionResult Index()
    {
        // Display map for selecting location
        return View();
    }

    [HttpPost]
    public ActionResult SaveLocation(double latitude, double longitude, string locationName)
    {
        // Save the location in your database
        // Redirect to a success page or back to the map view
        return RedirectToAction("Index"); // Example redirection to Index action
    }

    public ActionResult DisplayLocations()
    {
        // Retrieve stored locations from the database
        // Display a view with clickable links to the stored locations
        return View();
    }
}


View (Index. cshtml)
<!-- Display Google Map -->
<div id="map" style="height: 400px; width: 100%;"></div>

<!-- Button to submit selected location -->
<button id="submitLocation">Submit Location</button>

<script>
    function initMap() {
        var map = new google.maps.Map(document.getElementById('map'), {
            center: { lat: -34.397, lng: 150.644 },
            zoom: 8
        });

        var marker;

        // Add click event listener to get selected location
        map.addListener('click', function (e) {
            var latitude = e.latLng.lat();
            var longitude = e.latLng.lng();

            // Clear previous marker
            if (marker) {
                marker.setMap(null);
            }

            // Display marker on selected location
            marker = new google.maps.Marker({
                position: { lat: latitude, lng: longitude },
                map: map
            });
        });

        // Event listener for submit button
        document.getElementById('submitLocation').addEventListener('click', function () {
            // Check if marker is defined
            if (marker) {
                // Get selected location data and submit to server
                var latitude = marker.getPosition().lat();
                var longitude = marker.getPosition().lng();
                var locationName = ""; // You may prompt the user for a name
                // Send latitude, longitude, and locationName to the server using AJAX
            } else {
                alert("Please select a location on the map.");
            }
        });
    }
</script>

<!-- Load Google Maps API -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" async defer></script>



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in