Using ASP.NET Core and Vue to Build Application – Windows ASP.NET Core Hosting 2024 | Review and Comparison

By building a checklist app, I’ll show you the fundamentals of constructing your own web application in this tutorial. The frontend UI will be built using Vue, and the CRUD API will be built using ASP NET Core. You should be able to create your own web apps using the knowledge you’ve learned here. The GitHub repository contains the entire solution.

Building the API will come first, and then we’ll work on the Vue client.

Creating a checklist API

Create a new ASP NET Core Web API project in Visual Studio to get started.

Let’s begin by developing the model for the check-list items. Make a folder called Models and a file called ChecklistItem.cs inside of it.

public class ChecklistItem
{
    public int Id { get; set; }
    public string Text { get; set; }
}

We’ve given it a Text property, which will hold the text that we provide for the checklist item, and an Id, which will help identify this item specifically when we store it to a database.

We’ll configure the database next. I’m utilizing an in-memory database to make this tutorial more understandable. This is good for testing and demonstration reasons, but if you want to build a real app, you’ll need to establish your own database. To do this in code, just switch the data provider setting in the EF Core settings.

Install the upcoming NuGet packages first:

Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.InMemory

Next, add a new file called AppDbContext.cs to the project’s root folder:

using ASPNETCoreVueChecklist.Models;
using Microsoft.EntityFrameworkCore;

namespace ASPNETCoreVueChecklist
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions options) : base(options)
        {
        }

        public DbSet<ChecklistItem> ChecklistItems { get; set; }
    }
}

The object-relational mapper (ORM) EF Core makes it easier for C# programs to communicate with databases. The database’s records can be accessed via the AppDbContext class. This tells EF Core to look for a table in the database called ChecklistItems with columns provided by our model by giving it a property called ChecklistItems with a type of DbSetCheckListItem>.

Then, navigate to the ConfigureServices method of Startup.cs and add the following lines of code to configure our program to use this AppDbContext class and an in-memory database:

services.AddDbContext<AppDbContext>(options =>
{
    options.UseInMemoryDatabase(nameof(AppDbContext));
});

The controller, which in ASP NET Core defines the endpoints for our API, must be created last. Create a ChecklistController.cs file in the Controllers folder to begin with:

using Microsoft.AspNetCore.Mvc;

namespace ASPNETCoreVueChecklist.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ChecklistController : ControllerBase
    {
        private readonly AppDbContext _dbContext;

        public ChecklistController(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }
    }
}

Currently, we are injecting an instance of our AppDbContext class into the controller so that we can use it to access our checklist items. The controller also has an ApiController attribute, which configures the controller to be used for an API (instead of a standard ASP NET MVC controller), the Route attribute, which states that all endpoints will be prefixed by the name of the controller (checklist), and the Route attribute.

I’ll now demonstrate how to include each of the CRUD methods in the controller, starting with Create:

[HttpPost]
public async Task<int> Create(ChecklistItem item)
{
    _dbContext.ChecklistItems.Add(item);
    await _dbContext.SaveChangesAsync();

    return item.Id;
}

Since the aforementioned method includes the HttpPost attribute, only the POST method of an HTTP request can be used to access it. For creating records in APIs, this is normal practice. Using the text parameter that is supplied to the method, we build a fresh instance of the ChecklistItem class. We don’t have to bother about manually setting the Id because it will be done when we save the item to the database. The object is first added to the database and then saved in the lines that follow. Finally, we give the client the new item ID.

The next step is to read. Such an API often has two read methods: one that returns all items and one that returns the one item that matches the provided Id.

[HttpGet]
public async Task<IEnumerable<ChecklistItem>> Get()
{
    var items = await _dbContext.ChecklistItems.ToListAsync();

    return items;
}

[HttpGet("{id}")]
public async Task<ChecklistItem> Get(int id)
{
    var item = await _dbContext.ChecklistItems.FirstOrDefaultAsync(item => item.Id == id);

    return item;
}

When the HTTP GET method is performed on the controller (/checklist), the first method is configured to return a list of all checklist items. The second is similar to the first, with the exception that we’ve set the URL (/checklist/1) to need the Id of the checklist item. The database will be searched for an item with that Id using the Id as a parameter. The client will then receive that specific Id back from the system.

The next method is Update:

[HttpPut("{id}")]
public async Task<bool> Update(int id, ChecklistItem item)
{
    var existingItem = await _dbContext.ChecklistItems.FirstOrDefaultAsync(i => i.Id == id);
    existingItem.Text = item.Text;
    var result = await _dbContext.SaveChangesAsync();

    return result > 0;
}
The HTTP PUT method is typically used for updating, so we’ve set the route to need the Id of the item we want to update (checklist/1). To change an item, we first need to fetch it, edit the text, and then save it again in the database. The number of items that were modified is represented by an integer in SaveChangeAsync’s return value. Therefore, we can determine whether the update was successful by seeing if the number of changed items is larger than 0.

Finally we have the Delete method:

[HttpDelete("{id}")]
public async Task<bool> Delete(int id)
{
    var item = await _dbContext.ChecklistItems.FirstOrDefaultAsync(item => item.Id == id);
    _dbContext.ChecklistItems.Remove(item);
    var result = await _dbContext.SaveChangesAsync();

    return result > 0;
}
The Id of the object that is being removed is supplied in the URL, just like in the previous methods, but this time we utilize the HTTP DELETE method. The process of deleting items in EF Core entails fetching the item from the database, configuring it to be removed, saving the database, and then deleting the record. This may sound a little strange.
Going to Startup.cs and adding a CORS policy to the Configure method is the last thing we need to accomplish. This ought should show up between the app.App and UseHttpsRedirection().Use lines for routing(). This enables our client to send queries to the web API (NOTE: The default port for new Vue apps is 8080; if your client’s port is different, alter the code to use your client’s port).
 app.UseCors(builder =>
{
    builder
        .WithOrigins("http://localhost:8080")
        .AllowAnyMethod()
        .AllowAnyHeader();
});
Our API has now been finished. All CRUD operations have been implemented and may be accessed using different HTTP methods. The next step will be to build a Vue frontend that can access these API calls.

Creating a checklist client

Initially, confirm that the Vue CLI is installed. If not, kindly go to this website. Then, to create a Vue project, go to the root folder of your project and issue the following command:

vue create checklist-client

Make an empty Checklist.vue file in the components folder to get things started. After that, change App.vue so that it just shows the Checklist component.

<template>
  <div id="app">
    <Checklist/>
  </div>
</template>

<script>
import Checklist from './components/Checklist.vue'

export default {
  name: 'App',
  components: {
    Checklist,
  }
}
</script>

Open the Checklist.vue file next. To begin, let’s establish an input to generate a list of items:

<template>
  <div>
    <input type="text" v-model="newItemText" />
    <button @click="onClickSave"> Save </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      newItemText: ''
    }
  },
  methods: {
    async onClickSave() {
      await fetch('https://localhost:5001/checklist', {
        method: 'POST',
        data: this.newItemText
      })

      this.newItemText = ''
    }
  }
}
</script>

The newItemText data property is bound to our input in this case, ensuring that any changes to the value are reflected in both the input and the property. We also design a save button that, when clicked, invokes the onClickSave method. We make a POST request to our API at the /checklist endpoint from the onClickSave method. The input field’s text was all that was included in the data. The input text will be cleared if the request is granted.

Please be aware that the port I used might not match yours. To find out which port your API is using, kindly check your own API project.

We can add new items to the checklist now, but we can’t see them. Let’s compile a list of the things we took out of the database. Make a data property called items first:

data() {
  return {
    newItemText: '',
    items: []
  }
},

Then inside the methods object, add a loadItems method:

async loadItems() {
  await fetch('https://localhost:5001/checklist', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ text: this.newItemText })
  })

  this.newItemText = ''
}

This will send an HTTP GET request to /checklist (the default fetch method; we don’t need to define it explicitly). The response is then converted to JSON, and the list of things that results is then set to the newly formed items property.

When we initially visit the page, we want to load this information. We use the mounted method, which is located at the root of the Vue object, to accomplish this. For more information, please refer to the GitHub repository), which is called when the page first loads:

async mounted() {
  await this.loadItems()
},

We should also include this line (wait for this.Add loadItems()) at the end of the onClickSave method to refresh the list whenever a new item is added.

Then, to display the data that we just fetched, we will make the list items in the markup:

<ul>
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</ul>

The user must be given the option to delete already-existing items as the last step. Let’s devise a procedure that will enable us to do that:

async onClickDelete(id) {
  await fetch(`https://localhost:5001/checklist/${id}`, {
    method: 'DELETE'
  })

  await this.loadItems()
}

Then add a button that deletes each list item to each list item. The onClickDelete method receives the item Id, which is then sent as a URL parameter to the /checklist/:id endpoint.

<li v-for="item in items" :key="item.id">
  {{ item.text }}
  <button @click="onClickDelete(item.id)"> Delete </button>
</li>

The end of that. Open the Vue project in the browser after making sure both projects are active. You ought to now be able to add new items, view a list of your current items, and remove current items.

Conclusion

In this article, I’ve demonstrated how to use ASP.NET Core to build a simple CRUD API and connect it to a Vue frontend to build a checklist web application.