Asp.Net Core WebAPI를 사용하여 Reactjs에서 이미지를 로드하는 방법에 대해 논의했습니다. Asp.Net Core 웹 API를 생성하고 엔터티 프레임워크 코어를 사용하여 SQL 서버 DB를 생성했습니다. 그런 다음 이미지 로딩을 위한 asp.net Core API 컨트롤러를 만들었습니다.
우리는 Reactjs로 클라이언트 측 애플리케이션을 만들었습니다. 이를 위해 이미지 업로더가 포함된 양식이 설계되었습니다. 선택한 이미지 미리보기가 별도로 표시됩니다. 양식 제출 이벤트 내에서 선택한 이미지를 Asp.Net Web API에 로드했습니다.
도구: VS Code, Visual Studio, SSMS, Postman
클라이언트: ReactJS
API: Asp.Net 코어 WebAPI
이 컨트롤러는 데이터베이스 연결에 연결되어 있으며 HTTP GET, POST, PUT 및 DELETE 요청을 처리하여 직원 기록을 나열, 삽입, 업데이트 및 삭제하는 방법을 제공합니다.
이 컨트롤러는 직원이 이미지를 업로드할 수 있는 직원 양식과 상호 작용하여 작동합니다. 이 양식은 React 측에서 설계되었으며 프로젝트의 루트 디렉터리에 있는 npx create-react-app 명령을 사용하여 생성됩니다.
GET, POST, PUT 및 DELETE 작업에 대한 관련 메서드가 제공됩니다. GET 요청을 사용하면 기존 직원 기록이 모두 나열됩니다. PUT 요청은 지정된 직원 기록을 업데이트하고 직원의 프로필 사진을 변경하는 데에도 사용됩니다. POST 요청은 새 직원 기록을 추가하고 직원의 프로필 사진을 업로드합니다. DELETE 요청은 지정된 직원 기록을 삭제합니다.
또한 직원 기록의 이미지를 로드하고 삭제하기 위해 SaveImage() 및 DeleteImage()라는 두 가지 특수 메서드가 제공됩니다. 이러한 메서드는 직원 프로필 이미지의 업로드 및 삭제를 처리합니다.
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 ) ; } } }
이 양식을 통해 사용자는 직원을 추가하거나 편집할 수 있습니다. 이 구성 요소는 이미지를 업로드하고 직원 이름 및 직무에 대한 정보를 입력할 수 있는 양식을 제공합니다. 양식은 이미지 업로드 영역과 두 개의 텍스트 입력 필드로 구성됩니다.
변수가 많이 사용됩니다. defaultImageSrc 변수는 기본 이미지 경로를 보유합니다. initialFieldValues 변수에는 컴포넌트의 초기 상태에 사용될 초기 값이 포함됩니다. useState를 사용하면 구성 요소 내에서 값의 상태가 추적됩니다.
useEffect 함수는 컴포넌트를 로드하는 동안 사용되며, RecordForEdit 변수가 변경되면 컴포넌트가 다시 로드되도록 트리거하고 값 변수는 setValues 함수로 업데이트됩니다.
텍스트 입력 필드가 변경되면 handlerInputChange 함수가 호출됩니다. 이 함수는 e.target.name 및 e.target.value를 사용하여 변경된 필드의 이름과 값을 캡처하고 setValues 함수를 사용하여 값 변수를 업데이트합니다.
이미지 업로드 영역이 변경되면 showPreview 함수가 호출됩니다. 선택한 파일의 경로를 imageFile 변수에 저장하고 FileReader 객체를 사용하여 파일의 미리보기를 생성하고 imageSrc 변수를 업데이트합니다.
유효성 검사 기능은 양식의 정확성을 확인합니다. 이 함수는 값 변수가 올바르게 채워졌는지 확인하는 데 사용됩니다. setErrors 함수는 오류 변수를 업데이트하고 ResetForm 함수는 양식을 재설정하고 오류 변수를 지웁니다.
HandleFormSubmit 함수는 양식이 제출될 때 호출됩니다. 이 함수는 FormData 객체를 사용하여 양식 데이터를 검색하고 addOrEdit 함수를 호출하여 데이터를 서버로 보냅니다.
applyErrorClass 함수는 잘못된 필드 클래스를 잘못된 필드에 적용하므로 해당 필드가 오류와 함께 표시됩니다.
이 구성 요소는 이미지를 업로드하고 직원 이름 및 직무에 대한 정보를 입력할 수 있는 양식을 제공합니다. 양식은 이미지 업로드 영역과 두 개의 텍스트 입력 필드로 구성됩니다.
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 >
>
)
}
이 React 구성 요소는 직원 기록 생성, 편집, 삭제 및 보기를 처리합니다.
useState 및 useEffect React 후크를 사용합니다. useState는 구성 요소의 상태에 대한 상태 변수를 정의하고 setState를 반환합니다. useEffect는 구성요소 로드 또는 다시 그리기와 같은 이벤트에 대한 응답으로 함수를 실행합니다.
Axios는 RESTful 웹 서비스 호출을 수행하는 데 사용됩니다. EmployeeAPI라는 도우미 함수는 RESTful 호출을 인스턴스화하고 Axios 기능을 사용하여 리소스 URL을 변경할 수 있습니다.
RefreshEmployeeList 함수는 서버에서 직원 기록을 검색하고 이를 setEmployeeList를 사용하여 구성 요소의 상태로 설정합니다.
addOrEdit 함수는 직원 기록 생성 또는 업데이트를 처리합니다. FormData 객체에는 직원 기록에 대한 사용자 항목이 포함되어 있습니다.
onDelete 함수는 사용자에게 레코드 삭제 여부를 확인한 다음 서버에 DELETE 요청을 보냅니다.
imageCard 함수는 직원 카드를 표시하는 데 사용됩니다. 카드에는 직원의 사진, 이름, 직무 설명 및 삭제 버튼이 포함됩니다.
마지막으로 구성 요소에는 점보트론 헤더, 직원 양식 및 직원 카드 테이블이 포함됩니다. 테이블에는 세 개의 열과 세 번째 열이 채워지지 않은 추가 행으로 구성된 직원 카드가 표시됩니다.
{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 >
)
}
내 프로젝트에서 .Net Core 6.0을 사용하고 있습니다. 프로젝트에서 다운로드해야 하는 Nuget 패키지는 다음과 같습니다.
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
먼저 Models 폴더에 클래스를 추가해 보겠습니다. 이름을 EmployeeModel로 지정했습니다.
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 ; }
}
Models 폴더에 Context 클래스를 만들어 보겠습니다.
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 ; }
}
}
Startup.cs 폴더로 이동하여 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" ) ) ) ;
}
appsettings.json으로 이동하여 다음과 같이 json 파일에 연결 정보를 작성합니다.
"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;"
}
}
그런 다음 프로젝트를 빌드하고 패키지 관리자로 이동하여 마이그레이션 작업을 수행합니다.
PM > Add-Migration " InitialCreate "
명령으로 마이그레이션을 생성하고 프로젝트를 빌드해 보겠습니다. 그런 다음 이 명령을 내리세요.
PM > update-database
그럼 MSSQL에서 데이터베이스가 생성되었는지 확인해 보겠습니다.
그런 다음 EF 컨트롤러를 사용하는 작업과 함께 API 컨트롤러를 사용하여 모델에 대한 컨트롤러를 만들어 보겠습니다.
사진이 여기에 표시됩니다. 그 중 2개
컨트롤을 만들고, 모델과 컨텍스트 파일을 지정하고, 컨트롤러 이름을 지정했습니다.
5 AddDb.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DevConnection")))라는 서비스; 이 함수는 우리가 이 EmployeeController에 요청할 때마다 이 직원 컨트롤러에 대한 요청을 생성합니다. EmployeeController의 생성자는 public EmployeeController(EmployeeDbContext context)입니다. 이 코드의 EmployeeDbContext 값은 프레임워크 자체에서 자동으로 관리되는 asp.net 종속성 주입 방법이므로 이 컨트롤러의 요청은 현재 db와 통신합니다.
이제 직원 프로필 사진을 업로드하기 위해 직원 양식에 반응하도록 클라이언트 측을 디자인해 보겠습니다. 프로젝트 디렉터리에서 명령 프롬프트를 열어야 합니다.
npx create-react-app employee-register-client
클라이언트 측에 사용되는 Oxios 명령
$ npm install react-axios
이제 프로젝트의 구조를 만들었습니다. 이제 내 저장소에서 내 코드를 검토해 보세요. 행운을 빌어요 :)
GET /api/Employee
매개변수 | 약 | 설명 |
---|---|---|
api_key | string | 필요한 . 귀하의 API 키. |
GET /api/Employee/${id}
매개변수 | 약 | 설명 |
---|---|---|
id | string | 필요한 . 호출할 항목의 키값 |
{ "employeeID" : 3 , "employeeName" : " piedhorse " , "occupation" : " C# " , "imageName" : " capture_20231646962.jpeg " , "imageFile" : null , "imageSrc" : " https://localhost:44334/Images/capture_20231646962.jpeg " }