Wilfredo Meneses
Profesor universitario
Node:
.Net 5.0:
Visual Studio Code:
Postman:
En un contexto de desarrollo de software, es una implementación inicial mínima de la arquitectura de una aplicación que incluye y conecta los componentes básicos del sistema.
Como su nombre lo indica, la estructura es funcional de una manera rudimentaria. El sistema no proporciona el nivel de servicio requerido para el producto final. Los subsistemas están incompletos pero conectados.
Objetivos:
mkdir DatingApp
cd DatingApp
dotnet --info
dotnet -help
#Crear una solución
dotnet new sln
#Crear la API
dotnet new webapi -o API
#Ver los archivo creados
ls
#Agregar el proyecto API a la solución
dotnet sln add APIInstalar las siguiente extensiones:
Activar Auto save
cd API
dotnet watch run
dotnet dev-certs https --trust
AppUser
Entity Framework
DbContext
Instalar en vscode la extensión NuGet Gallery
Ctrl + Shift + P
Escribir NuGet: Open NuGet Gallery
Crear la carpeta Data y el archivo DataContext.cs
using API.Entities;
using Microsoft.EntityFrameworkCore;
namespace API.Data
{
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<AppUser> Users { get; set; }
}
}En Startup.cs
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext> (options => {
options.UseSqlite("Connection string");
});
services.AddControllers();
}
// ...En appSettings.Development.json
// ...
"ConnectionStrings": {
"DefaultConnection": "Data source=datingapp.db"
},
// ...// ...
private readonly IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
// ....
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(options =>
{
options.UseSqlite(_config.GetConnectionString("DefaultConnection"));
});
services.AddControllers();
}En Startup.cs
Con Nuget, instalar: Microsoft.EntityFrameworkCore.Design
// Hacer la migración desde la terminal
dotnet ef migrations add InitialCreate -o Data/MigrationsInstalar desde la terminal: dotnet tool install --global dotnet-ef --version 5.0.1 (Buscar la versión en nuget.org)
// Desde la terminal
dotnet ef database updatePara la administración de la BD se debe instalar la extesión SQLite en Visual Studio Code
Ctrl + Shift + P, escribe SQLite y selecciona la opción Open Database y luego especifica la ruta de la aplicación.
Listo!, ya se puede gestionar la base de datos.
using System.Collections.Generic;
using System.Linq;
using API.Data;
using API.Entities;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly DataContext _context;
public UsersController(DataContext context)
{
_context = context;
}
[HttpGet]
public ActionResult<IEnumerable<AppUser>> GetUsers()
{
return _context.Users.ToList();
}
[HttpGet("{id}")]
public ActionResult<AppUser> GetUser(int id)
{
return _context.Users.Find(id);
}
}
}Crear UsersController.cs
Habiendo creado el controlador es momento de probar las rutas creadas a través de Postman.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly DataContext _context;
public UsersController(DataContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<AppUser>>> GetUsers()
{
return await _context.Users.ToListAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<AppUser>> GetUser(int id)
{
return await _context.Users.FindAsync(id);
}
}
}// Desde la terminal en la raíz del proyecto
git status
git init
dotnet new gitignore
// Agregar en gitignore
appsettings.json
// Continuamos con GIT
git add .
git commit -m "Users entity finished"
git remote add origin https://github.com/meneseswill/DatingApp.git
git push -u origin masterDescargar GIT: https://git-scm.com/downloads
Crear cuenta en GitHub: https://github.com/l
Objetivos:
Objetivos:
Completar esa sesión requiere entender:
Visite el siguiente sitio para ver el proceso de instalación de Angular CLI: https://cli.angular.io/
// Verificar que se cumple con los requerimientos mínimos
node --version
npm --version
// Instalar Angular CLI
npm install -g @angular/cli
ng new clientcd client
ng serveAgregar el módulo en src/app/app.module.ts
// ...
import {HttpClientModule} from '@angular/common/http';
// ...
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],En src/app/app.component.ts
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'The Dating App';
users: any;
constructor(private http: HttpClient){}
ngOnInit(){
this.getUsers();
}
getUsers() {
this.http.get('https://localhost:5001/api/users').subscribe(response => {
this.users = response;
}, error => {
console.log(error);
})
}
}
Para arreglar el problema de los CORS, vamos a la API, en Startup.cs
// ....
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddCors();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.UseRouting();
app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:4200"));
app.UseAuthorization();
// ...
}
// ...En app.component.html
<h1>{{title}}</h1>
<ul>
<li *ngFor='let user of users'>
{{user.id}} - {{user.userName}}
</li>
</ul>Sitio oficial: https://valor-software.com/ngx-bootstrap
cd client
ng add ngx-bootstrap
npm install font-awesome
// Se debe reiniciar AngularOS X
1. Double click on the certificate (server.crt)
2. Select your desired keychain (login should suffice)
3. Add the certificate
4. Open Keychain Access if it isn’t already open
5. Select the keychain you chose earlier
6. You should see the certificate localhost
7. Double click on the certificate
8. Expand Trust
9. Select the option Always Trust in When using this certificate
10. Close the certificate window
Windows 10
1. Double click on the certificate (server.crt)
2. Click on the button “Install Certificate …”
3. Select whether you want to store it on user level or on machine level
4. Click “Next”
5. Select “Place all certificates in the following store”
6. Click “Browse”
7. Select “Trusted Root Certification Authorities”
8. Click “Ok”
9. Click “Next”
10. Click “Finish”
Copiar los certificados server.crt y server.key en una carpeta dentro de client llamada ssl, luego en angular.json
// ...
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"sslCert": "./ssl/server.crt",
"sslKey": "./ssl/server.key",
"ssl": true,
"browserTarget": "client:build"
},
//...
//
//Hay que reiniciar los servidores, antes debe agregar en el CORS la ruta actual
//Esto es en Startup.cs
app.UseCors(x => x.AllowAnyHeader()
.AllowAnyMethod()
.WithOrigins("https://localhost:4200"));git add .
git commit -m "End of section 3"
git push origin masterEn el gitignore del client, agregue la exclusión de la carpeta ssl
Objetivos:
Completar esa sesión requiere entender:
Objetivo:
Implementar autenticación en nuestra aplicación.
Entender:
Requerimientos:
Opción 1: en texto simple
Opción 2: encriptar la contraseña (hash)
Opción 2: Aplicar a la contraseña hash y salt
namespace API.Entities
{
public class AppUser
{
public int Id { get; set; }
public string UserName { get; set; }
public byte[] PasswordHash { get; set; }
public byte[] PasswordSalt { get; set; }
}
}// Desde la terminal
dotnet ef migrations add UserPasswordAdded
dotnet ef database update// En Controllers, crear la clase BaseApiController.cs
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class BaseApiController : ControllerBase
{
}
}// Se puede extender de BaseApiController y omitir los atributos iniciales
//
namespace API.Controllers
{
public class UsersController : BaseApiController
// ...using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using API.Data;
using API.Entities;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
public class AccountController : BaseApiController
{
private readonly DataContext _context;
public AccountController(DataContext context)
{
_context = context;
}
[HttpPost("register")]
public async Task<ActionResult<AppUser>> Register(string username, string password){
using var hmac = new HMACSHA512();
var user = new AppUser {
UserName = username,
PasswordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password)),
PasswordSalt = hmac.Key,
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
return user;
}
}
}Ctrl + Shift + P => .NET Generate Assets for Build and Debug
En Windows
En MAC
(Data Transfer Object)
Crear el archivo DTOs/RegisterDto.cs
namespace API.DTOs
{
public class RegisterDto
{
public string Username { get; set; }
public string Password { get; set; }
}
}(Data Transfer Object)
Usar el DTO en AccountController.cs y validar si el usuario ya existe
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using API.Data;
using API.DTOs;
using API.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace API.Controllers
{
public class AccountController : BaseApiController
{
private readonly DataContext _context;
public AccountController(DataContext context)
{
_context = context;
}
[HttpPost("register")]
public async Task<ActionResult<AppUser>> Register(RegisterDto registerDto){
if(await UserExists(registerDto.Username)) return BadRequest("Username is taken");
using var hmac = new HMACSHA512();
var user = new AppUser {
UserName = registerDto.Username.ToLower(),
PasswordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(registerDto.Password)),
PasswordSalt = hmac.Key,
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
return user;
}
private async Task<bool> UserExists(string username)
{
return await _context.Users.AnyAsync(x => x.UserName == username.ToLower());
}
}
}using System.ComponentModel.DataAnnotations;
namespace API.DTOs
{
public class RegisterDto
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
}// ...
[HttpPost("login")]
public async Task<ActionResult<AppUser>> Login(LoginDto loginDto)
{
var user = await _context.Users
.SingleOrDefaultAsync(x => x.UserName == loginDto.Username);
if(user == null) return Unauthorized("Invalid username");
using var hmac = new HMACSHA512(user.PasswordSalt);
var computeHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(loginDto.Password));
for(int i = 0; i < computeHash.Length; i++)
{
if(computeHash[i] != user.PasswordHash[i]) return Unauthorized("Invalid password");
}
return user;
}
// ...En AccountController.cs
namespace API.DTOs
{
public class LoginDto
{
public string Username { get; set; }
public string Password { get; set; }
}
}Crear LoginDto.cs en DTOs
dotnet ef database drop
dotnet ef database update
dotnet watch runDesde la terminal
public string CreateToken(AppUser user)
{
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.UserName)
};
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}Cttr + shift + p => nugget Gallery
By Wilfredo Meneses