ASP.NET Core MVC

Building a .NET Core REST API with MySQL| Insert Update Delete with Image Upload

Building a .NET Core REST API with MySQL  CRUD with Image upload

Outline

    • Introduction
  • What We’ll Build
  • Project Setup
  • Installing Dependencies
  • Creating the User Model
  • Database Setup with MySQL
  • Setting up DbContext
  • Enabling Snake_Case JSON Serialization
  • Building the UsersController
  • Running Migrations
  • Testing with Swagger or Postman
  • Advanced Features (Optional)
    • Password Hashing
    • Image Upload
    • Data Validation
  • Full Source Code
  • Conclusion & Next Steps

Introduction

In this comprehensive tutorial, we will build a powerful REST API using ASP.NET Core and MySQL. Our API will handle common user fields such as first_name, last_name, email, password, and image. We’ll make sure the API works smoothly with Laravel or JavaScript frontends expecting snake_case JSON.

What We’ll Build

By the end of this guide, you’ll have a working .NET Core API that supports:

  • CRUD operations on users
  • JSON input/output in snake_case
  • Connection to a MySQL database
  • Modular architecture with controller, model, and DbContext

 

Project Structure

Here’s how your .NET Core Web API project should be organized. This structure ensures separation of concerns and makes your application easy to manage and scale.


UserApi/
├── Controllers/
│   └── UsersController.cs
├── Data/
│   └── AppDbContext.cs
├── Models/
│   └── User.cs
├── Migrations/
│   └── (auto-generated EF files after running migrations)
├── wwwroot/
│   └── images/        # Uploaded image files
├── JsonSnakeCaseNamingPolicy.cs
├── Program.cs
├── appsettings.json
├── UserApi.csproj

Notes:

  • Controllers – Holds your API endpoints
  • Models – Contains your data model classes
  • Data – Handles database configuration via DbContext
  • Migrations – Stores EF Core migration history
  • wwwroot – Serves static files like images

Project Setup

Open your terminal and run the following command to scaffold a new .NET Web API project:

dotnet new webapi -n UserApi
cd UserApi

This creates a folder UserApi and sets up a boilerplate REST API. Open this folder in your IDE (e.g., VS Code or Visual Studio).

Installing Dependencies

To use MySQL with Entity Framework Core, you need to install the appropriate NuGet packages. Run the following commands inside your project directory:

dotnet add package Pomelo.EntityFrameworkCore.MySql
dotnet add package Microsoft.EntityFrameworkCore.Design

Here’s what these packages do:

  • Pomelo.EntityFrameworkCore.MySql – MySQL database provider for Entity Framework Core
  • Microsoft.EntityFrameworkCore.Design – Required for running migrations and scaffolding

After running these commands, your project is ready to connect to a MySQL database.

Database Setup with MySQL

Before integrating with your database, ensure MySQL is installed and running. You can use tools like XAMPP, Laragon, or install MySQL directly on your machine.

Open a MySQL client (like phpMyAdmin or MySQL Workbench), and create a new database:

CREATE DATABASE user_api_db;

This database will store all user records. Be sure to note your MySQL username and password — you’ll need them for the connection string in the next step.

Setting up DbContext

In Entity Framework Core, the DbContext class is used to interact with the database. Let’s create one that links to the User model and connects to MySQL.

Inside your project, create a new folder named Data. Then add a new file named AppDbContext.cs with the following content:

using Microsoft.EntityFrameworkCore;
using UserApi.Models;

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

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

Next, open Program.cs and configure the context with your MySQL connection string:

using UserApi.Data;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    });

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseMySql(builder.Configuration.GetConnectionString(\"DefaultConnection\"), 
        new MySqlServerVersion(new Version(8, 0, 32))));

var app = builder.Build();

app.MapControllers();
app.Run();

Finally, in appsettings.json, add the MySQL connection string:

{
  \"ConnectionStrings\": {
    \"DefaultConnection\": \"server=localhost;database=user_api_db;user=root;password=your_password\"
  }
}

Replace your_password with your actual MySQL root password. Now your application is connected to MySQL through Entity Framework Core.

Enabling Snake_Case JSON Serialization

By default, .NET Core serializes JSON in camelCase. However, to make your API compatible with Laravel or other systems using snake_case, you’ll need to configure the JSON serializer.

Open your Program.cs file and modify the controller services configuration like this:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy();
    });

You now need to define the custom naming policy JsonSnakeCaseNamingPolicy. Create a new file named JsonSnakeCaseNamingPolicy.cs and add the following code:

using System.Text.Json;

public class JsonSnakeCaseNamingPolicy : JsonNamingPolicy
{
    public override string ConvertName(string name)
    {
        return string.Concat(
            name.Select((ch, i) =>
                i > 0 && char.IsUpper(ch)
                    ? \"_\" + ch.ToString().ToLower()
                    : ch.ToString().ToLower()
            )
        );
    }
}

This class tells .NET to transform property names like FirstName into first_name during JSON serialization and deserialization.

Building the UsersController

Now that everything is connected, let’s build the API controller for the /ap/users endpoint. Create a new folder called Controllers and add a file named UsersController.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using UserApi.Data;
using UserApi.Models;

namespace UserApi.Controllers
{
    [ApiController]
    [Route("ap/users")]
    public class UsersController : ControllerBase
    {
        private readonly AppDbContext _context;

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

        [HttpGet]
        public async Task<ActionResult> GetUsers()
        {
            var users = await _context.Users.ToListAsync();
            return Ok(users);
        }

        [HttpGet("{id}")]
        public async Task<ActionResult> GetUser(int id)
        {
            var user = await _context.Users.FindAsync(id);
            if (user == null) return NotFound();
            return Ok(user);
        }

        [HttpPost]
        public async Task<ActionResult> CreateUser(User user)
        {
            _context.Users.Add(user);
            await _context.SaveChangesAsync();
            return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
        }

        [HttpPut("{id}")]
        public async Task<ActionResult> UpdateUser(int id, User updatedUser)
        {
            if (id != updatedUser.Id) return BadRequest();

            _context.Entry(updatedUser).State = EntityState.Modified;
            await _context.SaveChangesAsync();

            return NoContent();
        }

        [HttpDelete("{id}")]
        public async Task<ActionResult> DeleteUser(int id)
        {
            var user = await _context.Users.FindAsync(id);
            if (user == null) return NotFound();

            _context.Users.Remove(user);
            await _context.SaveChangesAsync();

            return NoContent();
        }
    }
}

This controller includes all CRUD endpoints: list all users, get user by ID, create, update, and delete a user. All routes are based at /ap/users, just like you requested.

Running Migrations

Now that you’ve set up the model, DbContext, and controller, it’s time to generate and apply a migration so Entity Framework can create the MySQL database tables.

Open your terminal and run the following command to scaffold the initial migration:

dotnet ef migrations add InitialCreate

This creates a new folder called Migrations with files that describe how to create your database schema based on the User model.

Next, apply the migration to create the database structure in MySQL:

dotnet ef database update

If everything is set up correctly, you’ll now see the Users table inside your user_api_db database. You’re now ready to test the API.

Testing with Swagger or Postman

After building your controller and running the migration, it’s time to test your API endpoints.

Option 1: Using Swagger UI

By default, .NET Core includes Swagger support in development mode. Run your project:

dotnet run

Then visit https://localhost:5001/swagger in your browser. You’ll see a graphical interface where you can:

  • List all users
  • Create a user
  • Update a user
  • Delete a user

Option 2: Using Postman

You can also test the API with Postman. Here are the typical endpoints:

  • GET /ap/users – List all users
  • GET /ap/users/{id} – Get a specific user
  • POST /ap/users – Create a new user
  • PUT /ap/users/{id} – Update a user
  • DELETE /ap/users/{id} – Delete a user

Don’t forget to set the Content-Type to application/json in Postman when sending data.

Advanced Features (Optional)

Password Hashing

Storing plain-text passwords is insecure. Instead, hash them before saving. You can use BCrypt.Net or ASP.NET Core Identity.

Install the package:

dotnet add package BCrypt.Net-Next

Modify the POST method in UsersController:

user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);

Image Upload

To allow file uploads (e.g., profile images), change your POST action to accept multipart form data.

Update the model or store the image file path after saving it to a directory:


[HttpPost("upload")]
public async Task<IActionResult> UploadImage(IFormFile file)
{
    if (file == null || file.Length == 0)
        return BadRequest("No file uploaded.");

    var path = Path.Combine("wwwroot/images", file.FileName);

    using (var stream = new FileStream(path, FileMode.Create))
    {
        await file.CopyToAsync(stream);
    }

    return Ok(new { image = $"/images/{file.FileName}" });
}

Data Validation

Make use of data annotations like [Required], [MaxLength], and [EmailAddress] in your model to ensure proper validation.

You can also validate manually in your controller:


if (!ModelState.IsValid)
{
    return BadRequest(ModelState);
}

Full Source Code

Here’s a summary of all essential files and code used in this tutorial. You can copy/paste or organize these into your own project structure.

Models/User.cs

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public string? Image { get; set; }
}

Data/AppDbContext.cs

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<User> Users { get; set; }
}

Controllers/UsersController.cs

[ApiController]
[Route("ap/users")]
public class UsersController : ControllerBase
{
    private readonly AppDbContext _context;

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

    [HttpGet]
    public async Task<ActionResult> GetUsers() => Ok(await _context.Users.ToListAsync());

    [HttpGet("{id}")]
    public async Task<ActionResult> GetUser(int id)
    {
        var user = await _context.Users.FindAsync(id);
        return user == null ? NotFound() : Ok(user);
    }

    [HttpPost]
    public async Task<ActionResult> CreateUser(User user)
    {
        user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);
        _context.Users.Add(user);
        await _context.SaveChangesAsync();
        return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
    }

    [HttpPut("{id}")]
    public async Task<ActionResult> UpdateUser(int id, User user)
    {
        if (id != user.Id) return BadRequest();
        _context.Entry(user).State = EntityState.Modified;
        await _context.SaveChangesAsync();
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<ActionResult> DeleteUser(int id)
    {
        var user = await _context.Users.FindAsync(id);
        if (user == null) return NotFound();
        _context.Users.Remove(user);
        await _context.SaveChangesAsync();
        return NoContent();
    }
}

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy();
    });

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseMySql(
        builder.Configuration.GetConnectionString("DefaultConnection"),
        new MySqlServerVersion(new Version(8, 0, 32))
    ));

var app = builder.Build();
app.MapControllers();
app.Run();

JsonSnakeCaseNamingPolicy.cs

public class JsonSnakeCaseNamingPolicy : JsonNamingPolicy
{
    public override string ConvertName(string name)
    {
        return string.Concat(
            name.Select((ch, i) =>
                i > 0 && char.IsUpper(ch)
                    ? \"_\" + ch.ToString().ToLower()
                    : ch.ToString().ToLower()
            )
        );
    }
}

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "server=localhost;database=user_api_db;user=root;password=your_password"
  }
}

Conclusion & Next Steps

Congratulations! 🎉 You’ve successfully built a full-featured REST API using ASP.NET Core with a MySQL backend and Laravel-compatible snake_case JSON responses.

This API supports all CRUD operations, proper model validation, secure password handling, and even image uploads. You now have a solid foundation for building more advanced applications or integrating with frontend frameworks like Vue, React, or even mobile apps.

Next Steps

  • ✅ Add JWT authentication for user login and protected routes
  • ✅ Implement pagination, search, or filters for the users list
  • ✅ Create a Laravel frontend or Vue SPA to consume this API
  • ✅ Deploy your API to a cloud platform (like Azure, Heroku, or DigitalOcean)

Thank you for following this tutorial. If you found it helpful, consider sharing it or bookmarking it for future reference. Happy coding! 💻🚀

Leave a Reply

Your email address will not be published. Required fields are marked *