Current Path : /www/sites/www.coderblog.in/index/
Url:

NameSizeOptions
App_DataDIRnone
backupDIRnone
cgi-binDIRnone
cssDIRnone
imgDIRnone
metaDIRnone
wp-adminDIRnone
wp-contentDIRnone
wp-includesDIRnone
.htaccess3.35 KBDEL
.htaccess.bk1.84 KBDEL
404.html0.13 KBDEL
ads.txt1.07 KBDEL
favicon.ico2.19 KBDEL
index.php0.40 KBDEL
license.txt19.44 KBDEL
php.ini0.58 KBDEL
readme.html7.25 KBDEL
service.php0.00 KBDEL
web.config2.87 KBDEL
wp-activate.php7.21 KBDEL
wp-blog-header.php1.21 KBDEL
wp-comments-post.php2.27 KBDEL
wp-config-sample.php3.26 KBDEL
wp-config.php2.98 KBDEL
wp-cron.php5.49 KBDEL
wp-links-opml.php2.44 KBDEL
wp-load.php3.84 KBDEL
wp-login.php50.21 KBDEL
wp-mail.php8.52 KBDEL
wp-settings.php29.38 KBDEL
wp-signup.php33.71 KBDEL
wp-trackback.php4.98 KBDEL
xmlrpc.php3.13 KBDEL
API – Coder Blog https://www.coderblog.in Join the coding revolution! Learn, share, and grow together! Mon, 28 Aug 2023 03:51:54 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 How to Implement JWT in Core API and Angular — Part 2 https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-2/ https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-2/#comments Fri, 25 Aug 2023 01:02:40 +0000 https://www.coderblog.in/?p=1048 This is post 2 of 2 in the series “How to Implement JWT” In this series, I will

<p>The post How to Implement JWT in Core API and Angular — Part 2 first appeared on Coder Blog.</p>

]]>
  1. How to Implement JWT in Core API and Angular – Part 1
  2. How to Implement JWT in Core API and Angular — Part 2

1. Introduction

We have introduced how to generate the JWT on the server side in previous article, so we will continue to introduce how to handle the JWT in client-side with Angular in this article.

For demonstration, we need to create a simple login page to call the login API, and create another simple page for checking the login token whether is valid, OK, let’s do it!

2. Create the Models

I will base it on the previous demo project for describing. To communicate with the API, we need to create the corresponding models to map the result, so we create a login-request to pass the login data

//MyDemo.Client\src\app\models\login-request.ts

export interface LoginRequest {
  username: string;
  password: string;
}

and create a token model

//MyDemo.Client\src\app\models\token.ts

export interface Token {
  [prop: string]: any;

  //the Jwt token return from API after login successfully
  access_token: string;
  //the current user id
  user_id?: string;
  //should be just handle the 'Bearer' type in this sample
  token_type?: string;
  //How long will be the token expired(e.g. after 30 mins). This is a timestamp format
  expires_in?: number;
  //the actually expire time, so should be the expires_in + current time, e.g. 
  //if expires_in = 30 mins, then exp would be current time + 30 mins
  exp?: number;
}

3. Create the Services

There are 3 services we need to handle:

3.1. Handle Token

First, we need to create a jwt-token to handle the token logic, put these logics into a core folder

//MyDemo.Client\src\app\core\jwt-token.ts

import { Token } from "../models/token";
import { capitalize, currentTimestamp } from "./util";

export class JwtToken {
  constructor(protected attributes: Token) {}

  get access_token(): string {
    return this.attributes.access_token;
  }

  get user_id(): string {
    return this.attributes.user_id ?? '';
  }

  get token_type(): string {
    return this.attributes.token_type ?? 'bearer';
  }

  get exp(): number | void {
    return this.attributes.exp;
  }

  valid(): boolean {
    return this.hasAccessToken() && !this.isExpired();
  }

  getBearerToken(): string {
    return this.access_token
      ? [capitalize(this.token_type), this.access_token].join(' ').trim()
      : '';
  }

  private hasAccessToken(): boolean {
    return !!this.access_token;
  }

  /**
  Check the expired time
  Unit: seconds
  */
  private isExpired(): boolean {
    return this.exp !== undefined && this.exp - currentTimestamp() <= 0;
  }

}

create the util for the common helper methods

//MyDemo.Client\src\app\core\util.ts

/**
 * Capitalize first letter
 * @param text the text wants to be capitalized
 * @returns
 */
export function capitalize(text: string): string {
  return text.substring(0, 1).toUpperCase() + text.substring(1, text.length).toLowerCase();
}

/**
 * Get the current timestamp
 * @returns
 */
export function currentTimestamp(): number {
  return Math.ceil(new Date().getTime() / 1000);
}

/**
 * Filter the Non null object to make sure the object is valid
 * @param obj filter object
 * @returns
 */
export function filterObject<T extends Record<string, unknown>>(obj: T) {
  return Object.fromEntries(
    Object.entries(obj).filter(([, value]) => value !== undefined && value !== null)
  );
}

because we need to save the token into local storage, so create a simple local storage service

//MyDemo.Client\src\app\services\local-storage.service.ts

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

@Injectable({
  providedIn: 'root',
})
export class LocalStorageService {
  get(key: string) {
    return JSON.parse(localStorage.getItem(key) || '{}') || {};
  }

  set(key: string, value: any): boolean {
    localStorage.setItem(key, JSON.stringify(value));

    return true;
  }

  has(key: string): boolean {
    return !!localStorage.getItem(key);
  }

  remove(key: string) {
    localStorage.removeItem(key);
  }

  clear() {
    localStorage.clear();
  }
}

in the end, create a token service to put these together

//MyDemo.Client\src\app\services\token.service.ts

import { Injectable, OnDestroy } from '@angular/core';
import { Token } from '../models/token';
import { LocalStorageService } from './local-storage.service';
import { JwtToken } from '../core/jwt-token';
import { currentTimestamp, filterObject } from '../core/util';

@Injectable({
  providedIn: 'root',
})
export class TokenService implements OnDestroy {
  private key = 'MyDemo-token';

  private _token?: JwtToken;

  constructor(private store: LocalStorageService) {}

  private get token(): JwtToken | undefined {
    if (!this._token) {
      this._token = new JwtToken(this.store.get(this.key));
    }

    return this._token;
  }

  set(token?: Token): TokenService {
    this.save(token);

    return this;
  }

  clear(): void {
    this.save();
  }

  valid(): boolean {
    return this.token?.valid() ?? false;
  }

  getUserid(): string {
    return this.token?.user_id ?? '';
  }

  getBearerToken(): string {
    return this.token?.getBearerToken() ?? '';
  }

  ngOnDestroy(): void {
  }

  /**
   * Save the token to local storage
   * @param token token model
   */
  private save(token?: Token): void {

    this._token = undefined;

    if (!token) {
      this.store.remove(this.key);
    } else {
      const value = Object.assign({ access_token: '', token_type: 'Bearer' }, token, {
        exp: token.expires_in ? currentTimestamp() + token.expires_in : null,
      });
      this.store.set(this.key, filterObject(value));
    }
  }
}

3.2. Authentication Service

We will call the API to login the system and get the Jwt token, so we need an auth service to handle the login and logout logic

import { Injectable } from '@angular/core';
import { map, tap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
import { config } from 'src/assets/config';
import { Token } from '../models/token';
import { TokenService } from './token.service';
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root',
})
export class AuthService {

  constructor(
    private tokenService: TokenService, private router: Router,
    protected http: HttpClient) {}

    /**
     * Call the API to login
     * @param username user name
     * @param password password
     * @returns Jwt token if login successfully
     */
    login(username: string, password: string) {

      var url = config.apiUrl + "/auth/login";
      //call the API to get token after login successfully
      return this.http.post<Token>(url, { username, password }).pipe(
        tap(token => {
          console.log('auth service logined ', token);
          //save the token into local storage
          this.tokenService.set(token);
        }),
        map(() => {
          console.log('auth service logined and map ', this.check());
          //check the token whether is valid
          return this.check();
        })
      );
    }

    /**
     * Clear the token after logout
     */
    logout(){
      this.tokenService.clear();
      this.router.navigateByUrl('/login');
    }

    check() {
      return this.tokenService.valid();
    }
}

4. Create the Login page

After creating the essential models and services, we can create the login page now. Run the below command to create a login page

ng g c Login --skip-tests

Cause we are just for demonstration, so only need to create a simple login layout :

<!-- MyDemo.Client\src\app\login\login.component.html -->

<p>Login</p>
<div class="row">
  <form class="form-field-full" [formGroup]="loginForm">
  <div class="col-sm-12">
      <mat-form-field class="col-sm-3">
        <mat-label>User Name: </mat-label>
        <input matInput formControlName="username" placeholder="User Name">
      </mat-form-field>
  </div>
  <div  class="col-sm-12">
      <mat-form-field class="col-sm-3">
        <mat-label>Password: </mat-label>
        <input matInput formControlName="password" type="password" placeholder="Password">
      </mat-form-field>

  </div>
  <div  class="col-sm-12">
    <button class="m-r-8 bg-green-700 text-light" mat-raised-button  (click)="login()">Login</button>
  </div>
  <div class="col-sm-12">
    <span style="color: red;" *ngIf="errorMsg != ''">{{ errorMsg }}</span>
  </div>
</form>
</div>

we can use the FormBuilder to get the input values and pass them to the login function

//MyDemo.Client\src\app\login\login.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { FormBuilder } from '@angular/forms';
import { filter } from 'rxjs/operators';
import { HttpErrorResponse } from '@angular/common/http';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent {

  constructor(private fb: FormBuilder, 
          private router: Router, 
          private auth: AuthService) {
  }

  public errorMsg: string = '';

  //init the loginForm 
  loginForm = this.fb.nonNullable.group({
    username: '',
    password: '',
  });

  get username() {
    return this.loginForm.get('username')!;
  }

  get password() {
    return this.loginForm.get('password')!;
  }

  login() {
    this.auth
      .login(this.username.value, this.password.value)
      .pipe(filter(authenticated => authenticated))
      .subscribe(
        () => {
          //redirect to user management page if login successfully
          this.router.navigateByUrl('/user-management');
        },
        (errorRes: HttpErrorResponse) => {
          //otherwise then update the error message in the page
          if(errorRes.status == 401){
            this.errorMsg = 'User name or password is not valid!';
          }
            console.log('Error', errorRes);
        }
      );
  }
}

Ok, we can take a look at the result :

1) Input the user name and password and click login

2) It will return the token (can find it in local storage ) and redirect to the user management page

Seems great, right? 🙂

But please hold on, we still have some problems. You will find that if you access the user management page directly and it also can be successful, that means the page didn’t check the login token, that does not make sense.

5. Add the guard for the user pages

We can solve the above issue with guard in Angular. The guard in Angular refers to route guards, which are interfaces that allow you to control navigation and access to routes in your Angular application. route guards allow you to check if a user can activate or deactivate a route, by implementing CanActivate or CanDeactivate interfaces. You can find more details here.

Create the route guards as below:

//MyDemo.Client\src\app\core\auth.guard.ts

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  CanActivateChild,
  RoutesRecognized,
  Router,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { filter, pairwise } from 'rxjs/operators';
import { AuthService } from '../services/auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate, CanActivateChild {

  previousUrl!: string;
  currentUrl!: string;

  constructor(private auth: AuthService, private router: Router) {
    this.router.events
      .pipe(filter((evt: any) => evt instanceof RoutesRecognized), pairwise())
      .subscribe((events: RoutesRecognized[]) => {
        this.previousUrl = events[0].urlAfterRedirects;
        this.currentUrl = events[1].urlAfterRedirects;
      });
  }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.authenticate();
  }

  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | UrlTree {
    return this.authenticate();
  }

  private authenticate(): boolean | UrlTree {
    //check whether is login successfully
    if (this.auth.check()) {
      return true;
    }
    else{
      this.router.navigateByUrl('/login');
    }
    return false;
  }
}

and use it in app routing, add the canActivate in the routes

//MyDemo.Client\src\app\app-routing.module.ts

const routes: Routes = [
  { path: 'user-management', component: UserManagementComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent },
];

after that, logout of the page and try to access the user management page directly, you will find that will be auto redirected to the login page!

6. Create the Interceptor

We are almost done, but still, there is an issue that needs to be solved! Even if we can login successfully and redirect to the user management page, we still can’t get the user data, because there is an authorized checking with the user controller API, so we need to pass the token to the API when we get the user data.

We can append the HTTP header with Authorization when calling the get user API /api/users, but we also need to do that for every API, so this is not a good approach!

The best way that we can use the interceptor.

Interceptors in Angular are services that allow you to intercept and transform HTTP requests and responses between your application and the server. Request interceptors can modify headers, add authentication tokens, log requests, etc.

Create the token-interceptor as below

//MyDemo.Client\src\app\core\token-interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpErrorResponse,
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
} from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { TokenService } from '../services/token.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(
    private tokenService: TokenService,
    private router: Router
  ) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    const handler = () => {
      //check the url if login then just redirect to user management page after login     
      if (this.router.url.includes('/login')) {
        this.router.navigateByUrl('/user-management');
      }
    };

    if (this.tokenService.valid()) {
      //if the token is valid, then append to the http header for each request
      return next
        .handle(
          request.clone({            
            headers: request.headers.append('Authorization', this.tokenService.getBearerToken()),
            withCredentials: true,
          })
        )
        .pipe(
          catchError((error: HttpErrorResponse) => {
            //error handler
            if (error.status === 401) {
              this.tokenService.clear();
            }
            return throwError(error);
          }),
          tap(() =>{
            handler();})
        );
    }

    return next.handle(request).pipe(tap(() =>{
        handler();
      }));
  }
}

add a provider in app.module

@NgModule({
  ...
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
  ],
  ...
})

after that, the TokenInterceptor will auto append the token in http header for each request.

login to the page again, you will see all of the user data as well!

7. Conclusion

Jwt is a better way to handle the API authorization, after this article, we learned how to handle the Jwt token in Angular, the flow should be as below:

1) Call API login function and get the token after successfully
2) Save the token in local storage
3) Add the checking for each user’s pages
4) Pass the token to each API request to get data

In the end, don’t forget there are two main points that create the router guard for checking login token and an interceptor for sending token to API.

Loading

<p>The post How to Implement JWT in Core API and Angular — Part 2 first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-2/feed/ 7
How to Implement JWT in Core API and Angular – Part 1 https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-1/ https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-1/#comments Mon, 21 Aug 2023 08:21:03 +0000 https://www.coderblog.in/?p=1015 This is post 1 of 2 in the series “How to Implement JWT” In this series, I will

<p>The post How to Implement JWT in Core API and Angular – Part 1 first appeared on Coder Blog.</p>

]]>
  1. How to Implement JWT in Core API and Angular – Part 1
  2. How to Implement JWT in Core API and Angular — Part 2

1. Introduction

JWT means JSON Web Tokens, it usually uses for API authorization with the client. JWT is an open standard that defines a way to transmit information between two parties securely as a JSON object in a compact and verifiable way. It is great for securing REST APIs and authorization. There are some common use cases of JWT include:

1) Authorization – Once the user is logged in, each subsequent request will include the JWT to allow access to routes, services, and resources.

2) Information Exchange – JWT is a good way of securely transmitting information between parties.

3) User information storage – JWT can store user details, preferences, etc in a web/mobile application.

The benefits of using JWT include compact size, url-safe, ability to be encrypted, decentralized, suitable for mobile apps, and more.

And in this post, I will show you how to use JWT for .Net Core API and Angular.

2. The flow for using JWT

Let’s take a look at the flow for how’s the JWT working:

1) Client calls the API for a login request
2) Server(API) side generates JWT after login success
3) Pass the JWT to client-side
4) Client-side needs to save the JWT
5) Client passes the JWT to the API for each request
6) Server-side needs to verify the JWT from the client’s request, if successful then return the data otherwise return auth failed message.

3. Generate the JWT

We can create a JWT service to help to do that! We will use JwtSecurityToken to generate the JWT, this method under System.IdentityModel.Tokens.Jwt namespace, let’s take a look at the signature for this method. There are 4 overwrite methods for this, but we will use the below

public JwtSecurityToken(string issuer = null, string audience = null, 
                IEnumerable<Claim> claims = null, DateTime? notBefore = null, DateTime? 
                expires = null, SigningCredentials signingCredentials = null);

suppose we can let the parameters be null, but for safer, we will use all of them. We can put these parameters into appsettings.json so that can be control

"JwtSettings": {
    "SecurityKey": "MyVeryOwnSecurityKey",
    "Issuer": "MyVeryOwnIssuer",
    "Audience": "https://localhost:4810",
    "ExpirationTimeInMinutes": 1440
  },

and use them to create the token

var jwtOptions = new JwtSecurityToken(
             issuer: _configuration["JwtSettings:Issuer"],
             audience: _configuration["JwtSettings:Audience"],
             claims: GetClaims(user),
             expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                     _configuration["JwtSettings:ExpirationTimeInMinutes"])),
             signingCredentials: GetSigningCredentials());

the complete codes for the JwtService as below

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using MyDemo.Core.Data.Entity;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace MyDemo.Core.Services;

public class JwtService
{
    private readonly IConfiguration _configuration;
    public JwtService(IConfiguration configuration)
    {
        //inject the configuration for getting the setting values
        _configuration = configuration;
    }
    public JwtSecurityToken GetToken(User user)
    {
        //create the Jwt token from setting values
        var jwtOptions = new JwtSecurityToken(
                        issuer: _configuration["JwtSettings:Issuer"],
                        audience: _configuration["JwtSettings:Audience"],
                        claims: GetClaims(user),
                        expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                                _configuration["JwtSettings:ExpirationTimeInMinutes"])),
                        signingCredentials: GetSigningCredentials());
        return jwtOptions;
    }
    //generate signing credentials
    private SigningCredentials GetSigningCredentials()
    {
        var key = Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecurityKey"]);
        var secret = new SymmetricSecurityKey(key);
        return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
    }
    //create the claim base current user name and email
    private List<Claim> GetClaims(User user)
    {
        var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email) };
        return claims;
    }
}

4. Create the Login function

We need to create an API login function to return the JWT to the client.

4.1 Create the DTO

Before we start, we need to receive the login request object and a result object for return to the client, so we need to create two DTOs for the login function. In this way, I will simply introduce what’s DTO.

DTO stands for Data Transfer Object. It is a design pattern used to transfer data between different layers of an application, such as between the UI layer and the business logic layer. The main purpose of a DTO is to transfer data, so they don’t contain any behavior except for storage, retrieval, serialization, and deserialization of data.

We can create a DTO folder under the Data folder and create the LoginRequest.cs in this folder

//MyDemo.Core/Data/DTO/LoginRequest.cs

namespace MyDemo.Core.Data.DTO;

public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; } 
}

and the result object for returning the token

//MyDemo.Core/Data/DTO/LoginToken.cs

namespace MyDemo.Core.Data.DTO;

public class LoginToken
{
    /// <summary>
    /// The JWT token if the login attempt is successful, or NULL if not
    /// </summary>
    public string? access_token { get; set; }
    public string? user_id { get; set; }
    public string? token_type { get; set; }
    /// <summary>
    /// expires time in seconds
    /// </summary>
    /// <value></value>
    public int? expires_in { get; set; }
}

4.2. Create the Auth API

Create the AuthController to handle the login function, and we need to inject the below instance first

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IUserRepository _userRepository;
    private readonly JwtService _jwtService;
    private readonly IConfiguration _configuration;
    private readonly ILogger<AuthController> _logger;

    public AuthController(IUserRepository userRepository,
                        JwtService jwtService,
                        IConfiguration configuration,
                        ILogger<AuthController> logger)
    {
        _userRepository = userRepository;
        _jwtService = jwtService;
        _configuration = configuration;
        _logger = logger;
    }
}

create the login API to receive a LoginRequest

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest loginRequest)
{
    //todo...
}

check the user name and password for login, if successful then generate the token, otherwise, return the null data.

var user = await _userRepository.GetItemWithConditionAsync(
    u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
if (user == null)
{
    return Unauthorized(new LoginToken()
    {
        access_token = null,
        user_id = null,
        expires_in = 0,
        token_type = "Bearer"
    });
}
//generate a token by user information and appsettings values
var secToken = _jwtService.GetToken(user);
//serializes the token into a JWT format
var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
//get the expiration times and pass them to the client
var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
return Ok(new LoginToken()
{
    access_token = jwt,
    user_id = user.Id.ToString(),
    expires_in = expires_sec,
    token_type = "Bearer"
});

by the way, this is only for a demo, so I didn’t encrypt the password, you should encrypt it in a real project.

and we should use try...catch to handle exceptions in a complete function

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest loginRequest)
{
    try
    {
        var user = await _userRepository.GetItemWithConditionAsync(
            u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
        if (user == null)
        {
            return Unauthorized(new LoginToken()
            {
                access_token = null,
                user_id = null,
                expires_in = 0,
                token_type = "Bearer"
            });
        }
        //generate a token by user information and appsettings values
        var secToken = _jwtService.GetToken(user);
        //serializes the token into a JWT format
        var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
        //get the expiration times and pass them to the client
        var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
        return Ok(new LoginToken()
        {
            access_token = jwt,
            user_id = user.Id.ToString(),
            expires_in = expires_sec,
            token_type = "Bearer"
        });
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Login Error");
        return StatusCode(500);
    }
}

5. Setup Program.cs

In the end, we also need to add the service to program.cs file. Before that, we need to install the below package to API project from Nuget first

Microsoft.AspNetCore.Authentication.JwtBearer

and add the below codes

// Add Authentication services & middlewares
builder.Services.AddAuthentication(opt =>
{
    opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        RequireExpirationTime = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
        ValidAudience = builder.Configuration["JwtSettings:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.
                GetBytes(builder.Configuration["JwtSettings:SecurityKey"]))
    };
});

builder.Services.AddScoped<JwtService>();

Ok, let’s take a look at the result in the swagger UI

Figure

Great! We got the token after login successfully!

6. Authorize the User Controller

We have created the login token but we still need to add the authorization to the user controller, otherwise, the token will be useless, just adding the attribute to the user controller will be ok

[Authorize()]
public class UserController : ControllerBase
{
    //...
}

7. Conclusion

We have learned how to generate and handle the Jwt on the server side (API), the main port that uses the JwtSecurityToken to generate a token and serialize it into a JWT format and return it to the client. Because that’s more complex for implementing on the client side, so I split to part 2 to describe how to handle it on the client side in the next article! 🙂

Loading

<p>The post How to Implement JWT in Core API and Angular – Part 1 first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-implement-jwt-in-core-api-and-angular-part-1/feed/ 15
How to do Unit Testing for the Core Api Project with Repository https://www.coderblog.in/2023/08/how-to-do-unit-testing-for-the-core-api-project-with-repository/ https://www.coderblog.in/2023/08/how-to-do-unit-testing-for-the-core-api-project-with-repository/#comments Sun, 13 Aug 2023 01:48:40 +0000 https://www.coderblog.in/?p=964 This is post 8 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post How to do Unit Testing for the Core Api Project with Repository first appeared on Coder Blog.</p>

]]>
  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

1. Introduction

We have created a .Net Core Api project before, this post will discuss how to do the unit testing for the project.

First question, why should we do the unit testing?

Here are some key reasons why unit testing is important and worth investing:

Finds bugs early – Unit tests help catch bugs early in the development cycle, when they are less expensive to fix.

Improves design – Writing unit tests forces you to think about how your code will be used and drive more modular, reusable code.

Facilitates change – Good unit tests allow developers to refactor code quickly and verifiably. Tests help ensure changes don’t break existing functionality.

Reduces risks – Tests provide a safety net to catch edge cases and errors. They build confidence that refactoring and changes won’t introduce hard-to-find bugs.

Documents code – Unit tests document how production code should be used. They are live examples of how to call various methods.

Enables automation – Automated testing catches issues quickly and frees developers from manual testing. Tests can be run on every code change.

Improves collaboration – Unit tests enable multiple developers to collaborate on code using a shared suite of tests to ensure changes don’t cause issues.

Avoids technical debt – Lack of tests creates a technical debt that slows future development. Writing tests avoid this debt.

2. Create the Unit Testing Project

As I have mentioned before, I like to use VS Code, so I will also base on VS Code and xUnit Test to demonstrate how to do it! 🙂

If you have installed the VS Code extension vscode-solution-explorer then you can easy to create a xUnit Test project from the solution explorer. Just right click the solution and “Add a new project”

then use the xUnit Test Project template, and input the project name MyDemo.Test, then will be created a xUnit Test project

3. Mock Framework

We need to use a mock framework for creating fake objects that simulate the behavior of real objects for testing. So we can install Moq from Nuget for our testing project

4. Create the testing cases

4.1. Mock the Repository

Before we start, we need to create the mock object for repository. Because the mock framework needs to inject into the function logic for testing, so we must use the interfaces programming for the project (like a repository pattern, so this is one of the benefits of using a repository pattern :)).

We can create a MockIUserRepository class to handle user repository testing

//MyDemo.Test/Mocks/MockIUserRepository.cs

using System.Linq.Expressions;
using Moq;
using MyDemo.Core.Data.Entity;
using MyDemo.Core.Repositories;

namespace MyDemo.Test.Mocks
{
    public class MockIUserRepository
    {
        //todo...
    }
}

because we use IQueryable for GetAll in our repository method

public IQueryable<T> GetAll() => _context.Set<T>().AsNoTracking();

so we also need to return the same object for a mock object, then we create the user data as below

public static IQueryable<User> GetUsers()
{
    var users = new List<User>() {
    new User() {
        Id = 100,
        Name = "Unit Tester 1",
        Email = "unit.tester1@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now.AddDays(-2),
        UpdatedAt = DateTime.Now.AddDays(-2)
    },
    new User() {
        Id = 200,
        Name = "Unit Tester 2",
        Email = "unit.tester2@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now.AddDays(-1),
        UpdatedAt = DateTime.Now.AddDays(-1)
    }
    }.AsQueryable();

    return users;
}

then we create the mock instance for IUserRepository

var mock = new Mock<IUserRepository>();

setup the mock instance to return a list of users when the GetAll method is called

//get the fake user data
var users = GetUsers(); 
//setup the GetAll and return the user data
mock.Setup(u => u.GetAll()).Returns(()=> users);

setup the GetItemWithConditionAsync and GetWithConditionAsync methods, because these methods need to pass an expression parameter for query data, so we also need to use the Expression<Func<User, bool> for mock the parameter, and this is an async method, so need to use ReturnsAsync for return the value

mock.Setup(u => u.GetItemWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
        ReturnsAsync((Expression<Func<User, bool>> expr) => users.FirstOrDefault(expr));

mock.Setup(u => u.GetWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
                ReturnsAsync((Expression<Func<User, bool>> expr) => users.Where(expr));

the complete codes as below

//MyDemo.Test/Mocks/MockIUserRepository.cs

using System.Linq.Expressions;
using Moq;
using MyDemo.Core.Data.Entity;
using MyDemo.Core.Repositories;

namespace MyDemo.Test.Mocks
{
    public class MockIUserRepository
    {
        public static Mock<IUserRepository> GetMock()
        {
            var mock = new Mock<IUserRepository>();

            var users = GetUsers();

            mock.Setup(u => u.GetAll()).Returns(()=> users);

            mock.Setup(u => u.GetItemWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
                ReturnsAsync((Expression<Func<User, bool>> expr) => users.FirstOrDefault(expr));

            mock.Setup(u => u.GetWithConditionAsync(It.IsAny<Expression<Func<User, bool>>>())).
                ReturnsAsync((Expression<Func<User, bool>> expr) => users.Where(expr));

            return mock;
        }

        public static IQueryable<User> GetUsers()
        {
            var users = new List<User>() {
            new User() {
                Id = 100,
                Name = "Unit Tester 1",
                Email = "unit.tester1@abc.com",
                IsActive = true,
                CreatedAt = DateTime.Now.AddDays(-2),
                UpdatedAt = DateTime.Now.AddDays(-2)
            },
            new User() {
                Id = 200,
                Name = "Unit Tester 2",
                Email = "unit.tester2@abc.com",
                IsActive = true,
                CreatedAt = DateTime.Now.AddDays(-1),
                UpdatedAt = DateTime.Now.AddDays(-1)
            }
            }.AsQueryable();
            return users;
        }
    }
}

4.2. GetUsers testing case

Ok, now we can create the first testing case to get all user data. Get the mock instance and call the repository GetAll method

[Fact]
public void Test1_GetUsers()
{
    //get the mock instance
    var mockUserRepository = MockIUserRepository.GetMock();
    //call the repository to get user data
    var users = mockUserRepository.Object.GetAll();

    //test the result

    //the users should not be null
    Assert.NotNull(users);
    //the users should be an IQueryable<User> object
    Assert.IsAssignableFrom<IQueryable<User>>(users);
    //should be only 2 items in the user list (this is the fake data that we created)
    Assert.Equal(2, users.Count());
}

VS Code can be very helpful to add the actions on the testing case method, you can click the Run Test or Debug Test to run the test

if everything is ok, you will see the testing result in terminal

4.3. Get user by Id testing case

This time we want to test the method GetUser of UserController. We need to create the user controller instance first, as you know there is a constructor for receiving two parameters in the user controller, that’s means we also need to pass these when new it

public UserController(IUserRepository userRepository, ILogger<User> logger)
{
    this._userRepository = userRepository;
    this._logger = logger;
}

but how should we pass the repository and logger objects? Ok, we can also mock them:

//get the user repository mock instance
var mockUserRepository = MockIUserRepository.GetMock();
//create the ILogger mock instance
var logger = Mock.Of<ILogger<User>>();

now we can new a user controller instance

var userController = new UserController(mockUserRepository.Object, logger);

call the GetUser from controller, because the fake user id is 100 and 200, so we try to pass 100 to get a user

//get the user data by id
var result = userController.GetUser(100);

//convert the result to ObjectResult so that we can check the status codes
var objResult = result.Result.Result as ObjectResult;

//convert the result value to our define's ApiResult object, and we can get the user data
var apiResult = objResult.Value as ApiResult<User>;

//test the result values
Assert.NotNull(result);
Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
Assert.IsAssignableFrom<User>(apiResult.Data);
Assert.Equal("Unit Tester 1", apiResult.Data.Name);

the complete codes as below

[Fact]
public void Test2_GetUserById()
{
    var mockUserRepository = MockIUserRepository.GetMock();

    //mock the logger
    var logger = Mock.Of<ILogger<User>>();

    var userController = new UserController(mockUserRepository.Object, logger);

    var result = userController.GetUser(100);
    var objResult = result.Result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<User>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
    Assert.IsAssignableFrom<User>(apiResult.Data);
    Assert.Equal("Unit Tester 1", apiResult.Data.Name);
}

4.4. Create user testing case

You should know the logic and concept, so I just show you the codes below

[Fact]
public void Test3_CreateUser()
{
    var mock = MockIUserRepository.GetMock();
    //mock the logger
    var logger = Mock.Of<ILogger<User>>();
    var userController = new UserController(mock.Object, logger);

    //call the Post method to create a user
    var result = userController.PostUser(new User()
    {
        Id = 300,
        Name = "new user",
        Email = "newTester@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now,
        UpdatedAt = DateTime.Now
    });

    var objResult = result.Result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<User>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
    Assert.Equal(true, apiResult.Success);
    Assert.Equal(300, apiResult.Data.Id);
}

4.5. Update user testing case

The codes as below

[Fact]
public void Test4_UpdateUser()
{
    var mock = MockIUserRepository.GetMock();
    //mock the logger
    var logger = Mock.Of<ILogger<User>>();
    var userController = new UserController(mock.Object, logger);

    var result = userController.PutUser(new User()
    {
        Id = 100,
        Name = "update user",
        Email = "updateTester@abc.com",
        IsActive = true,
        CreatedAt = DateTime.Now,
        UpdatedAt = DateTime.Now
    });

    var objResult = result.Result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<User>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);
    Assert.Equal(true, apiResult.Success);
    Assert.Equal("update user", apiResult.Data.Name);
}

4.6. Delete user testing case

Because there is no user data returned by the delete function, so we just need to check the success status from the Api

[Fact]
public void Test5_DeleteUserById()
{
    var mock = MockIUserRepository.GetMock();
    //mock the logger
    var logger = Mock.Of<ILogger<User>>();
    var userController = new UserController(mock.Object, logger);

    var result = userController.DeleteUser(100);
    var objResult = result.Result as ObjectResult;
    var apiResult = objResult.Value as ApiResult<Object>;

    Assert.NotNull(result);
    Assert.Equal(StatusCodes.Status200OK, objResult.StatusCode);

    //check the success whether is true
    Assert.Equal(true, apiResult.Success);
}

In the end, we can run all tests on the class level

and find the result below

5. Summarize

We learned how to do the unit test with the xUnit Test and how to mock the data. There is an important thing you should know if you want to mock data for testing, you must use the interface programming and dependency injection, so if you call the get user method directly from a help method, then you can’t mock it for testing, you can put the method into service and implement from an interface.

The other thing that needs to note is that must use the same parameter type with your method when you setup a mock instance.

Please let me know if you have any ideas for that! 😀

Loading

<p>The post How to do Unit Testing for the Core Api Project with Repository first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/how-to-do-unit-testing-for-the-core-api-project-with-repository/feed/ 8
Create Paging Data from .Net Core Api https://www.coderblog.in/2023/08/create-paging-data-from-net-core-api/ https://www.coderblog.in/2023/08/create-paging-data-from-net-core-api/#comments Fri, 04 Aug 2023 00:41:04 +0000 https://www.coderblog.in/?p=929 This is post 7 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Create Paging Data from .Net Core Api first appeared on Coder Blog.</p>

]]>
  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

This article is the second part for Create Angular Project and Communicate with .Net Core

1. Data Result Service

As we need to support filter and paging data from Api, so we also need to create a date result service for handle it. We can create this service in MyDemo.Core Api project:

1.1. Create the properties for filter and paging

//MyDemo.API/MyDemo.Core/Services/DataResultService.cs

public class DataResultService<T>
{
   #region Properties
    /// <summary>
    /// The data result.
    /// </summary>
    public List<T> Data { get; private set; }

    /// <summary>
    /// Zero-based index of current page.
    /// </summary>
    public int PageIndex { get; private set; }

    /// <summary>
    /// Number of items contained in each page.
    /// </summary>
    public int PageSize { get; private set; }

    /// <summary>
    /// Total items count
    /// </summary>
    public int TotalCount { get; private set; }

    /// <summary>
    /// Total pages count
    /// </summary>
    public int TotalPages { get; private set; }

    /// <summary>
    /// TRUE if the current page has a previous page,
    /// FALSE otherwise.
    /// </summary>
    public bool HasPreviousPage
    {
        get
        {
            return (PageIndex > 0);
        }
    }

    /// <summary>
    /// TRUE if the current page has a next page, FALSE otherwise.
    /// </summary>
    public bool HasNextPage
    {
        get
        {
            return ((PageIndex + 1) < TotalPages);
        }
    }

    /// <summary>
    /// Sorting Column name (or null if none set)
    /// </summary>
    public string? SortColumn { get; set; }

    /// <summary>
    /// Sorting Order ("ASC", "DESC" or null if none set)
    /// </summary>
    public string? SortOrder { get; set; }

    /// <summary>
    /// Filter Column name (or null if none set)
    /// </summary>
    public string? FilterColumn { get; set; }
    /// <summary>
    /// Filter Query string
    /// (to be used within the given FilterColumn)
    /// </summary>
    public string? FilterQuery { get; set; }
    #endregion
}

1.2. Create a private constructor

We need to return the data object for client side and include the above properties, so we can use a private constructor for return the data

private DataResultService(
    List<T> data,
    int count,
    int pageIndex,
    int pageSize,
    string? sortColumn,
    string? sortOrder,
    string? filterColumn,
    string? filterQuery)
{
    Data = data;
    PageIndex = pageIndex;
    PageSize = pageSize;
    TotalCount = count;
    TotalPages = (int)Math.Ceiling(count / (double)pageSize);
    SortColumn = sortColumn;
    SortOrder = sortOrder;
    FilterColumn = filterColumn;
    FilterQuery = filterQuery;
}

1.3. Get paging data

Cause I want to support dynamic filter with Linq, so we need to create a method for handle this, and need to install below packages from Nuget for support dynamic filter query

System.Linq.Dynamic.Core
System.Linq.Async

Now we can create the below method

public static async Task<DataResultService<T>> CreateAsync(
            IQueryable<T> source,
            int pageIndex,
            int pageSize,
            string? sortColumn = null,
            string? sortOrder = null,
            string? filterColumn = null,
            string? filterQuery = null)
        {
          //todo...
        }

this method can support many parameters from client side, because these parameters were passed from client by a Get method with a string format, so the first thing we need to do is split the column and query, after that, just create a dynamic SQL query pass to Linq object for get data

if (!string.IsNullOrEmpty(filterColumn)
    && !string.IsNullOrEmpty(filterQuery))
{
    if (filterColumn.Contains(","))
    {
        //handle multiple columns filter
        var index = 0;
        var sql = "";
        var queries = filterQuery.Split(',');
        foreach (var column in filterColumn.Split(','))
        {
            if (!string.IsNullOrEmpty(queries[index]) && queries[index] != "null")
            {
                sql += string.Format("{0}.Contains(@{1}) and ", column, index);
            }
            index++;
        }
        if (sql.Length > 0)
        {
            sql = sql.Substring(0, sql.Length - 4); //remove the end of 'and '
        }
        if (sql != "")
        {
            source = source.Where(sql, queries);
        }
    }
    else
    {
        source = source.Where(
        string.Format("{0}.Contains(@0)",
        filterColumn),
        filterQuery);
    }
}

get the total items and generate the data for paging

var count = await source.CountAsync();

//sort data by column 
if (!string.IsNullOrEmpty(sortColumn))
{
    sortOrder = !string.IsNullOrEmpty(sortOrder)
    && sortOrder.ToUpper() == "ASC" ? "ASC" : "DESC";

    source = source.OrderBy(string.Format("{0} {1}",sortColumn,sortOrder));
}

//get the data by paging query
source = source.Skip(pageIndex * pageSize).Take(pageSize);

//convert to list items
var data = await source.ToAsyncEnumerable().ToListAsync();

//generate the result by private constructor
return new DataResultService<T>(
            data,
            count,
            pageIndex,
            pageSize,
            sortColumn,
            sortOrder,
            filterColumn,
            filterQuery);

2. Update UserController

After created the DataResultService , we can update our UserController.

First, update the GetUsers for support paging parameters, and change the return value to DataResultService<User>

public async Task<ActionResult<DataResultService<User>>> GetUsers(
                        int pageIndex = 0, int pageSize = 10,
                        string? sortColumn = null, string? sortOrder = null,
                        string? filterColumn = null, string? filterQuery = null)

update the apiResult type to DataResultService<User>

var apiResult = new ApiResult<DataResultService<User>>();

in the end, update the get data method

var users = _userRepository.GetAll();

apiResult.Data = await DataResultService<User>.CreateAsync(
            users,
            pageIndex,
            pageSize, sortColumn,
            sortOrder, filterColumn,
            filterQuery);

3. Test the Api

Ok, we have finished the changes with our Api, now we can try it with the angular client.

First, start the Api project in VS Code console with below command (don’t forget goto the Api folder)

dotnet watch run

and create another new console and goto MyDemo.Client folder to run below command

npm run hmr

after that, you should access the user management page as below URL

http://localhost:4810/user-management

and if everything is ok, the result should be like below

Figure 1

after search by user name

Figure 2

Loading

<p>The post Create Paging Data from .Net Core Api first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/08/create-paging-data-from-net-core-api/feed/ 8
Create .Net Core Api controller for CRUD with repository https://www.coderblog.in/2023/07/create-net-core-api-controller-for-crud-with-repository/ https://www.coderblog.in/2023/07/create-net-core-api-controller-for-crud-with-repository/#comments Thu, 27 Jul 2023 02:00:04 +0000 https://www.coderblog.in/?p=855 This is post 4 of 8 in the series “Create a website with ASP.NET Core and Angular” In

<p>The post Create .Net Core Api controller for CRUD with repository first appeared on Coder Blog.</p>

]]>
  1. How to setup VS Code environment for ASP.NET Core Project
  2. Use code first to connect database in .Net Core API
  3. Use the Repository pattern in .Net core
  4. Create .Net Core Api controller for CRUD with repository
  5. Use the Serilog in .Net Core
  6. Create Angular Project and Communicate with .Net Core
  7. Create Paging Data from .Net Core Api
  8. How to do Unit Testing for the Core Api Project with Repository

In the previous article, I introduced how to use the repository pattern, at this time, I will show you how to use for CURD with the Api controller.


We need to create the controller for implement the RESTful Api, so first of all, we need to know what’s that!

1. What’s RESTful Api

REST stands for Representational State Transfer, and it is an architectural style for building web services. A RESTful API is a web service that follows the principles of REST.

A RESTful API is characterized by the following properties:

  1. Client-server architecture: Separation of concerns between the client and the server.

  2. Stateless: Each request from a client to the server contains all the information necessary for the server to understand the request. The server does not store any client context between requests.

  3. Cacheable: Clients can cache responses to improve performance.

  4. Uniform interface: A set of well-defined HTTP methods (GET, POST, PUT, DELETE, etc.) is used to interact with resources, and resources are identified by URIs.

  5. Layered system: A client can’t tell whether it is connected directly to the end server or communicating through an intermediary, such as a load balancer.

  6. Code on demand (optional): Servers can transfer executable code to clients, such as JavaScript, to be executed in the client’s context.

In practice, a RESTful API typically provides a set of endpoints that clients can use to interact with the resources exposed by the API. For example, a RESTful API for a blog might provide endpoints for creating, updating, and deleting blog posts, and for retrieving a list of blog posts or a single blog post by ID.

2. Create the Api Controller

2.1. What’s Route

Route is an attribute that can be applied to an action method in a controller to specify the URL pattern that the action method should respond to. In other words, it defines the route template for the action method.

The Route attribute can be also used to specify the route template directly on an action method, like this:

[HttpGet("users/{id}")]
public IActionResult GetUser(int id)
{
    // ...
}

In this example, the HttpGet attribute specifies that the action method should handle HTTP GET requests, and the users/{id} URL pattern specifies that the method should handle requests for URLs that match the /users/{id} pattern, where {id} is a placeholder for the ID of the user being requested.

Alternatively, you can specify the route template at the controller level using the Route attribute on the controller class, and then specify additional path segments for individual actions using the HttpGet, HttpPost, etc. attributes. Here’s an example:

[Route("api/[controller]")]
public class UserController : ControllerBase
{
    [HttpGet("{id}")]
    public IActionResult GetUser(int id)
    {
        // ...
    }

    [HttpPost]
    public IActionResult CreateUser([FromBody] User user)
    {
        // ...
    }
}

In this example, the Route attribute specifies that all action methods in the UserController should be mapped under the /api/users URL path. The HttpGet and HttpPost attributes specify the additional path segments and HTTP methods for the GetUser and CreateUser methods, respectively.

2.2 Create the Api Methods

Ok, let’s create out controller and methods. As the previous article mention, we need to create an ApiResult object for handle return data. After that, we can use C# Extensions to help to create an UserController

We need to update the constructor for setup(inject) the Repository object

//  MyDemo/Controllers/UserController.cs

[ApiController]
[Route("api")]
public class UserController : ControllerBase
{
    private readonly IUserRepository _userRepository;

    public UserController(IUserRepository userRepository)
    {
        this._userRepository = userRepository;
    }
}

For better user friendly the Api Url, I just set the controller router attribute to api, that’s mean don’t need to add the controller name in the api url.

Before start to create the Api method, I want to enhance the previous codes for register Repository service in Program.cs. We just use below

builder.Services.AddScoped<IUserRepository, UserRepository>();

for register the service, but there is a problem here, if there are multiple Repositories need to be register, then we need to add many of above for each one, to avoid not many codes in Program and try to let it clear, we can create another service expansion method for handle this:

// MyDemo.Core/Service/RepositoryService.cs

public static class RepositoryService
{
    public static IServiceCollection AddRepositories(this IServiceCollection services)
    {
        services.AddScoped<IUserRepository, UserRepository>();
        return services;
    }
}

and just call it in Program.cs

builder.Services.AddRepositories();

of course, you also need to add more repositories into this service when you need, but just can keep to program.cs clear and easy to maintain.

The first Api method is GetUsers, that’s mean we need to get more than one users, and use the HttpGet attribute for specify this is a Get method

/// <summary>
/// Get all users
/// Api Url: http://website-domain/api/users  (without controller name)
/// </summary>
/// <returns></returns>
[HttpGet("users")]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
    var apiResult = new ApiResult<IEnumerable<User>>();
    try
    {
        apiResult.Data = await _userRepository.GetAllAsync();
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

Create a Get method GetUser for get single user

 /// <summary>
/// Get user by id
/// Api Url: http://website-domain/api/user/id  (without controller name)
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("user/{id}")]
public async Task<ActionResult<User>> GetUser(int id)
{
    var apiResult = new ApiResult<User>();
    try
    {
        apiResult.Data = await _userRepository.GetItemWithConditionAsync(u => u.Id == id);
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

Create a Post method PostUser for create user, we need to check the user name whether exist before create the new one

/// <summary>
/// Create a user
/// Api Url: http://website-domain/api/user  (without controller name)
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost("user")]
public async Task<ActionResult<User>> PostUser(User user)
{
    var apiResult = new ApiResult<Object>();
    try
    {
        var isExist = await _userRepository.UserIsExist(user.Name);
        if (isExist)
        {
            apiResult.Success = false;
            apiResult.Message = "The user has already exists!";
        }
        else
        {
            _userRepository.Create(user);
            await _userRepository.SaveAsync();
        }
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}    

Create a Put method PutUser for update user

/// <summary>
/// Update the user
/// Api Url: http://website-domain/api/user  (without controller name)
/// </summary>
/// <param name="id"></param>
/// <param name="user"></param>
/// <returns></returns>
[HttpPut("user")]
public async Task<ActionResult<User>> PutUser(User user)
{
    var apiResult = new ApiResult<Object>();
    //if the id is 0, then don't allow to update it
    if (user.Id == 0)
    {
        return BadRequest();
    }
    try
    {
        //get the current DB user
        var dbUser = await _userRepository.GetItemWithConditionAsync(u => u.Id == user.Id);
        if (dbUser == null)
        {
            apiResult.Success = false;
            apiResult.Message = "Can't found the user!";
        }
        else
        {
            //update the user
            _userRepository.Update(user);
            await _userRepository.SaveAsync();
        }
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

In the end, create the Delete method DeleteUser for delete the user by id

/// <summary>
/// Delete user
/// Api Url: http://website-domain/api/user/id  (without controller name)
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete("user/{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
    var apiResult = new ApiResult<Object>();
    try
    {
        var dbUser = await _userRepository.GetItemWithConditionAsync(u => u.Id == id);
        if (dbUser == null)
        {
            apiResult.Success = false;
            apiResult.Message = "Can't found the user!";
        }
        else
        {
            //delete the user
            _userRepository.Delete(dbUser);
            await _userRepository.SaveAsync();
        }
        return Ok(apiResult);
    }
    catch (Exception ex)
    {
        apiResult.Success = false;
        apiResult.Message = ex.Message;
        return StatusCode(500, apiResult);
    }
}

Ok, for now, we have finished the CRUD methods for RESTful API, let’s try them through swagger. There are 5 APIs show in the swagger page:

First, we try to create a user with POST method. Pass the below data to API

It will return the below information, the success flag is true and there is no error message, that’s mean the record has been created!

we can check the database

That’s cool! Ok, let try to get the data

and we got the data with our AipResult object format.

well, I try to create more data as below for test get user by id

and try to get the id = 2 items

That’s great, it’s working! The last testing to try to delete an user by id

and check the database data

Everything is fine! 🙂

3. Summarize

We talked about what’s RESTful API and how to create it. But there is one thing we still need to improve, for the complete system, we should need to handle the event/error logs, this can help us to trace the error or some outstanding issue, so in the next, we will discus how to do it! :smirk:

Loading

<p>The post Create .Net Core Api controller for CRUD with repository first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2023/07/create-net-core-api-controller-for-crud-with-repository/feed/ 7
Create the Http service for support SSL both on Android and iOS with Xamarin Form https://www.coderblog.in/2020/03/create-the-http-service-for-support-ssl-both-on-android-and-ios-with-xamarin-form/ https://www.coderblog.in/2020/03/create-the-http-service-for-support-ssl-both-on-android-and-ios-with-xamarin-form/#comments Wed, 25 Mar 2020 10:13:49 +0000 http://www.coderblog.in/?p=248 4. Use the service in form project: It’s done!

<p>The post Create the Http service for support SSL both on Android and iOS with Xamarin Form first appeared on Coder Blog.</p>

]]>

If you try to make a API call in xamarin, you should use the HTTPClient, this is a standard network call for c#, but when you try to call with SSL on Android, you will get the below error:

For solve this is issue, you need to ignore the SSL hostname verifier on Android, so you need to create a custom service for this.

  1. Create the IHttpClientHandler interface in form project:
//file name:  IHttpClientHandler.cs
using System;
using System.Net.Http;

namespace MyMobileApp.Services
{
    public interface IHttpClientHandler
    {
        HttpClient GetHttpClient();
    }
}
  1. For iOS, just use the default HttpClient is ok, so create the iOS service in iOS project as below:
//file name: HttpClientiOS.cs
using System;
using System.Net.Http;
using System.Runtime.CompilerServices;
using MyMobileApp.iOS.Services;
using MyMobileApp.Services;
using Xamarin.Forms;


[assembly: Xamarin.Forms.Dependency(typeof(HttpClientiOS))]
namespace MyMobile.iOS.Services
{  

    public class HttpClientiOS : IHttpClientHandler
    {
        public HttpClient GetHttpClient()
        {
            var client = new HttpClient();
            return client;
        }
    }
}
  1. Create the android service to ignore the SSL hostname verifier:
//file name: HttpClientAndroid.cs
using System;
using System.Net.Http;
using Android.Net;
using Javax.Net.Ssl;
using Org.Apache.Http.Client;
using MyMobile.Droid.Services;
using MyMobile.Services;
using Xamarin.Android.Net;
using Xamarin.Forms;

[assembly: Dependency(typeof(HttpClientAndroid))]
namespace MyMobile.Droid.Services
{
    public class HttpClientAndroid : IHttpClientHandler
    {
        public HttpClient GetHttpClient()
        {
            var client = new HttpClient(new IgnoreSSLClientHandler());
            return client;
        }
    }

    internal class IgnoreSSLClientHandler : AndroidClientHandler
    {
        protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
        {
            return SSLCertificateSocketFactory.GetInsecure(1000, null);
        }

        protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
        {
            return new IgnoreSSLHostnameVerifier();
        }
    }

    internal class IgnoreSSLHostnameVerifier : Java.Lang.Object, IHostnameVerifier
    {
        public bool Verify(string hostname, ISSLSession session)
        {
            return true;
        }
    }
}

4. Use the service in form project:

using (var client = DependencyService.Get<Services.IHttpClientHandler>().GetHttpClient())
{
    //do the logic for call API
    var jsonRequest = new { email = this.Email, password = this.Password };

    var serializedJsonRequest = JsonConvert.SerializeObject(jsonRequest);
    HttpContent content = new StringContent(serializedJsonRequest, Encoding.UTF8, "application/json");

    var response = await client.PostAsync(new Uri(Globals.APIs.login), content);
    if (response.IsSuccessStatusCode)
    {
        //todo...
    }
}

It’s done!

Loading

<p>The post Create the Http service for support SSL both on Android and iOS with Xamarin Form first appeared on Coder Blog.</p>

]]>
https://www.coderblog.in/2020/03/create-the-http-service-for-support-ssl-both-on-android-and-ios-with-xamarin-form/feed/ 4