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 :: Building a User Subscription Module in ASP.NET MVC with C# 14

clock April 29, 2025 10:00 by author Peter

In modern web applications, subscription-based features are essential for managing user access, monetizing content, or offering tiered services. In this guide, we will build a comprehensive User Subscription Module using ASP.NET MVC (Model-View-Controller) and C# 14. The module will support:

  • User registration and login
  • Subscription plan selection
  • Payment integration (mocked)
  • Role-based access based on subscription level
  • Subscription management (upgrade/downgrade/cancel)

We’ll also take advantage of new C# 14 features such as primary constructors, collection expressions, field-backed properties, and extension members to make our code more concise and modern. These features help streamline the codebase and make it easier to read and maintain, especially in large applications with many interrelated components.

By the end of this tutorial, you'll have a functioning module that you can integrate into an existing ASP.NET MVC project. Whether you're building a SaaS platform or a content subscription service, the patterns and practices here will provide a strong foundation for building reliable, scalable subscription systems.

1. Project Setup
The first step in developing our subscription module is to use Visual Studio to create a new ASP.NET MVC application. Our data models, business logic, and user interface elements will be constructed upon this basis. Select the MVC template and set up your authentication preferences according to your user base's requirements when prompted during project creation. In order to enable Entity Framework and ASP.NET Identity, which are essential to our user and subscription management features, we must install a few NuGet packages after building the project. Microsoft, for instance. We may construct our database access layer and Microsoft.AspNet.Identity using EntityFramework. EntityFramework offers the ability to authenticate users.

Install-Package Microsoft.EntityFramework -Version 6.4.4
Install-Package Microsoft.AspNet.Identity.EntityFramework
Install-Package Stripe.net // Optional for real payment integration

These packages lay the groundwork for our entire system. With Entity Framework, we can interact with the database using strongly typed models, and Identity gives us a comprehensive system for managing users and roles. The Stripe package is optional but included for future scalability and integration.

Using NuGet simplifies the setup process and ensures that you get the latest, most secure versions of each dependency. This modular approach also makes the project easier to maintain, since each package can be updated independently of the others.

2. Define Models

To store and manage subscription data, we need to define our data models. These include SubscriptionPlan for defining available plans, UserSubscription to represent a user’s active or past subscriptions, and ApplicationUser which extends ASP.NET Identity’s user model. Each of these models plays a key role in organizing how data is stored and retrieved.

public class SubscriptionPlan
{
    public int Id { get; set; }

    public string Name
    {
        get => field;
        set => field = value ?? throw new ArgumentNullException(nameof(value));
    }

    public decimal Price { get; set; }
    public string Description { get; set; }
    public int DurationInDays { get; set; }
}

public class UserSubscription
{
    public int Id { get; set; }
    public string UserId { get; set; }
    public int PlanId { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public bool IsActive => DateTime.UtcNow <= EndDate;

    public virtual ApplicationUser User { get; set; }
    public virtual SubscriptionPlan Plan { get; set; }
}

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<UserSubscription> Subscriptions { get; set; }
}

These classes map to tables in our SQL database and help organize our business logic. The use of navigation properties (virtual) allows us to navigate related entities easily when using Entity Framework. The UserSubscription model is especially critical for tracking start and end dates, which will be used to determine access.

In C# 14, field-backed properties like Name in SubscriptionPlan help ensure data integrity with built-in validation. This allows us to enforce rules such as requiring names to be non-null directly in the model, rather than repeating this logic throughout the codebase.

3. Add Subscription Logic to DbContext

Next, we extend our DbContext to include our new models. This allows us to use EF to manage our tables and data.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public DbSet<SubscriptionPlan> SubscriptionPlans { get; set; }
    public DbSet<UserSubscription> UserSubscriptions { get; set; }

    public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { }

    public static ApplicationDbContext Create() => new();
}

This DbContext class bridges your application to the database. Entity Framework uses it to perform CRUD operations and generate SQL queries under the hood. The DbSet<T> properties tell EF which entities should be tracked and mapped to database tables.

The constructor and factory method (Create) show how to simplify context initialization, and using the latest syntax from C# 14 makes it even more concise. Keeping the context clean and modular helps when unit testing or transitioning to more advanced patterns like repository or unit-of-work.

4. Seed Subscription Plans
Add seed data to initialize plans in development or production environments.

context.SubscriptionPlans.AddOrUpdate(p => p.Name,
    new SubscriptionPlan { Name = "Free", Price = 0, Description = "Basic access", DurationInDays = 30 },
    new SubscriptionPlan { Name = "Pro", Price = 9.99M, Description = "Pro features", DurationInDays = 30 },
    new SubscriptionPlan { Name = "Enterprise", Price = 29.99M, Description = "All features", DurationInDays = 90 }
);

Seeding is essential to provide default options without requiring manual input every time the database is recreated. This is useful not only in development but also in CI/CD pipelines for automated deployments. These plans can be adjusted or extended later to suit business needs.

This code ensures that users always have valid plans to choose from. It's especially useful when the app is first launched, ensuring a smooth experience from the very first interaction.
5. Controllers and Views
Here’s a controller that uses a primary constructor:
[Authorize]
public class SubscriptionController(ApplicationDbContext db) : Controller
{
    public ActionResult Index() => View(db.SubscriptionPlans.ToList());

    public ActionResult Subscribe(int id) => View(db.SubscriptionPlans.Find(id));

    [HttpPost]
    public ActionResult SubscribeConfirmed(int id)
    {
        var userId = User.Identity.GetUserId();
        var plan = db.SubscriptionPlans.Find(id);

        var subscription = new UserSubscription
        {
            UserId = userId,
            PlanId = plan.Id,
            StartDate = DateTime.UtcNow,
            EndDate = DateTime.UtcNow.AddDays(plan.DurationInDays)
        };

        db.UserSubscriptions.Add(subscription);
        db.SaveChanges();

        return RedirectToAction("MySubscription");
    }

    public ActionResult MySubscription()
    {
        var userId = User.Identity.GetUserId();
        var activeSub = db.UserSubscriptions
            .Include("Plan")
            .Where(s => s.UserId == userId && s.IsActive)
            .OrderByDescending(s => s.EndDate)
            .FirstOrDefault();

        return View(activeSub);
    }
}


This controller manages the user's interaction with subscription plans. It retrieves available plans, handles subscription actions, and displays the user's current subscription. Using C# 14 primary constructors simplifies dependency injection, especially when there are few dependencies.

The controller is tied to Identity for user management, ensuring that actions like subscribing are tied to the correct user context. This integration of business logic and authentication is central to secure and personalized application flows.

6. Access Control Based on Subscription

Create a custom authorization attribute:
public class SubscriptionAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var userId = httpContext.User.Identity.GetUserId();
        var db = new ApplicationDbContext();
        var sub = db.UserSubscriptions.FirstOrDefault(x => x.UserId == userId && x.EndDate >= DateTime.UtcNow);
        return sub != null;
    }
}


Use it like this:
[SubscriptionAuthorize]
public ActionResult PremiumContent()
{
    return View();
}


This custom attribute checks if the logged-in user has an active subscription. It’s a lightweight and reusable solution to protect controller actions or entire controllers. It works well for scenarios where only subscribers should access certain parts of the application.

By encapsulating this logic into an attribute, we keep our controller actions clean and focused on what they need to do, rather than how authorization should be enforced. This improves both readability and maintainability.

7. Optional: Mock Payment Service

Handling payments is a critical part of any subscription system, but during development or for MVP testing, it's useful to mock this functionality. A mock payment service allows us to simulate successful transactions without integrating a third-party provider like Stripe or PayPal.

Below is a simple mock PaymentService class. It returns true to simulate a successful payment transaction, regardless of input. This allows you to develop and test your system logic without incurring real charges or managing payment API credentials.
public class PaymentService
{
    public bool ChargeUser(string userId, decimal amount)
    {
        // Simulate a successful payment
        Console.WriteLine($"Charging user {userId} for ${amount}");
        return true;
    }
}


You can integrate this into your subscription flow by calling it before saving the subscription. For example:
var paymentService = new PaymentService();
if (paymentService.ChargeUser(userId, plan.Price))
{
    var subscription = new UserSubscription
    {
        UserId = userId,
        PlanId = plan.Id,
        StartDate = DateTime.UtcNow,
        EndDate = DateTime.UtcNow.AddDays(plan.DurationInDays)
    };

    db.UserSubscriptions.Add(subscription);
    db.SaveChanges();
}

Using a mock now allows you to fully test your end-to-end subscription logic while making it simple to switch to a real provider later. When you're ready for live payments, just replace it PaymentService with a real implementation that connects to your gateway of choice.

8. Extension Members in C# 14

Use extension members to clean check the premium status:
public static class SubscriptionExtensions
{
    extension(UserSubscription subscription)
    {
        public bool IsPremium => ["Pro", "Enterprise"].Contains(subscription.Plan?.Name);
    }
}


Then in your code:

if (userSubscription.IsPremium)
{
    // Show premium features
}

Extension members let you encapsulate logic related to a class without modifying the class itself. This is ideal for computed properties like determining plan tier. It keeps your domain model lean while making your code more expressive. This feature is especially useful in scenarios where your business logic spans multiple types or you need utility-style operations across many parts of the app. You avoid cluttering your core model with too much logic.

9. Views

Here's a simple Index.cshtml:
@model IEnumerable<SubscriptionPlan>
<h2>Choose a Plan</h2>
@foreach (var plan in Model)
{
    <div>
        <h3>@plan.Name</h3>
        <p>@plan.Description</p>
        <p>Price: $@plan.Price</p>
        @Html.ActionLink("Subscribe", "Subscribe", new { id = plan.Id })
    </div>
}

This view lists all available subscription plans. Each plan includes a button that links to the subscribe confirmation page. The use of Razor syntax makes it easy to iterate through models and generate dynamic HTML. Views like this help users quickly understand the value proposition of each plan. With Bootstrap or Tailwind CSS, you could further enhance the design for a polished, responsive experience.

Conclusion

We built a user subscription module using ASP.NET MVC and C# 14, leveraging modern language features for better performance and readability. We explored models, controllers, access control, extension methods, and mocked payment processing. This provides a solid foundation for a SaaS or subscription-based application. Next steps might include adding webhook support, real payment integration (e.g., Square, Stripe), user dashboards, or analytics.



ASP.NET MVC Hosting - HostForLIFE.eu :: Knowing EF Core MVC's Connected Disconnected Scenarios

clock January 7, 2025 06:56 by author Peter

Working with data is essential in contemporary online applications, and Entity Framework Core (EF Core) offers a productive method of interacting with databases through the use of Object-Relational Mapping (ORM). The Connected and Disconnected scenarios are the two main methods that EF Core provides for handling data in an application. Both situations differ in how data is recorded, accessed, and stored in the database, and they are crucial in distinct use cases. Both of these ideas are examined in this article along with workable solutions in a.NET Core MVC application.

What is a Connected Scenario?
In a Connected scenario, the DbContext is directly connected to the entity instances and actively tracks changes made to the data. This is the default behavior of EF Core when entities are retrieved using a context and modified within the scope of the context.

  • Key Characteristics of a Connected Scenario
  • Change Tracking: EF Core automatically tracks changes made to the entities. When SaveChanges() is called, it updates the database with those changes.
  • Short-lived Context: The DbContext instance is created, used, and disposed of within a limited scope, often tied to a single HTTP request in a web application.
  • Automatic State Management: EF Core keeps track of the state of entities, such as Added, Modified, Deleted, and Unchanged.

Practical Example
Let’s create a simple CRUD operation in a Connected scenario for managing a list of users.

Create the Model
namespace EFCoreConnectedDisconnectedDemo.Model
{
    public class User
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Email { get; set; }
    }
}

Set Up DbContext
using EFCoreConnectedDisconnectedDemo.Model;
using Microsoft.EntityFrameworkCore;

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

        public DbSet<User> Users { get; set; }
    }
}

Create the controller

using EFCoreConnectedDisconnectedDemo.ApplicationContext;
using EFCoreConnectedDisconnectedDemo.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EFCoreConnectedDisconnectedDemo.Controllers
{
    public class UserController : Controller
    {
        private readonly ApplicationDbContext _context;

        public UserController(ApplicationDbContext context)
        {
            _context = context;
        }

        // GET: User
        public IActionResult Index()
        {
            var users = _context.Users.ToList();
            return View(users);
        }

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

        // POST: User/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create([Bind("Id, Name, Email")] User user)
        {
            if (ModelState.IsValid)
            {
                _context.Add(user);
                _context.SaveChanges(); // Save the changes in the connected scenario
                return RedirectToAction(nameof(Index));
            }
            return View(user);
        }

        // GET: User/Edit/5
        public IActionResult Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var user = _context.Users.Find(id);
            if (user == null)
            {
                return NotFound();
            }
            return View(user);
        }

        // POST: User/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(int id, [Bind("Id, Name, Email")] User user)
        {
            if (id != user.Id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(user);
                    _context.SaveChanges(); // EF Core automatically tracks changes and updates the database
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!_context.Users.Any(e => e.Id == user.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(user);
        }

        // GET: User/Delete/5
        public IActionResult Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var user = _context.Users
                .FirstOrDefault(m => m.Id == id);
            if (user == null)
            {
                return NotFound();
            }

            return View(user);
        }

        // POST: User/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(int id)
        {
            var user = _context.Users.Find(id);
            _context.Users.Remove(user);
            _context.SaveChanges(); // EF Core will automatically track the deletion
            return RedirectToAction(nameof(Index));
        }
    }
}


Create the Views
Index.cshtml, Create.cshtml, Edit.cshtml, and Delete.cshtml are Razor views for handling the respective CRUD actions.

How it Works in a Connected Scenario?

  • Tracking Changes: EF Core tracks changes made to User entities in the database automatically.
  • Saving Changes: Calling SaveChanges() saves the changes (whether added, updated, or deleted) to the database.

What is a Disconnected Scenario?
In a Disconnected scenario, the DbContext is not available to track changes once the data is retrieved from the database. This scenario is common in applications where the DbContext is disposed of after the entity data is fetched, such as in web APIs where entities are transferred over HTTP.

Key Characteristics of a Disconnected Scenario

  • No Change Tracking: Entities are not tracked once the DbContext is disposed of, meaning changes must be manually managed.
  • Manual State Management: In disconnected scenarios, the entity must be explicitly attached to the context and marked with its state (e.g., Modified) when changes are saved.
  • Common in Web APIs: Commonly used in Web API scenarios where entities are transferred to and from clients and must be handled after detaching from the context.

Practical Example
Let’s handle a simple scenario where data is fetched in a disconnected manner, then updated and saved.

Create a Web API Controller for Disconnected Scenario
using EFCoreConnectedDisconnectedDemo.ApplicationContext;
using EFCoreConnectedDisconnectedDemo.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EFCoreConnectedDisconnectedDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ApiUserController : ControllerBase
    {
        private readonly ApplicationDbContext _context;

        public ApiUserController(ApplicationDbContext context)
        {
            _context = context;
        }

        // GET: api/ApiUser/5
        [HttpGet("{id}")]
        public ActionResult<User> GetUser(int id)
        {
            var user = _context.Users.Find(id);
            if (user == null)
            {
                return NotFound();
            }
            return user;
        }

        // PUT: api/ApiUser/5
        [HttpPut("{id}")]
        public IActionResult PutUser(int id, User user)
        {
            if (id != user.Id)
            {
                return BadRequest();
            }

            // In disconnected scenario, we must attach the entity back to the context and mark it as modified
            _context.Users.Attach(user);
            _context.Entry(user).State = Microsoft.EntityFrameworkCore.EntityState.Modified;

            try
            {
                _context.SaveChanges(); // Save changes to the database manually
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!_context.Users.Any(e => e.Id == user.Id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }
    }
}


How it Works in a Disconnected Scenario?
The data is fetched and passed to the client (e.g., as JSON).

When the client sends an updated version of the entity, the entity must be explicitly attached to the DbContext.

The entity is marked as modified, and the changes are saved by calling SaveChanges().

When to Use Each Scenario?

  • Connected Scenario: Best suited for web applications or APIs where the DbContext remains active throughout the request lifecycle. It simplifies the development process since EF Core automatically handles change tracking.
  • Disconnected Scenario: Ideal for web APIs and scenarios where the DbContext is not available after data is retrieved. It requires explicit management of entity states and is suitable for distributed systems or client-server models.

Conclusion
In this post, we looked at how to use Entity Framework Core in an ASP.NET Core MVC application to manage connected and disconnected circumstances. Knowing the distinctions between these two methods can help you decide whether your application needs manual state management (disconnected) or automatic change monitoring (connected) for data management. Both situations are necessary for developing effective and reliable data-driven applications and are crucial in various use cases.



ASP.NET MVC Hosting - HostForLIFE.eu :: How to Mapping with Conditional Display in a ASP.NET MVC Application?

clock August 27, 2024 07:47 by author Peter

Keeping resumes organized across several job vacancies is essential in the recruitment business. This blog post explores the effective mapping of resumes to various job listings within a.NET MVC application. We'll go over the structure of the database, the reasoning needed to link resumes to job IDs, and the procedures to make sure that resumes associated with closed jobs are removed from consideration for open positions.

With the help of this guide, you will be able to optimize your job and resume management process in.NET MVC, regardless of whether you're creating a recruiting portal or improving existing HR administration system.

Here's how to approach your.NET MVC project to meet the requirements.

1. Database Design

  • Job Table
    • JobID (Primary Key)
    • JobStatus (e.g., Active, Closed)
  • Resume Table
    • ResumeID (Primary Key)
    • Other resume details.
  • JobResumeMapping Table
    • JobID (Foreign Key to Job Table)
    • ResumeID (Foreign Key to Resume Table)
    • MappingID (Primary Key for the table)

2. Mapping Resumes to Different Job IDs
You would create or update records in the JobResumeMapping table to map resumes to different jobs.
Example method to map a resume to a job.

public void MapResumeToJob(int jobId, int resumeId)
{
    using (var context = new YourDbContext())
    {
        var mapping = new JobResumeMapping
        {
            JobID = jobId,
            ResumeID = resumeId
        };
        context.JobResumeMappings.Add(mapping);
        context.SaveChanges();
    }
}

3. Preventing Resume from Showing in Other Jobs once the Job is closed
You should filter out resumes mapped to closed jobs.
Example of how to do it in a query.

    var openJobsWithResumes = context.JobResumeMappings
                                      .Where(jrm => jrm.Job.JobStatus == "Active")
                                      .Include(jrm => jrm.Resume)
                                      .ToList();


Alternatively, you can use a method to update the status of the job and remove the mapping.
public void CloseJob(int jobId)
{
    using (var context = new YourDbContext())
    {
        var job = context.Jobs.Find(jobId);
        if (job != null)
        {
            job.JobStatus = "Closed";
            context.SaveChanges();
        }
    }
}


4. Ensure UI Logic
In your view, only show resumes linked to jobs that are not closed.

@foreach (var job in Model.Jobs)
{
    if (job.JobStatus == "Active")
    {
        // Display resumes mapped to this job
    }
}

5. Additional Considerations
To preserve referential integrity, you could also choose to use update operations or cascade deletes. The mappings must to be updated or eliminated in the event that a job is deleted.

6. Business Logic in the Controller

Make sure the logic for closing jobs and filtering resumes is handled by your controller methods.
Your demands for matching resumes to various jobs should be met by this configuration, which also makes sure that resumes linked to closed jobs are removed from consideration for open positions.



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": "your-email@gmail.com",
    "Username": "your-email@gmail.com",
    "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 :: 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 admin@artisan.com");
                    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="someone@example.com">
            <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="someone@example.com" 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 = "noreply.testpk@gmail.com";
        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 :: Consuming ASP.NET Web API REST Service In ASP.NET MVC Using HttpClient

clock March 28, 2024 10:10 by author Peter

In numerous forum postings, developers and students have addressed the same question: how to utilize Web API REST Service in an ASP.NET MVC application and how to initiate a call between them to exchange data. So, in response to this need, I decided to write this post to show how to use HttpClient to consume the ASP.NET Web API REST Service in an ASP.NET MVC application.

Prerequisites
If you're not sure what a Web API REST service is or how to construct, publish, and host an ASP.NET Web API REST Service, please watch my video and read the papers linked below. Follow the same steps if you want to learn how to create, host, and consume web API REST services in client applications.

In this article, we will use the same hosted Web API REST service to consume in our created ASP.NET MVC web application. Now, let's start consuming Web API REST service in the ASP.NET MVC application step by step.

Step 1. Create an MVC Application
Start, followed by All Programs, and select Microsoft Visual Studio 2015.
Click File, followed by New, and click Project. Select ASP.NET Web Application Template, provide the Project a name as you wish, and click OK.
After clicking, the following Window will appear. Choose an empty project template and check on the MVC option.

The preceding step creates the simple empty ASP.NET MVC application without a Model, View, and Controller, The Solution Explorer of created web application will look like the following.

Step 2. Install HttpClient library from NuGet
We are going to use HttpClient to consume the Web API REST Service, so we need to install this library from NuGet Package Manager .

What is HttpClient?

HttpClient is base class which is responsible to send HTTP request and receive HTTP response resources i.e from REST services.
To install HttpClient, right click on Solution Explorer of created application and search for HttpClient, as shown in the following image.

Now, click on Install button after choosing the appropriate version. It will get installed after taking few seconds, depending on your internet speed.

Step 3. Install WebAPI.Client library from NuGet
This package is used for formatting and content negotiation which provides support for System.Net.Http. To install, right click on Solution Explorer of created application and search for WebAPI.Client, as shown in following image.


Now, click on Install button after choosing the appropriate version. It will get installed after taking few seconds depending on your internet speed. We have installed necessary NuGet packages to consume Web API REST services in web application. I hope you have followed the same steps.

Step 4. Create Model Class
Now, let us create the Model class named Employee.cs or as you wish, by right clicking on Models folder with same number of entities which are exposing by our hosted Web API REST service to exchange the data. The code snippet of created Employee.cs class will look like this.

Employee.cs
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
}

Step 5. Add Controller Class
Now, let us add ASP.NET MVC controller

After clicking Add button, it will show in the Window. Specify the Controller name as Home with suffix Controller. Now, let's modify the default code of Home controller .

Our hosted Web API REST Service includes these two methods, as given below.

  • GetAllEmployees (GET )
  • GetEmployeeById (POST ) which takes id as input parameter

We are going to call GetAllEmployees method which returns the all employee details ,The hosted web api REST service base URL is http://192.168.95.1:5555/ and to call GetAllEmployees from hosted web API REST service, The URL should be Base url+api+apicontroller name +web api method name as following http://192.168.95.1:5555/api/Employee/GetAllEmployees.

In the preceding url

  • http://localhost:56290 Is the base address of web API service, It can be different as per your server.
  • api It is the used to differentiate between Web API controller and MVC controller request .
  • Employee This is the Web API controller name.
  • GetAllEmployees This is the Web API method which returns the all employee list.

After modifying the code of Homecontroller class, the code will look like the following.

Homecontroller.cs
using ConsumingWebAapiRESTinMVC.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace ConsumingWebAapiRESTinMVC.Controllers
{
    public class HomeController : Controller
    {
        //Hosted web API REST Service base url
        string Baseurl = "http://192.168.95.1:5555/";
        public async Task<ActionResult> Index()
        {
            List<Employee> EmpInfo = new List<Employee>();
            using (var client = new HttpClient())
            {
                //Passing service base url
                client.BaseAddress = new Uri(Baseurl);
                client.DefaultRequestHeaders.Clear();
                //Define request data format
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                //Sending request to find web api REST service resource GetAllEmployees using HttpClient
                HttpResponseMessage Res = await client.GetAsync("api/Employee/GetAllEmployees");
                //Checking the response is successful or not which is sent using HttpClient
                if (Res.IsSuccessStatusCode)
                {
                    //Storing the response details recieved from web api
                    var EmpResponse = Res.Content.ReadAsStringAsync().Result;
                    //Deserializing the response recieved from web api and storing into the Employee list
                    EmpInfo = JsonConvert.DeserializeObject<List<Employee>>(EmpResponse);
                }
                //returning the employee list to view
                return View(EmpInfo);
            }
        }
    }
}


I hope, you have gone through the same steps and understood about the how to use and call Web API REST service resource using HttpClient .

Step 6. Create strongly typed View
Now, right click on Views folder of the created application and create strongly typed View named by Index by choosing Employee class to display the employee list from hosted web API REST Service, as shown in the following image.

Now, click on Add button. It will create View named index after modifying the default code. The code snippet of the Index View looks like the following.

Index.cshtml

@model IEnumerable<ConsumingWebAapiRESTinMVC.Models.Employee>
@{
    ViewBag.Title = "www.compilemode.com";
}
<div class="form-horizontal">
    <hr />
    <div class="form-group">
        <table class="table table-responsive" style="width:400px">
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.Name)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.City)
                </th>
            </tr>
            @foreach (var item in Model) {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.Name)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.City)
                    </td>
                </tr>
            }
        </table>
    </div>
</div>


The preceding View will display all employees list . Now, we have done all the coding.

Step 7. Run the Application
After running the Application, the employee list from hosted web API REST service.

I hope, from the above examples, you have learned how to consume Web API REST Service in ASP.NET MVC using HttpClient.

Note

  • Download the Zip file of the Published code to learn and start quickly.
  • This article is just a guideline on how to consume Web API REST Service in ASP.NET MVC application using HttpClient.
  • In this article, the optimization is not covered in depth; do it as per your skills.




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