Ich habe besprochen, wie man Bilder in Reactjs mit Asp.Net Core WebAPI lädt. Wir erstellen die Asp.Net Core-Web-API und eine SQL Server-Datenbank mit einem Entity-Framework-Kern. und dann habe ich einen asp.net Core API-Controller zum Laden von Bildern erstellt.
Wir haben die clientseitige Anwendung in Reactjs erstellt. Hierfür wurde ein Formular mit Bild-Uploader entwickelt. Die ausgewählte Bildvorschau wird separat angezeigt. Im Rahmen des Formularsendeereignisses habe ich das ausgewählte Bild in die Asp.Net-Web-API geladen.
Tools: VS Code, Visual Studio, SSMS, Postman
Kunde: Reactjs
API: Asp.Net Core WebAPI
Dieser Controller ist mit einer Datenbankverbindung verbunden und bietet Methoden zum Auflisten, Einfügen, Aktualisieren und Löschen von Mitarbeiterdatensätzen durch die Verarbeitung von HTTP-GET-, POST-, PUT- und DELETE-Anfragen.
Dieser Controller interagiert mit einem Mitarbeiterformular, in das Mitarbeiter ihre Bilder hochladen können. Dieses Formular wurde auf der React-Seite entworfen und mit dem Befehl npx create-react-app im Stammverzeichnis des Projekts erstellt.
Für GET-, POST-, PUT- und DELETE-Operationen werden relevante Methoden bereitgestellt. Bei einer GET-Anfrage werden alle vorhandenen Mitarbeiterdatensätze aufgelistet. Eine PUT-Anfrage aktualisiert einen bestimmten Mitarbeiterdatensatz und wird auch zum Ändern des Profilbilds eines Mitarbeiters verwendet. Die POST-Anfrage fügt einen neuen Mitarbeiterdatensatz hinzu und lädt das Profilbild des Mitarbeiters hoch. Eine DELETE-Anfrage löscht einen angegebenen Mitarbeiterdatensatz.
Darüber hinaus stehen zwei spezielle Methoden, SaveImage() und DeleteImage(), zum Laden und Löschen von Bildern in Mitarbeiterdatensätzen zur Verfügung. Diese Methoden übernehmen das Hochladen und Löschen von Mitarbeiterprofilbildern.
using System ; using System . Collections . Generic ; using System . Linq ; using System . Threading . Tasks ; using Microsoft . AspNetCore . Http ; using Microsoft . AspNetCore . Mvc ; using Microsoft . EntityFrameworkCore ; using EmployeeRegisterAPI . Models ; using System . IO ; using Microsoft . AspNetCore . Hosting ; namespace EmployeeRegisterAPI . Controllers { [ Route ( "api/[controller]" ) ] [ ApiController ] public class EmployeeController : ControllerBase { private readonly EmployeeDbContext _context ; private readonly IWebHostEnvironment _hostEnvironment ; public EmployeeController ( EmployeeDbContext context , IWebHostEnvironment hostEnvironment ) { _context = context ; this . _hostEnvironment = hostEnvironment ; } // GET: api/EmployeeModels [ HttpGet ] public async Task < ActionResult < IEnumerable < EmployeeModel > > > GetEmployees ( ) { return await _context . Employees . Select ( x => new EmployeeModel ( ) { EmployeeID = x . EmployeeID , EmployeeName = x . EmployeeName , Occupation = x . Occupation , ImageName = x . ImageName , ImageSrc = String . Format ( "{0}://{1}{2}/Images/{3}" , Request . Scheme , Request . Host , Request . PathBase , x . ImageName ) } ) . ToListAsync ( ) ; } // GET: api/Employee/5 [ HttpGet ( "{id}" ) ] public async Task < ActionResult < EmployeeModel > > GetEmployeeModel ( int id ) { var employeeModel = await _context . Employees . FindAsync ( id ) ; if ( employeeModel == null ) { return NotFound ( ) ; } return employeeModel ; } // PUT: api/Employee/5 // To protect from overposting attacks, enable the specific properties you want to bind to, for // more details, see https://go.microsoft.com/fwlink/?linkid=2123754. [ HttpPut ( "{id}" ) ] public async Task < IActionResult > PutEmployeeModel ( int id , [ FromForm ] EmployeeModel employeeModel ) { if ( id != employeeModel . EmployeeID ) { return BadRequest ( ) ; } if ( employeeModel . ImageFile != null ) { DeleteImage ( employeeModel . ImageName ) ; employeeModel . ImageName = await SaveImage ( employeeModel . ImageFile ) ; } _context . Entry ( employeeModel ) . State = EntityState . Modified ; try { await _context . SaveChangesAsync ( ) ; } catch ( DbUpdateConcurrencyException ) { if ( ! EmployeeModelExists ( id ) ) { return NotFound ( ) ; } else { throw ; } } return NoContent ( ) ; } // POST: api/Employee // To protect from overposting attacks, enable the specific properties you want to bind to, for // more details, see https://go.microsoft.com/fwlink/?linkid=2123754. [ HttpPost ] public async Task < ActionResult < EmployeeModel > > PostEmployeeModel ( [ FromForm ] EmployeeModel employeeModel ) { employeeModel . ImageName = await SaveImage ( employeeModel . ImageFile ) ; _context . Employees . Add ( employeeModel ) ; await _context . SaveChangesAsync ( ) ; return StatusCode ( 201 ) ; } // DELETE: api/Employee/5 [ HttpDelete ( "{id}" ) ] public async Task < ActionResult < EmployeeModel > > DeleteEmployeeModel ( int id ) { var employeeModel = await _context . Employees . FindAsync ( id ) ; if ( employeeModel == null ) { return NotFound ( ) ; } DeleteImage ( employeeModel . ImageName ) ; _context . Employees . Remove ( employeeModel ) ; await _context . SaveChangesAsync ( ) ; return employeeModel ; } private bool EmployeeModelExists ( int id ) { return _context . Employees . Any ( e => e . EmployeeID == id ) ; } [ NonAction ] public async Task < string > SaveImage ( IFormFile imageFile ) { string imageName = new String ( Path . GetFileNameWithoutExtension ( imageFile . FileName ) . Take ( 10 ) . ToArray ( ) ) . Replace ( ' ' , '-' ) ; imageName = imageName + DateTime . Now . ToString ( "yymmssfff" ) + Path . GetExtension ( imageFile . FileName ) ; var imagePath = Path . Combine ( _hostEnvironment . ContentRootPath , "Images" , imageName ) ; using ( var fileStream = new FileStream ( imagePath , FileMode . Create ) ) { await imageFile . CopyToAsync ( fileStream ) ; } return imageName ; } [ NonAction ] public void DeleteImage ( string imageName ) { var imagePath = Path . Combine ( _hostEnvironment . ContentRootPath , "Images" , imageName ) ; if ( System . IO . File . Exists ( imagePath ) ) System . IO . File . Delete ( imagePath ) ; } } }
Das Formular ermöglicht es dem Benutzer, einen Mitarbeiter hinzuzufügen oder zu bearbeiten. Die Komponente stellt ein Formular bereit, in das Sie ein Bild hochladen und Informationen zum Namen und zur Stelle des Mitarbeiters eingeben können. Das Formular besteht aus einem Bild-Upload-Bereich und zwei Texteingabefeldern.
Es werden viele Variablen verwendet. Die Variable defaultImageSrc enthält den Standardbildpfad. Die Variable initialFieldValues enthält die Anfangswerte, die im Anfangszustand der Komponente verwendet werden sollen. Mithilfe von useState werden die Zustände der Werte innerhalb der Komponente verfolgt.
Die Funktion „useEffect“ wird während des Ladens der Komponente verwendet. Wenn sich die Variable „recordForEdit“ ändert, löst sie ein erneutes Laden der Komponente aus und die Variable „values“ wird mit der Funktion „setValues“ aktualisiert.
Die Funktion handleInputChange wird aufgerufen, wenn Texteingabefelder geändert werden. Diese Funktion erfasst den Namen und Wert des geänderten Felds mithilfe von e.target.name und e.target.value und aktualisiert die Wertevariable mithilfe der Funktion setValues.
Die Funktion showPreview wird aufgerufen, wenn der Bild-Upload-Bereich geändert wird. Diese Funktion speichert den Pfad der ausgewählten Datei in der Variable imageFile, erstellt mithilfe des FileReader-Objekts eine Vorschau der Datei und aktualisiert die Variable imageSrc.
Die Validierungsfunktion überprüft die Richtigkeit des Formulars. Diese Funktion wird verwendet, um sicherzustellen, dass die Wertevariable ordnungsgemäß gefüllt ist. Die Funktion „setErrors“ aktualisiert die Variable „errors“ und die Funktion „resetForm“ setzt das Formular zurück und löscht die Variable „errors“.
Die Funktion handleFormSubmit wird aufgerufen, wenn das Formular gesendet wird. Diese Funktion ruft die Formulardaten mithilfe des FormData-Objekts ab und sendet die Daten an den Server, indem sie die Funktion addOrEdit aufruft.
Die Funktion applyErrorClass wendet die Invalid-Field-Klasse auf fehlerhafte Felder an und führt so dazu, dass diese Felder mit Fehlern angezeigt werden.
Die Komponente stellt ein Formular bereit, in das Sie ein Bild hochladen und Informationen zum Namen und zur Stelle des Mitarbeiters eingeben können. Das Formular besteht aus einem Bild-Upload-Bereich und zwei Texteingabefeldern.
import React , { useState , useEffect } from 'react'
const defaultImageSrc = '/img/3135715.png'
const initialFieldValues = {
employeeID : 0 ,
employeeName : '' ,
occupation : '' ,
imageName : '' ,
imageSrc : defaultImageSrc ,
imageFile : null
}
export default function Employee ( props ) {
const { addOrEdit , recordForEdit } = props
const [ values , setValues ] = useState ( initialFieldValues )
const [ errors , setErrors ] = useState ( { } )
useEffect ( ( ) => {
if ( recordForEdit != null )
setValues ( recordForEdit ) ;
} , [ recordForEdit ] )
const handleInputChange = e => {
const { name , value } = e . target ;
setValues ( {
... values ,
[ name ] : value
} )
}
const showPreview = e => {
if ( e . target . files && e . target . files [ 0 ] ) {
let imageFile = e . target . files [ 0 ] ;
const reader = new FileReader ( ) ;
reader . onload = x => {
setValues ( {
... values ,
imageFile ,
imageSrc : x . target . result
} )
}
reader . readAsDataURL ( imageFile )
}
else {
setValues ( {
... values ,
imageFile : null ,
imageSrc : defaultImageSrc
} )
}
}
const validate = ( ) => {
let temp = { }
temp . employeeName = values . employeeName == "" ? false : true ;
temp . imageSrc = values . imageSrc == defaultImageSrc ? false : true ;
setErrors ( temp )
return Object . values ( temp ) . every ( x => x == true )
}
const resetForm = ( ) => {
setValues ( initialFieldValues )
document . getElementById ( 'image-uploader' ) . value = null ;
setErrors ( { } )
}
const handleFormSubmit = e => {
e . preventDefault ( )
if ( validate ( ) ) {
const formData = new FormData ( )
formData . append ( 'employeeID' , values . employeeID )
formData . append ( 'employeeName' , values . employeeName )
formData . append ( 'occupation' , values . occupation )
formData . append ( 'imageName' , values . imageName )
formData . append ( 'imageFile' , values . imageFile )
addOrEdit ( formData , resetForm )
}
}
const applyErrorClass = field => ( ( field in errors && errors [ field ] == false ) ? ' invalid-field' : '' )
return (
< >
< div className = "container text-center" >
< p className = "lead" > p >
div >
< form autoComplete = "off" noValidate onSubmit = { handleFormSubmit } >
< div className = "card" style = { { backgroundColor : '#ced114' } } >
< img src = { values . imageSrc } className = "card-img-top" />
< div className = "card-body" >
< div className = "form-group" >
< input type = "file" accept = "image/*" className = { "form-control-file" + applyErrorClass ( 'imageSrc' ) }
onChange = { showPreview } id = "image-uploader" />
div >
< div className = "form-group" style = { { backgroundColor : '#ced114' } } >
< input className = { "form-control" + applyErrorClass ( 'employeeName' ) } placeholder = "Employee Name" name = "employeeName"
value = { values . employeeName }
onChange = { handleInputChange } />
div >
< div className = "form-group" >
< input className = "form-control" placeholder = "Occupation" name = "occupation"
value = { values . occupation }
onChange = { handleInputChange } />
div >
< div className = "form-group text-center" >
< button type = "submit" className = "btn btn-light" style = { { color : '#ced114' } } > Submit button >
div >
div >
div >
form >
>
)
}
Diese React-Komponente übernimmt das Erstellen, Bearbeiten, Löschen und Anzeigen eines Mitarbeiterdatensatzes.
Es verwendet die React-Hooks useState und useEffect. useState definiert eine Zustandsvariable für den Zustand der Komponente und gibt setState zurück. useEffect führt eine Funktion als Reaktion auf Ereignisse wie das Laden oder Neuzeichnen der Komponente aus.
Axios wird verwendet, um RESTful-Webdienstaufrufe durchzuführen. Eine Hilfsfunktion namens „employeeAPI“ instanziiert RESTful-Aufrufe und kann die Ressourcen-URL mithilfe der Axios-Funktionen ändern.
Die Funktion „refreshEmployeeList“ ruft Mitarbeiterdatensätze vom Server ab und legt sie mit setEmployeeList als Status der Komponente fest.
Die Funktion addOrEdit übernimmt das Erstellen oder Aktualisieren des Mitarbeiterdatensatzes. Das FormData-Objekt enthält Benutzereinträge für den Mitarbeiterdatensatz.
Die onDelete-Funktion fordert den Benutzer auf, zu bestätigen, ob er einen Datensatz löschen möchte, und sendet dann eine DELETE-Anfrage an den Server.
Die imageCard-Funktion dient der Anzeige von Mitarbeiterkarten. Die Karte enthält das Bild des Mitarbeiters, den Namen, die Stellenbeschreibung und eine Schaltfläche zum Löschen.
Schließlich umfasst die Komponente einen Jumbotron-Header, ein Mitarbeiterformular und eine Tabelle mit Mitarbeiterkarten. Die Tabelle zeigt Mitarbeiterkarten mit drei Spalten und zusätzlichen Zeilen, bei denen die dritte Spalte nicht belegt ist.
{imageCard(employeeList[3 * i])} | {employeeList[3 * i + 1] ? imageCard(employeeList[3 * i + 1]) : null} | {employeeList[3 * i + 2] ? imageCard(employeeList[3 * i + 2]) : null} |
import React , { useState , useEffect } from 'react'
import Employee from './Employee'
import axios from "axios" ;
export default function EmployeeList ( ) {
const [ employeeList , setEmployeeList ] = useState ( [ ] )
const [ recordForEdit , setRecordForEdit ] = useState ( null )
useEffect ( ( ) => {
refreshEmployeeList ( ) ;
} , [ ] )
const employeeAPI = ( url = 'https://localhost:44334/api/Employee/' ) => {
return {
fetchAll : ( ) => axios . get ( url ) ,
create : newRecord => axios . post ( url , newRecord ) ,
update : ( id , updatedRecord ) => axios . put ( url + id , updatedRecord ) ,
delete : id => axios . delete ( url + id )
}
}
function refreshEmployeeList ( ) {
employeeAPI ( ) . fetchAll ( )
. then ( res => {
setEmployeeList ( res . data )
} )
. catch ( err => console . log ( err ) )
}
const addOrEdit = ( formData , onSuccess ) => {
if ( formData . get ( 'employeeID' ) == "0" )
employeeAPI ( ) . create ( formData )
. then ( res => {
onSuccess ( ) ;
refreshEmployeeList ( ) ;
} )
. catch ( err => console . log ( err ) )
else
employeeAPI ( ) . update ( formData . get ( 'employeeID' ) , formData )
. then ( res => {
onSuccess ( ) ;
refreshEmployeeList ( ) ;
} )
. catch ( err => console . log ( err ) )
}
const showRecordDetails = data => {
setRecordForEdit ( data )
}
const onDelete = ( e , id ) => {
e . stopPropagation ( ) ;
if ( window . confirm ( 'Are you sure to delete this record?' ) )
employeeAPI ( ) . delete ( id )
. then ( res => refreshEmployeeList ( ) )
. catch ( err => console . log ( err ) )
}
const imageCard = data => (
< div className = "card" onClick = { ( ) => { showRecordDetails ( data ) } } >
< img src = { data . imageSrc } className = "card-img-top rounded-circle" />
< div className = "card-body" >
< h5 > { data . employeeName } h5 >
< span > { data . occupation } span > < br />
< button className = "btn btn-light delete-button" onClick = { e => onDelete ( e , parseInt ( data . employeeID ) ) } >
< i className = "far fa-trash-alt" > i >
button >
div >
div >
)
return (
< div className = "row" >
< div className = "col-md-12" style = { { backgroundColor : '#ced114' } } >
< div className = "jumbotron jumbotron-fluid py-4" style = { { backgroundColor : '#ced114' } } >
< div className = "container text-center" style = { { backgroundColor : '#ced114' } } >
< h1 className = "display-4" style = { { backgroundColor : '#ced114' , color : 'yellow' } } > Employee Register h1 >
div >
div >
div >
< div className = "container text-center" >
< p className = "lead" > p >
div >
< div className = "col-md-4" >
< Employee
addOrEdit = { addOrEdit }
recordForEdit = { recordForEdit }
/>
div >
< div className = "col-md-8" >
< table >
< tbody >
{
//tr > 3 td
[ ... Array ( Math . ceil ( employeeList . length / 3 ) ) ] . map ( ( e , i ) =>
< tr key = { i } >
< td > { imageCard ( employeeList [ 3 * i ] ) } td >
< td > { employeeList [ 3 * i + 1 ] ? imageCard ( employeeList [ 3 * i + 1 ] ) : null } td >
< td > { employeeList [ 3 * i + 2 ] ? imageCard ( employeeList [ 3 * i + 2 ] ) : null } td >
tr >
)
}
tbody >
table >
div >
div >
)
}
Ich verwende .Net Core 6.0 in meinem Projekt. Nuget-Pakete, die im Projekt heruntergeladen werden müssen, sind unten aufgeführt.
Microsoft.VisualStudio.Web.CodeGeneration.Design
PM > NuGet I nstall-Package Microsoft.VisualStudio.Web.CodeGeneration.Design -Version 6.0.13
Microsoft.EntityFrameworkCore
PM > NuGet I nstall-Package Microsoft.EntityFrameworkCore -Version 7.0.4
Microsoft.EntityFrameworkCore.SqlServer
PM > NuGet I nstall-Package Microsoft.EntityFrameworkCore.SqlServer -Version 7.0.4
Microsoft.EntityFrameworkCore.Tools
PM > NuGet I nstall-Package Microsoft.EntityFrameworkCore.Tools -Version 7.0.4
Microsoft.AspNetCore.Cors
PM > NuGet I nstall-Package Microsoft.AspNetCore.Cors -Version 2.2.0
Fügen wir zunächst eine Klasse zu unserem Models-Ordner hinzu. Ich habe es EmployeeModel genannt.
public class EmployeeModel
{
[ Key ]
public int EmployeeID { get ; set ; }
[ Column ( TypeName = "nvarchar(50)" ) ]
public string EmployeeName { get ; set ; }
[ Column ( TypeName = "nvarchar(50)" ) ]
public string Occupation { get ; set ; }
[ Column ( TypeName = "nvarchar(100)" ) ]
public string ImageName { get ; set ; }
}
Erstellen wir eine Context-Klasse in unserem Models-Ordner.
using Microsoft . EntityFrameworkCore ;
namespace EmployeeRegisterAPI . Models
{
public class EmployeeDbContext : DbContext //burada ef Dbcontext den kalitim aldirdik
{
public EmployeeDbContext ( DbContextOptions < EmployeeDbContext > options ) : base ( options )
{
}
public DbSet < EmployeeModel > Employees { get ; set ; }
}
}
Gehen wir zum Ordner „Startup.cs“ und erstellen Sie eine Verbindungszeichenfolge für die Methode „ConfigureServices“.
public void ConfigureServices ( IServiceCollection services )
{
services . AddControllers ( ) ;
//Baglanti dizesini olusturduk ve bu kodda sql server icin bir ConnectionString olusturduk "DevConnection" diye bunula baglanti bilgilerimizi appsettings de belirtecegiz
services . AddDbContext < EmployeeDbContext > ( options => options . UseSqlServer ( Configuration . GetConnectionString ( "DevConnection" ) ) ) ;
}
Kommen wir zu appsettings.json. Hier schreiben wir die Verbindungsinformationen wie folgt in die JSON-Datei
"Logging" : {
"LogLevel" : {
"Default" : "Information" ,
"Microsoft" : "Warning" ,
"Microsoft.Hosting.Lifetime" : "Information"
}
} ,
"AllowedHosts" : "*" ,
//buradan sonraki kod satirlaridir
"ConnectionStrings" : {
"DevConnection" : "Server=LAPTOP-6OAEM3JA; Database=EmployeeDB; Trusted_Connection=True; MultipleActiveResultSets=True;"
}
}
Anschließend erstellen wir unser Projekt, gehen zum Paketmanager und führen unsere Migrationsvorgänge durch.
PM > Add-Migration " InitialCreate "
Lassen Sie uns mit dem Befehl Migration erstellen und unser Projekt erstellen. Geben Sie dann diesen Befehl ein
PM > update-database
Dann prüfen wir, ob die Datenbank aus MSSQL erstellt wurde oder nicht.
Und dann erstellen wir einen Controller für unser Modell. Ich werde den API-Controller mit Aktionen verwenden, die den EF-Controller verwenden.
Bilder kommen hierher, 2 davon
Wir haben unsere Steuerelemente erstellt, unser Modell und unsere Kontextdatei angegeben und unseren Controller-Namen angegeben.
5 Diese Dienste namens AddDb.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DevConnection"))); Die Funktion erstellt jedes Mal eine Anfrage für diesen Mitarbeitercontroller, wenn wir eine Anfrage an diesen EmployeeController stellen. Der Konstruktor des EmployeeControllers ist public EmployeeController(EmployeeDbContext context). Der EmployeeDbContext-Wert in diesem Code ist diese asp.net-Abhängigkeitsinjektionsmethode, die automatisch vom Framework selbst verwaltet wird, sodass die Anforderung in diesem Controller vorerst mit der Datenbank kommuniziert.
Lassen Sie uns nun die Client-Seite so gestalten, dass sie mit einem Mitarbeiterformular reagiert, um Mitarbeiterprofilbilder hochzuladen. Wir müssen die Eingabeaufforderung aus dem Verzeichnis dieses Projekts öffnen.
npx create-react-app employee-register-client
Oxios-Befehl, der für die Clientseite verwendet werden soll
$ npm install react-axios
Wir haben die Struktur unserer Projekte erstellt. Von hier aus möchte ich, dass Sie meine Codes in meinem Repo überprüfen. Viel Glück :)
GET /api/Employee
Parameter | Medizin | Erläuterung |
---|---|---|
api_key | string | Notwendig . Ihr API-Schlüssel. |
GET /api/Employee/${id}
Parameter | Medizin | Erläuterung |
---|---|---|
id | string | Notwendig . Schlüsselwert des aufzurufenden Elements |
{ "employeeID" : 3 , "employeeName" : " piedhorse " , "occupation" : " C# " , "imageName" : " capture_20231646962.jpeg " , "imageFile" : null , "imageSrc" : " https://localhost:44334/Images/capture_20231646962.jpeg " }