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
Post a Comment