4 Ocak '22 7 ay önce
2 dk 264 kelime
Bu makale ben ve Oğuzhan Karacabay tarafından yazılmıştır, İngilizce dilinde de okunabilir, Medium üzerinde de okunabilir.
Bir Frontend SPA
’dan Laravel Backend API
’nize hem dosya yüklemek hem de JSON
veri göndermek istediğinizde multipart/form-data
şeklinde gönderirsiniz.
Frontend Framework’lardan bağımsız olarak bunu yapmanın tipik bir örneği şöyle olabilir:
postMultipartFormData(form) {
let formData = new FormData()
formData.append('file', form.file)
formData.append('id', form.id) // 1234
formData.append('reason_type', form.reason_type) // 3
formData.append('rate', form.rate) // 10.15
formData.append('fee', form.fee * 100) // 1000 * 100
formData.append('tax', form.tax * 100) // 190 * 100
formData.append('description', form.description) // some description
formData.append('action_date', form.action_date) // 2022-01-07 17:52:06
return apiClient
.post('/post/multipart-formdata', formData, {
headers: {'Content-Type': 'multipart/form-data'}
})
.then(response => { return response })
.catch(err => { throw err })
}
Frontend’ten gelen bu isteği Backend API
’de, iyi bir Laravel geliştiricisi olarak, Controller
'a uğramadan önce, bir Request
ile doğrulamak istersiniz.
Laravel Controller
’u da aşağı yukarı şuna benzer.
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\MultipartFormRequest;
use App\Http\Resources\MultipartFormResource;
class MultipartFormController extends Controller
{
public function store(MultipartFormRequest $request): MultipartFormResource
{
$file = $request->file('file');
$path = '/your/path';
$filename = 'filename.xlsx';
// Save file to a local or remote file bucket
$file->storeAs($path, $filename, ['disk' => 's3-public']);
// Create a model with file url
$yourModel = YourModel::create(array_merge($request->validated(), [
'file_url' => $path . $filename,
]));
// Return a resource with your newly generated model
return new MultipartFormResource($yourModel);
}
}
Veriler ve yüklenecek dosya Controller
’a gelmeden önce şuna benzer bir Request
ile doğrulanır:
<?php
namespace App\Http\Requests;
use App\Enums\PromissoryNoteReasonType;
use BenSampo\Enum\Rules\EnumValue;
use Illuminate\Foundation\Http\FormRequest;
class MultipartFormRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'bank_id' => ['bail', 'required', 'numeric', 'exists:banks,id'],
'promissory_note_reason' => ['bail', 'required', new EnumValue(PromissoryNoteReasonType::class)],
'interest_rate' => ['bail', 'required', 'between:0,99.9999'],
'fixed_fee' => ['bail', 'required', 'integer'],
'tax' => ['bail', 'required', 'integer'],
'description' => ['bail', 'required', 'string'],
'action_date' => ['bail', 'required', 'date'],
'file' => ['bail', 'required', 'file', 'mimes:xls,xlsx',
],
];
}
}
Buraya kadar her şey mükemmel ilerledi fakat bu kodu çalıştırdığınızda, gönderdiğiniz verilerin doğrulamadan geçemediğini ve derinlemesine incelediğinizde Laravel API
’nize gelen verilerin beklediğimizden çok farklı olduğunu farkedeceksiniz:
array:8 [▼
"file" => "file-content"
"id" => "123"
"reason_type" => "3"
"rate" => "10.15"
"fee" => "10000"
"tax" => "1900"
"description" => "request description"
"action_date" => "2020-10-07 17:52:06"
]
Böylece multipart/formdata
’nın binary dosya gönderebilme yeteneğinin yanında, bütün veri tiplerini string
’lere çevirdiğini farkettiniz.
Bu aşamadan sonra gelen verileri API
tarafında doğrulama kurallarınıza gelen verilerin string
olacağını varsayarak değiştirdikten sonra, string
tipindeki değerleri tam olarak doğrulamak için kendi parse jimnastiğinizi
yapabilirsiniz.
Eğer FormData
nesnesi sadece string
türünde veri gönderebiliyor ve dosya yüklemek için mutlaka FormData
nesnesi kullanmamız gerekiyorsa biz de öyle yaparız. Tabii ki öncesinde gönderilecek verilerin hepsini bir JSON
string
'e çevirerek.
Frontend tarafında yüklemek istediğimiz dosyayı FormData
’ya ekledikten sonra (I.) tüm diğer tüm verileri stringify()
ile bir JSON
string
’e çeviriyoruz. (II.)
Böylece Backend API
’sine göndermek üzere, elimizde sadece file
ve payload
verilerini içeren bir FormData
nesnesi olmuş oldu. (III.)
stringify()
fonksiyonu float tipindeki verileri JSON
string
’e doğru veri tipiyle aktaramadığı için parseFloat()
fonksiyonunu kullandık. (IV.)
postMultipartFormData(form) {
let formData = new FormData()
formData.append('file', form.file) // I.
let payload = JSON.stringify({
id: form.id,
reason_type: form.reason_type,
rate: parseFloat(form.rate), // IV.
fee: form.fee * 100,
tax: form.tax * 100,
description: form.description,
action_date: form.action_date
}); // II.
formData.append('payload', payload) // III.
return apiClient
.post('/post/multipart-formdata', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(response => { return response })
.catch(err => { throw err })
}
Backend API
tarafına gelen JSON
string
'i doğrulama kurallarından geçirmeden hemen önce bir JSON
Payload
'a çevirmemiz gerekiyor. Laravel’in Request
sınıflarındaki prepareForValidation()
metodu da tam da bu iş için var.
/**
* Prepare the data for validation.
*
* @return void
*
* @throws \JsonException
*/
protected function prepareForValidation(): void
{
$this->merge(json_decode($this->payload, true, 512, JSON_THROW_ON_ERROR));
}
prepareForValidation()
metodu içinde JSON
String
’i json_decode()
fonksiyonunu kullanarak JSON
Payload
’a çevirdikten sonra yine Laravel Request
sınıflarının diğer bir metodu olan merge()
ile diğer Request
verileriyle birleştiriyoruz.
array:7 [▼
"id" => 123
"reason_type" => 3
"rate" => 10.15
"fee" => 10000
"tax" => 1900
"description" => "request description"
"action_date" => "2020-10-07 17:52:06"
]
Böylece verilerimiz uygun veri tiplerine cast edilerek doğrulama kurallarından geçebilmiş oldu. Ne doğrulama kurallarını değiştirmeye ne de ayrıca doğrulama jimnastiği yapmaya gerek kaldı.