vue how to post list with files to spring - spring-boot

Srping boot 2.x and vue 2.x.
My UI looks like(not the complete picture for simplicity) :
my java bean is:
public class Attachment {
private Integer eaId;
private Integer eId;
private String description;
private MultipartFile file;
//setters and getters
}
public class Booking {
private Integer id;
private String eNo;
private List<Attachment> attachmentList;
//some other fields
}
Each attachment item contains file and file description.
My front-end is:
<tr v-for=" (a, index) in eb.attachmentList" v-bind:key="index">
<td><input type="text" v-model="a.description" /></td>
<td><input type="file" v-on:change="selectFile(index, $event)" /></td>
<td><i class="far fa-trash-alt fa-lg" v-on:click="delAttachment(index)" style="color: red"></i>
</td>
</tr>
my spring controller is:
#PostMapping("/upload")
public Booking upload(#ModelAttribute Booking b) {
//....
}
the vue code is:
var vm = new Vue({
el: '#test',
data() {
return {
eb: {
'attachmentList': []
}
}
},
methods: {
selectFile(index, e) {
this.eb.attachmentList[index].file = e.target.files[0];
},
saveTest() {
var formData = new FormData();
//how to populate formData ?
axios.post("/tf/upload",
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function() {
//....
})
.catch(function(error) {
//...
});
}
}
});
I want to post this form data to spring controller with axios, that is convert form data to java bean Booking and the file list of form to List<Attachment> attachmentList of Booking.
It seems that I have to use FormData, right?
My problems is:
How to populate the FormData in my case?
If just upload file, I can use formData.append('file', file) then
post it with header multipart/form-data directly.
But now there are a few file list and each file associated with its description. I don't know how to do such that I can get the form data in my spring controller.

In Vue side:
First, you need to create a new object of type FormData and append to it a reference for the input element that you are appending the file to.
HTML:
<input type="file" #change="processFile($event)">
Vue:
methods: {
processFile(event) {
this.myFile = event.target.files[0]
}
}
This will do myFile variable hold a reference to the appended file.
When the time to upload comes:
const formData = new FormData();
formData.append('attribute-name-spring-expects', this.myFile, this.myFile.name);
// other attributes
Then, send this form data with axios like this:
const requestConfig = {
headers: { 'content-type': 'multipart/form-data' }
}
axios.post(url, formData, requestConfig);

Related

How to send by form an image in http request, and with what headers?

I searched on google and nothing...
i have a form in my view on vueJS :
<form #submit.prevent="avatar()" method="POST" enctype="multipart/form-data">
<input type="file" name="profilePicture" id="profilepicture" >
<button type="submit">Valider mon avatar?</button>
</form>
The user send an image.
My question is,
i want to send the image (sending by user with the form) in a function, and this function send the image to Http Request in headers ...
the api request begin with :
app.post('/myupload/:iduser', async (req, res) => {
try{
var { iduser } = req.params ;
const { image } = req.file ;
[...]
my function in my view on vuejs is actually :
async function avatar() {
console.log(document.getElementById("profilepicture").value);
// for having the image sending src
let response = await fetch(`http://localhost:196/myupload/${MyTokenStore.myid}`, {
method: "POST",
headers: {
"Content-Type": "multipart/form-data",
},
body: JSON.stringify(document.getElementById("profilepicture").value)
})
.then((response) => response.json())
.catch((error) => {
console.log("Failed", error)
});
if(response.error){
alert(response.message);
return
}
}
But the only parameter i make in request is the string of the src image, and the error servor is :
TypeError: Cannot destructure property 'image' of 'req.files' as it is undefined.
Please, i need help, the request run when I go directly to the url of the request (but the browser directly displays the server response):
<form :action="`http://localhost:196/myupload/${MyTokenStore.myid}`"
method="POST" enctype="multipart/form-data">
<input type="file" name="image"/>
<button type="submit">Valider mon avatar</button>
</form>
but I fail to put it in a function...
thank you.
To send an image from Vue doing a request you have to use a FormData object. For example:
/**
* #description - register user
*/
const register = async () => {
try {
const fd = new FormData();
Object.keys(user.value).forEach((key) => {
fd.append(key, user.value[key]);
});
const res = await axios.post('/register', fd, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
toast.success(res.data);
} catch (err) {
console.log(err);
toast.error(err.response.data);
}
};
In the previous code, in register I'm using a FormData to send the user object to the backend. Previously, you should set it. I made it like the following example:
const setProfileImage = (e) => {
image_preview.value = URL.createObjectURL(e.target.files[0]);
document.getElementById('image_preview').style.height = '150px';
document.getElementById('image_preview').style.width = '150px';
user.value.profile_image = e.target.files[0]; // this is important for you.
user.value.image = e.target.files[0].name;
};
setProfileImage is receiving an event from an Input file. If you see the comment I write in the code, user.value.profile_image = e.target.files[0] is the entire file. The rest is to display the image and send the name to store into the database.
<div class="mb-3">
<label
for="profile_image"
class="bg-primary text-white rounded-3 text-center w-100 p-2 profile_image"
>
Seleccione su foto de perfil <i class="ps-1 bi bi-card-image"></i>
</label>
<input
type="file"
class="form-control d-none"
id="profile_image"
placeholder="Adjunte su foto de perfil"
#change="setProfileImage"
/>
</div>
This is the Input file what I was talking. The event is an #change to catch correctly the file.
Hope it works for you.

Front End File Upload using Vue and Winter Cms

I'm trying to upload images from a Vue frontend via Illuminate/Http/Request to WinterCMS.
Vue finds the file and i can console.log the File object, but I'm unsure how to get this over the api. for example I've tried
public function saveImage(Request $req){
$images = $req->files('images');
}
which doesn't work, nor does
public function saveImage(Request $req){
$images = $req['images'];
}
I'm using a controller to handle my routes eg:
Route::post('/saveImage', 'Author\Project\Controllers\ProductControl#saveImage');
I've added an attachOne relation to the plugin as usual and my form has enctype="multipart/form-data"
I've had this problem before and got around it by converting images to base64 but this project will have quite a few images and I don't want to go down that route again.
Any suggestions greatly appreciated
You can send images as regular post and use regular $request->file('images') method in your Laravel controller.
You can use Javascript FormData object. For example;
<div>
<input type="file" #change="handleImages" multiple>
<button #click="uploadImages">Upload!</button>
</div>
data: () => ({
images: []
}),
methods: {
handleImages (event) {
this.images = event.target.files
},
uploadImages () {
const formData = new FormData();
for (const i of Object.keys(this.images)) {
formData.append('images', this.images[i])
}
axios.post('/saveImage', formData, {
}).then((res) => {
console.log(res)
})
}
}

Required String parameter 'newPassword' is not present

SOLVED
We've solved it. The form that the backend requires data was param, so the form that the vue sends was changed to param, not data!
I'm making a password change page.
Meanwhile, I got an error that there was no old password, new password data but I definitely transferred the data from Vue through axios.
There were answers to 'required=false', so I tried oldpassword, but here's the result.
However, we took 'required= true' because we believe newpassword, oldpassword is essential data for this approach.
When using post-man or swagger, the password is changed signaling 200 normally.
I thought there might be a problem with 'patch', so I tried 'put', but the error didn't change.
frontend
<div class="input-with-label">
<input v-model="newpassword" id="newpassword" placeholder="새 비밀번호를 입력하세요." :type="passwordType" />
<label for="newpassword">새 비밀번호</label>
<div class="error-text" v-if="error.newpassword">{{error.newpassword}}</div>
</div>
<form #submit.prevent="changepassword" #submit="checkForm">
<div v-if="activeButton() && !isSubmit">
<!-- <button #click="PopUpEmailModal" class="btn-bottom" >가입하기</button> -->
<button class="btn-bottom">변경하기</button>
</div>
<div v-else>
<button class="btn-bottom disabled" >변경하기</button>
</div>
</form>
import axios from 'axios'
export default {
name:'ChangePassword',
data: ()=> {
return{
email:"",
oldpassword:"",
newpassword:"",
isSubmit: true,
error: {
newpassword: false,
},
passwordType: "password",
passwordSchema: new PV(),
}
},
created() {
this.passwordSchema
.is()
.min(8)
.is()
.max(100)
.has()
.digits()
.has()
.letters();
},
watch: {
newpassword: function(v) {
this.checkForm();
},
},
methods:{
changepassword(){
axios({
url:'http://127.0.0.1:8080/account/changePassword',
method:'patch',
data:{
email: this.email,
oldPassword: this.oldpassword,
newPassword: this.newpassword,
},
})
.then(res=>{
console.log(res)
this.$router.push({ name:'Login' })
})
.catch(err=>{
console.log(typeof this.oldpassword)
console.log(this.oldpassword)
console.log(this.newpassword)
console.log(this.email)
console.log(err)
})
},
}
backend
#PatchMapping("/account/changePassword")
#ApiOperation(value = "비밀번호변경")
public Object changePassword(#RequestParam(required = false) final String oldPassword,
#RequestParam(required = true) final String newPassword,
#RequestParam(required = true) final String email){
Optional<User> userOpt = userDao.findByEmail(email);
if(!passwordEncoder.matches(oldPassword, userOpt.get().getPassword())){
throw new IllegalArgumentException("잘못된 비밀번호입니다.");
}
User user = new User(userOpt.get().getUid(), userOpt.get().getNickname(), email,
passwordEncoder.encode(newPassword), userOpt.get().getIntroduction(), userOpt.get().getThumbnail(), userOpt.get().getRoles());
userDao.save(user);
final BasicResponse result = new BasicResponse();
result.status = true;
result.data = "success";
ResponseEntity response = null;
response = new ResponseEntity<>("OK", HttpStatus.OK);
return response;
}
I believe you are using #RequestParam annotations but you are not sending any params in the URL from the frontend, hence the 400 error.
Since you are using Patch/Put I would suggest you change your changePassword function to take a dto. And since you are already sending data in the body from frontend so no change needed there.
For example
public Object changePassword(#RequestBody #Valid ChangePasswordDto changePasswordDto){
// your logic
}
Then create a ChangePasswordDto class with #Required on properties that are required.
public class ChangePasswordDto{
private String email;
private String newPassword;
private String oldPassword;
#Required
public void setEmail(String email) {
this.email= email;
}
#Required
public void setOldPassword(String oldPassword) {
this.oldPassword= oldPassword;
}
#Required
public void setNewPassword(String newPassword) {
this.newPassword= newPassword;
}
// corresponding getters
}

Getting dynamic field data using vue.js in laravel

I am creating multiple dynamic fields and the same is posted to the laravel controller methods but i'm able to get the simple fields data (fields that are not dynamically generated) but the array fields that are dynamically generated those values i'm unable to get in the controller method.
<input :name="'students['+studentdetails.id+']['+studentdetails.class+']'" type="text" class="form-control"/>
<input :name="'students['+studentdetails.id+']['+studentdetails.class+']'" type="text" class="form-control"/>
In my controller method when i'm getting it:
printr($request->attributes);
export default {
data() {
...
return {
form: new Form({
title: "",
attributes: [],
}),
};
addStudent() {
const header = {
Authorization: "Bearer " + this.token,
};
this.form
.post(APP_URL + `/api/addStudent`, { headers: header })
.then((response) => {
if (response.status == 200) {
location.replace(APP_URL + "/success");
}
});
},
};
there is not value.
You are now creating [] and students as strings, and they should not be. Remove the string quotes completely, as you are using variables only:
:name="students[studentdetails.id][studentdetails.class]"

I m using Post Method but Apache keep saying GET not supported, Why?

I m making a small web/application to exercise what I m learning from Spring boot and ajax.
I have a web page where I have a form that have two date inputs 'start date' and 'end date' I wanted to send the user input to a RestController and to get a response that I should show in a empty div
here is what I have done till now
My Form :
<div class="w3-container w3-padding-64" id="reserver_salle">
<h1>Resrvation de salle</h1><br>
<form id="reserver_salle_form" >
<p><input class="w3-input w3-padding-16" id="dateDebut" type="date" required placeholder="Date début"></p>
<p><input class="w3-input w3-padding-16" id="dateFin" type="date" required placeholder="Date fin"></p>
<p><button id="searchSalle" type="submit" class="w3-button w3-light-grey w3-section" >[[#{homePage.envoyer}]]</button></p>
</form>
</div>
<div id="salleList" class="w3-container">
</div>
<script type="text/javascript" th:src="#{~/cdnjs.cloudflare.com_ajax_libs_jquery_2.1.3_jquery.min.js}">
</script>
<script type="text/javascript" th:src="#{~/js/start_page.js}"></script>
Now I will show you my ajax script
$(document).ready(function () {
//alert("is loading");
$("#searchSalle").submit(function (event) {
//stop submit the form, we will post it manually.
event.preventDefault();
fire_serach_salle();
});
$("#send_mail").submit(function (event) {
//stop submit the form, we will post it manually.
event.preventDefault();
fire_send_mail();
});
});
function fire_serach_salle(){
var searchReserv = {};
searchReserv["dateDebutAnnee"] = $("#dateDebut").val().split('-')[0];
searchReserv["dateDebutMois"] = $("#dateDebut").val().split('-')[1];
searchReserv["dateDebutJour"] = $("#dateDebut").val().split('-')[2];
searchReserv["dateFinAnnee"] = $("#dateFin").val().split('-')[0];
searchReserv["dateFinMois"] = $("#dateFin").val().split('-')[1];
searchReserv["dateFinJour"] = $("#dateFin").val().split('-')[2];
$("#searchSalle").prop("disabled",true);
console.log(searchReserv);
$.ajax({
type: "POST",
contentType: "application/json",
url: "/homePage/reservation/search",
data: JSON.stringify(searchReserv),
dataType: 'json',
cache: false,
timeout: 900000,
success: function (result) {
if (result.answer === "DONE") {
/*<![CDATA[*/
var html ='<h2>Liste des salles </h2>';
html+='<ul class="w3-ul w3-hoverable">';
for(var i = 0 ;i<result.data.length;i++){
html += ' <li class="w3-display-container ">'+result.data[i].titre+'-'+result.data[i].id+'<span onclick="reserve('+result.data[i].id+')" class="w3-button w3-transparent w3-display-right w3-hover-green">reserver</span></li>';
}
html+='</ul>';
/*]]>*/
$("#salleList").html(html);
$("#searchSalle").prop("disabled",false);
}
}
});
}
now I will show my controller
#RestController
#PropertySource(ignoreResourceNotFound = true, value = "classpath:messages.properties")
public class HomePageController {
#RequestMapping(value = "/homePage/reservation/search", method = RequestMethod.POST)
public #ResponseBody
AjaxResponse doSearch(#RequestBody SearchReserv searchReserv, Model model) {
try {
System.out.println(searchReserv);
ArrayList<ReservationSearchResponse> response = new ArrayList<>();
response.add(new ReservationSearchResponse(Long.valueOf(0), "hello 0"));
response.add(new ReservationSearchResponse(Long.valueOf(1), "hello 1"));
return new AjaxResponse("DONE", response);
} catch (Exception e) {
e.printStackTrace();
return new AjaxResponse("BAD", null);
}
}
Finally my two classes AjaxResponse and SearcheReserv
public class AjaxResponse {
private String answer;
private Object data;
//geters setters and contructors
}
import org.springframework.stereotype.Component;
/**
*
* #author taleb
*/
#Component
public class SearchReserv {
private int dateDebutAnnee;
private int dateDebutMois;
private int dateDebutJour;
private int dateFinAnnee;
private int dateFinMois;
private int dateFinJour;
//geters setters and contructors
}
What I can't understand that I m using this way to implement a send mail using spring mail and it working fine
but in this one I still getting
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
there is no GET in my code
I m using latest Spring Boot 2.0.0.M7
I have no configuration handmade on the apache server
Try using annotation #RequestMapping(method=GET) and share the result after that. It should resolve this issue.
After hours of hard the issue was stupid, When attaching Ajax request we do it to the form Id not the Button
I fixed this by updating my ajax script from
$("#searchSalle").submit(function (event)
to
$("#reserver_salle_form").submit(function (event)
and it is working like a charm

Resources