前提・実現したいこと
VueからLaravel側にaxios postでデータを送信したところ
「422 (Unprocessable Entity)」とコンソールに出力されました。
検証のプレビューには
address: ["The address field is required."] email: ["The email field is required."] name: ["The name field is required."] password: ["The password field is required."] password_confirmation: ["The password confirmation field is required."] message: "The given data was invalid."
と表示されましたが、今回のpostではこれらのデータは送信しておらず、
api側でもrequiredされるような設定はしておりません。
根本的な原因が分からないのでlaravel.logを確認したところ
「ERROR: Token Signature could not be verified.」と記録されていました。
これが422の原因かは分かりませんが解決したいと思っています。
発生している問題・エラーメッセージ
PHP
1// storage/logs/laravel.log 2ERROR: Token Signature could not be verified. {"exception":"[object] (Tymon\JWTAuth\Exceptions\TokenInvalidException(code: 0): Token Signature could not be verified. at /home/ren0525/troc/troc/troc/vendor/tymon/jwt-auth/src/Providers/JWT/Lcobucci.php:137) 3[stacktrace]
該当のソースコード
JavaScript
1// ItemPostForm.vue 2 3<template> 4 <div class="container"> 5 <form class="forms row" action="register" method="POST" @submit.prevent="post"> 6 <div class="form-group col-12"> 7 <p>商品画像</p> 8 <input type="file" @change="onFileChange" /> 9 <output v-if="preview" class="col-12"> 10 <img :src="preview" alt="" class="col-12"> 11 </output> 12 </div> 13 <div class="form-group col-12"> 14 <p>{{ postForm.error_messages.name }}</p> 15 <p>商品名</p> 16 <br /> 17 <input 18 type="text" 19 class="form-content" 20 id="name" 21 v-model="postForm.name" 22 /> 23 </div> 24 <div class="form-group col-12"> 25 <p>{{ postForm.error_messages.genre }}</p> 26 <p>商品ジャンル</p> 27 <br /> 28 <select v-model="postForm.genre"> 29 <option>Genre1</option> 30 <option>Genre2</option> 31 <option>Genre3</option> 32 <option>Genre4</option> 33 <option>Genre5</option> 34 <option>Genre6</option> 35 <option>Genre7</option> 36 <option>Genre8</option> </select 37 ><br /> 38 </div> 39 <div class="form-group col-12"> 40 <p>{{ postForm.error_messages.quality }}</p> 41 <p>商品の状態</p> 42 <br /> 43 <select v-model="postForm.quality"> 44 <option>新品・未使用</option> 45 <option>未使用に近い</option> 46 <option>やや傷や汚れあり</option> 47 <option>傷や汚れあり</option> 48 </select 49 ><br /> 50 </div> 51 <div class="form-group col-12"> 52 <p>{{ postForm.error_messages.post_day }}</p> 53 <p>発送日の目安</p> 54 <br /> 55 <select v-model="postForm.post_day"> 56 <option>1~2日で発送</option> 57 <option>2~3日で発送</option> 58 <option>4~7日で発送</option> 59 </select 60 ><br /> 61 </div> 62 <div class="form-group col-12"> 63 <p>{{ postForm.error_messages.what_you_want }}</p> 64 <p>あなたが交換したい商品</p> 65 <br /> 66 <input 67 type="text" 68 class="form-content" 69 id="what_you_want" 70 v-model="postForm.what_you_want" 71 /> 72 </div> 73 <div class="form-group col-12"> 74 <p>{{ postForm.error_messages.description }}</p> 75 <p>説明文</p> 76 <br /> 77 <textarea 78 type="text" 79 class="form-content" 80 id="description" 81 v-model="postForm.description" 82 ></textarea> 83 </div> 84 <button type="submit" class="btn btn-primary">送信</button> 85 </form> 86 </div> 87</template> 88 89<style scoped> 90.forms { 91 border-radius: 5px; 92 margin: 0 auto; 93 width: 70%; 94 border: 1px black solid; 95 box-shadow: 0 0 2px 2px black; 96} 97.form-content { 98 border: 1px black solid; 99 font-size: 20px; 100} 101table { 102 width: 100%; 103} 104textarea#description { 105 height:50%; 106} 107</style> 108 109<script> 110import axios from "axios"; 111 112export default { 113 data() { 114 return { 115 preview: null, 116 item_id: null, 117 photo: null, 118 postForm: { 119 name: "", 120 genre: "", 121 quality: "", 122 post_day: "", 123 what_you_want: "", 124 description: "", 125 error_messages: [] 126 } 127 }; 128 }, 129 methods: { 130 post() { 131 const response = axios.post("http://localhost:8000/api/auth/items/post", this.postForm, { 132 headers: { 133 Authorization: `Bearer ${this.$store.getters['auth/token']}` 134 } 135 }) 136 console.log(response.data); 137 if (response.status === 'error') { 138 return false 139 } 140 else { 141 this.submit(); 142 } 143 this.$router.push("/"); 144 }, 145 onFileChange(event) { 146 if (event.target.files.length === 0) { 147 this.reset(); 148 return false; 149 } 150 151 if (!event.target.files[0].type.match("image.*")) { 152 this.reset(); 153 return false; 154 } 155 156 const reader = new FileReader(); 157 158 reader.onload = e => { 159 this.preview = e.target.result; 160 }; 161 162 reader.readAsDataURL(event.target.files[0]); 163 this.photo = event.target.files[0]; 164 }, 165 reset() { 166 this.preview = ""; 167 this.photo = null; 168 this.$el.querySelector('input[type="file"]').value = null; 169 }, 170 async submit() { 171 const formData = new FormData(); 172 formData.append("photo", this.photo); 173 axios.post("http://localhost:8000/api/auth/items_photo/post", formData); 174 175 this.reset(); 176 this.$emit("input", false); 177 } 178 } 179}; 180</script>
PHP
1// api.php 2 3Route::group(['middleware' => 'api', 'prefix' => 'auth'], function () { 4 //商品投稿 5 Route::post('/items/post', 'App\Http\Controllers\ItemController@create'); 6 //商品写真投稿 7 Route::post('/items_photo/post', 'App\Http\Controllers\PhotoController@create'); 8});
PHP
1// ItemController.php 2 3public function create(Request $request) { 4 $validator = Validator::make($request->all(), [ 5 'name' => 'required|string|max:255', 6 'description' => 'required|string|max:255', 7 'genre' => 'required', 8 'what_you_want' => 'required|string|max:255', 9 'post_day' => 'required', 10 'quality' => 'required', 11 ]); 12 13 if($validator->fails()) { 14 return response()->json([ 15 'status' => 'error', 16 'messages' => $validator->messages() 17 ]); 18 } else { 19 $item = new Item; 20 $item->fill($request->all()); 21 $item->user_id = auth()->user()->id; 22 $item->save(); 23 24 return response()->json([ 25 'status' => 'success', 26 'data' => $item 27 ], 200); 28 } 29 }
PHP
1 2//PhotoController.php 3public function create(StoreItem $request) { 4 // 投稿写真の拡張子を取得する 5 $extension = $request->photo->extension(); 6 7 $photo = new Photo; 8 9 $user_id = auth()->user()->id; 10 $photo->user_id = $user_id; 11 12 $photo->item_id = Item::where('user_id', $user_id)->max("id"); 13 14 // インスタンス生成時に割り振られたランダムなID値と 15 // 本来の拡張子を組み合わせてファイル名とする 16 $photo->filename = $photo->id . '.' . $extension; 17 18 // S3にファイルを保存する 19 // 第三引数の'public'はファイルを公開状態で保存するため 20 Storage::cloud() 21 ->putFileAs('', $request->photo, $photo->filename, 'public'); 22 23 // データベースエラー時にファイル削除を行うため 24 // トランザクションを利用する 25 DB::beginTransaction(); 26 27 try { 28 Auth::user()->photos()->save($photo); 29 DB::commit(); 30 } catch (\Exception $exception) { 31 DB::rollBack(); 32 // DBとの不整合を避けるためアップロードしたファイルを削除 33 Storage::cloud()->delete($photo->filename); 34 throw $exception; 35 } 36 37 // リソースの新規作成なので 38 // レスポンスコードは201(CREATED)を返却する 39 return response($photo, 201); 40 }
###試したこと
下記のリンクを参考に
php artisan key:generate php artisan jwt:secret php artisan config:cache
を行いましたが何も変化はありませんでした。
補足情報(FW/ツールのバージョンなど)
Ubuntu20.04
PHP 7.4.25
Laravel 8.68.1
Vue.js 3.0.6
あなたの回答
tips
プレビュー