RxJS with Dotnet Core Web API

Create Angular Service with RxJS Operators

We'll use operators like map, catchError, switchMap, etc., to handle the responses and errors effectively.

// crud.service.ts

import { Injectable } from '@angular/core';

import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Observable, throwError } from 'rxjs';

import { catchError, map } from 'rxjs/operators';

 

@Injectable({

  providedIn: 'root'

})

export class CrudService {

  private apiUrl = 'https://your-api-url/api/items'; // Replace with your .NET Core API endpoint

 

  constructor(private http: HttpClient) { }

 

  // Get all items

  getItems(): Observable<any[]> {

    return this.http.get<any[]>(this.apiUrl).pipe(

      map(data => data), // You can transform the data here if needed

      catchError(this.handleError) // Handle errors here

    );

  }

 

  // Get a single item by ID

  getItemById(id: number): Observable<any> {

    return this.http.get<any>(`${this.apiUrl}/${id}`).pipe(

      catchError(this.handleError) // Handle errors here

    );

  }

 

  // Create a new item

  createItem(item: any): Observable<any> {

    return this.http.post<any>(this.apiUrl, item, {

      headers: new HttpHeaders({

        'Content-Type': 'application/json'

      })

    }).pipe(

      catchError(this.handleError) // Handle errors here

    );

  }

 

  // Update an existing item

  updateItem(id: number, item: any): Observable<any> {

    return this.http.put<any>(`${this.apiUrl}/${id}`, item, {

      headers: new HttpHeaders({

        'Content-Type': 'application/json'

      })

    }).pipe(

      catchError(this.handleError) // Handle errors here

    );

  }

 

  // Delete an item

  deleteItem(id: number): Observable<any> {

    return this.http.delete<any>(`${this.apiUrl}/${id}`).pipe(

      catchError(this.handleError) // Handle errors here

    );

  }

 

  // Handle HTTP errors

  private handleError(error: any) {

    console.error('An error occurred:', error);

    return throwError(() => new Error('Something went wrong; please try again later.'));

  }

}

2. Update Component with RxJS Operators

Now let's use RxJS operators like map, switchMap, and catchError in the component to manage asynchronous data flow.

// crud.component.ts

import { Component, OnInit } from '@angular/core';

import { CrudService } from './crud.service';

import { catchError, of } from 'rxjs';

 

@Component({

  selector: 'app-crud',

  templateUrl: './crud.component.html',

  styleUrls: ['./crud.component.css']

})

export class CrudComponent implements OnInit {

  items: any[] = [];

  currentItem: any = { id: 0, name: '' }; // Example object

  errorMessage: string = '';

 

  constructor(private crudService: CrudService) {}

 

  ngOnInit(): void {

    this.loadItems();

  }

 

  loadItems(): void {

    this.crudService.getItems().pipe(

      catchError(err => {

        this.errorMessage = 'Failed to load items';

        return of([]); // Return empty array on error

      })

    ).subscribe(

      (data: any[]) => {

        this.items = data;

      },

      error => {

        console.error('Error loading items', error);

      }

    );

  }

 

  createItem(): void {

    this.crudService.createItem(this.currentItem).pipe(

      catchError(err => {

        this.errorMessage = 'Failed to create item';

        return of(null); // Return null on error

      })

    ).subscribe(

      (data) => {

        if (data) {

          this.items.push(data); // Add the newly created item to the list

          this.resetForm();

        }

      },

      error => {

        console.error('Error creating item', error);

      }

    );

  }

 

  updateItem(): void {

    this.crudService.updateItem(this.currentItem.id, this.currentItem).pipe(

      catchError(err => {

        this.errorMessage = 'Failed to update item';

        return of(null); // Return null on error

      })

    ).subscribe(

      (data) => {

        if (data) {

          const index = this.items.findIndex(item => item.id === data.id);

          if (index !== -1) {

            this.items[index] = data; // Update the item in the list

          }

          this.resetForm();

        }

      },

      error => {

        console.error('Error updating item', error);

      }

    );

  }

 

  deleteItem(id: number): void {

    this.crudService.deleteItem(id).pipe(

      catchError(err => {

        this.errorMessage = 'Failed to delete item';

        return of(null); // Return null on error

      })

    ).subscribe(

      () => {

        this.items = this.items.filter(item => item.id !== id); // Remove the item from the list

      },

      error => {

        console.error('Error deleting item', error);

      }

    );

  }

 

  resetForm(): void {

    this.currentItem = { id: 0, name: '' }; // Reset form fields

  }

 

  editItem(item: any): void {

    this.currentItem = { ...item }; // Set the form with the current item's data

  }

}

3. HTML Template

Your HTML template remains the same as before, where it handles the interaction with the user.

<!-- crud.component.html -->

<div>

  <h2>CRUD Operations</h2>

 

  <!-- Error Message -->

  <div *ngIf="errorMessage" class="error">{{ errorMessage }}</div>

 

  <!-- Form for creating and editing items -->

  <div>

    <input [(ngModel)]="currentItem.name" placeholder="Item Name" />

    <button (click)="currentItem.id ? updateItem() : createItem()">

      {{ currentItem.id ? 'Update Item' : 'Create Item' }}

    </button>

  </div>

 

  <!-- List of items -->

  <ul>

    <li *ngFor="let item of items">

      {{ item.name }}

      <button (click)="editItem(item)">Edit</button>

      <button (click)="deleteItem(item.id)">Delete</button>

    </li>

  </ul>

</div>

 

 

 

 

To implement a .NET Core Web API that works with the Angular service using RxJS operators (like map, catchError, switchMap), the API needs to support the CRUD operations and handle errors effectively. Below is the code for your .NET Core Web API that matches the Angular service with RxJS handling.

1. Model (Item)

The Item model represents the structure of the data. This is similar to what Angular expects when interacting with the API.

// Models/Item.cs

namespace CrudApi.Models

{

    public class Item

    {

        public int Id { get; set; }

        public string Name { get; set; }

    }

}

2. Database Context

Here, we define the database context using Entity Framework Core to interact with the database.

// Data/AppDbContext.cs

using Microsoft.EntityFrameworkCore;

using CrudApi.Models;

 

namespace CrudApi.Data

{

    public class AppDbContext : DbContext

    {

        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

 

        public DbSet<Item> Items { get; set; }

    }

}

3. Controller (ItemsController)

The controller defines the API endpoints to handle the CRUD operations for Item resources. It also handles errors and returns meaningful responses using try-catch blocks and returns proper HTTP status codes.

// Controllers/ItemsController.cs

using Microsoft.AspNetCore.Mvc;

using Microsoft.EntityFrameworkCore;

using CrudApi.Data;

using CrudApi.Models;

 

namespace CrudApi.Controllers

{

    [Route("api/[controller]")]

    [ApiController]

    public class ItemsController : ControllerBase

    {

        private readonly AppDbContext _context;

 

        public ItemsController(AppDbContext context)

        {

            _context = context;

        }

 

        // GET: api/items

        [HttpGet]

        public async Task<ActionResult<IEnumerable<Item>>> GetItems()

        {

            try

            {

                var items = await _context.Items.ToListAsync();

                return Ok(items);

            }

            catch (Exception ex)

            {

                return StatusCode(500, $"Internal server error: {ex.Message}");

            }

        }

 

        // GET: api/items/5

        [HttpGet("{id}")]

        public async Task<ActionResult<Item>> GetItem(int id)

        {

            try

            {

                var item = await _context.Items.FindAsync(id);

 

                if (item == null)

                {

                    return NotFound($"Item with id {id} not found.");

                }

 

                return Ok(item);

            }

            catch (Exception ex)

            {

                return StatusCode(500, $"Internal server error: {ex.Message}");

            }

        }

 

        // POST: api/items

        [HttpPost]

        public async Task<ActionResult<Item>> CreateItem(Item item)

        {

            try

            {

                _context.Items.Add(item);

                await _context.SaveChangesAsync();

 

                return CreatedAtAction(nameof(GetItem), new { id = item.Id }, item);

            }

            catch (Exception ex)

            {

                return StatusCode(500, $"Internal server error: {ex.Message}");

            }

        }

 

        // PUT: api/items/5

        [HttpPut("{id}")]

        public async Task<IActionResult> UpdateItem(int id, Item item)

        {

            if (id != item.Id)

            {

                return BadRequest("Item ID mismatch.");

            }

 

            try

            {

                _context.Entry(item).State = EntityState.Modified;

                await _context.SaveChangesAsync();

                return NoContent();

            }

            catch (DbUpdateConcurrencyException)

            {

                if (!ItemExists(id))

                {

                    return NotFound($"Item with id {id} not found.");

                }

                else

                {

                    throw;

                }

            }

            catch (Exception ex)

            {

                return StatusCode(500, $"Internal server error: {ex.Message}");

            }

        }

 

        // DELETE: api/items/5

        [HttpDelete("{id}")]

        public async Task<IActionResult> DeleteItem(int id)

        {

            try

            {

                var item = await _context.Items.FindAsync(id);

                if (item == null)

                {

                    return NotFound($"Item with id {id} not found.");

                }

 

                _context.Items.Remove(item);

                await _context.SaveChangesAsync();

 

                return NoContent();

            }

            catch (Exception ex)

            {

                return StatusCode(500, $"Internal server error: {ex.Message}");

            }

        }

 

        private bool ItemExists(int id)

        {

            return _context.Items.Any(e => e.Id == id);

        }

    }

}

4. Dependency Injection and Database Setup

In the Program.cs (for .NET 6 and above), you need to configure the dependency injection and connect the AppDbContext to your database.

// Program.cs (for .NET 6+)

using Microsoft.EntityFrameworkCore;

using CrudApi.Data;

 

var builder = WebApplication.CreateBuilder(args);

 

// Add services to the container.

builder.Services.AddControllers();

 

// Set up the database context with an InMemoryDatabase for testing (replace with actual DB).

builder.Services.AddDbContext<AppDbContext>(options =>

    options.UseInMemoryDatabase("CrudApiDb"));

 

var app = builder.Build();

 

// Configure the HTTP request pipeline.

app.UseAuthorization();

 

app.MapControllers();

 

app.Run();

For a real database connection, you can replace UseInMemoryDatabase with something like:

options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));

Make sure to configure the connection string in the appsettings.json file.

5. Testing the API

You now have the following endpoints:

  • GET /api/items: Get all items.
  • GET /api/items/{id}: Get a specific item by ID.
  • POST /api/items: Create a new item.
  • PUT /api/items/{id}: Update an existing item.
  • DELETE /api/items/{id}: Delete an item.

Conclusion

This .NET Core Web API is structured to handle CRUD operations, error handling, and responses effectively. It matches the Angular service you've created using RxJS operators like map and catchError, which will allow you to easily manage asynchronous requests and errors in the Angular component.

Make sure your API's apiUrl in the Angular service points to this backend URL (e.g., http://localhost:5000/api/items).

  


Comments

Popular posts from this blog

Multiline to singleline IN C# - CODING

EF Core interview questions for beginners

EF Core interview questions for experienced