I have SpringBoot backend API that handle user registration, it looks like this:
@PostMapping("/signin")
public ResponseEntity < ? > authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getEmail(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List < String > roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority()).collect(Collectors.toList());
return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getId(), userDetails.getEmail(), roles));
}
@PostMapping(value = "/signup", consumes = "multipart/form-data")
public ResponseEntity<?> registerUser(@RequestPart(value = "profilePicture", required = false) MultipartFile profilePicture,
@RequestPart("signUpRequest") SignupRequest signUpRequest) {
try {
if (userRepository.existsByEmail(signUpRequest.getEmail())) {
return ResponseEntity.badRequest().body(new MessageResponse("Error: Email is already in use!"));
}
User user = new User(signUpRequest.getEmail(), encoder.encode(signUpRequest.getPassword()));
Set < String > strRoles = signUpRequest.getRole();
Set < Role > roles = new HashSet < > ();
if (strRoles == null) {
Role userRole = roleRepository.findByName(ERole.ROLE_CLIENT).orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
} else {
strRoles.forEach(role -> {
switch (role) {
case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN).orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(adminRole);
break;
case "mod":
Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR).orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(modRole);
break;
case "freelancer":
Role freelancerRole = roleRepository.findByName(ERole.ROLE_FREELANCER).orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(freelancerRole);
break;
default:
Role clientRole = roleRepository.findByName(ERole.ROLE_CLIENT).orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(clientRole);
}
});
}
user.setRoles(roles);
if (strRoles != null && strRoles.contains("freelancer")) {
FreelancerProfile freelancerProfile = FreelancerProfile.createFromSignupRequestFreelancer(signUpRequest, user);
Set < String > selectedSkills = signUpRequest.getSkills();
if (selectedSkills != null && !selectedSkills.isEmpty()) {
for (String selectedSkillName: selectedSkills) {
Skill skill = skillRepository.findBySkillName(selectedSkillName).orElseGet(() -> {
Skill newSkill = new Skill();
newSkill.setSkillName(selectedSkillName);
return skillRepository.save(newSkill);
});
freelancerProfile.getSkills().add(skill);
}
}
user.setFreelancerProfile(freelancerProfile);
} else {
ClientProfile clientProfile = ClientProfile.createFromSignupRequestClient(signUpRequest, user);
user.setClientProfile(clientProfile);
}
if (profilePicture != null) {
logger.info("Received profile picture: {}", profilePicture.getOriginalFilename());
} else {
logger.info("No profile picture received");
}
if (profilePicture != null && !profilePicture.isEmpty()) {
Photo photo = new Photo();
photo.setData(profilePicture.getBytes());
photo.setUser(user);
user.setPhoto(photo);
}
userRepository.save(user);
return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new MessageResponse("Internal Server Error"));
}
}`
And when I test it in postman like this:
key profilePicture as file and select file
key signUpRequest and value as:
{ "email": "[email protected]", "password": "asdfasdfsaf", "role": [ "client" ], "firstName": "asdfasdf", "lastName": "asdsad", "contactPhone": "asdasdasd", "location": "NY", "skills": [ null ], "portfolio": "", "yearsOfExperience": 0 }
user is saved success, but on my react part I can not save him, Guess I need to handle image process more better in frontend
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Formik, Field, Form, ErrorMessage } from "formik";
import { register } from "../slices/auth";
import validationSchema from "../services/utils/validationSchemas";
import Select from "react-select";
import useApiData from "../services/utils/useApiData";
const Register = () => {
const [successful, setSuccessful] = useState(false);
const [selectedRole, setSelectedRole] = useState("");
const locations = useApiData("http://localhost:8080/api/utils/getAllLocations");
const skills = useApiData("http://localhost:8080/api/utils/getAllSkills");
const { message } = useSelector((state) => state.message);
const dispatch = useDispatch();
const initialValues = {
email: "",
password: "",
role: "",
firstName: "",
lastName: "",
contactPhone: "",
location: "",
portfolio: "",
yearsOfExperience: 0,
};
const handleRegister = (formValue, { resetForm }) => {
const {
email,
password,
role,
firstName,
lastName,
contactPhone,
location,
portfolio,
yearsOfExperience,
skills,
} = formValue;
setSuccessful(false);
const rolesArray = Array.isArray(role) ? role : [role];
const skillsArray = Array.isArray(skills) ? skills : [skills];
const additionalFields = {
firstName,
lastName,
contactPhone,
location,
portfolio,
yearsOfExperience,
};
dispatch(
register({
email,
password,
role: rolesArray,
skills: skillsArray,
...additionalFields,
})
)
.unwrap()
.then(() => {
setSuccessful(true);
resetForm();
})
.catch(() => {
setSuccessful(false);
});
};
const handleRoleChange = (event, setFieldValue) => {
const role = event.target.value;
setSelectedRole(role);
setFieldValue("role", role);
};
const locationOptions = locations.map((location) => ({
value: location,
label: formatLocationName(location),
}));
function formatLocationName(location) {
const words = location.split("_");
const formattedWords = words.map(
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
);
return formattedWords.join(" ");
}
return (
<div className="col-md-12 signup-form">
<div className="card card-container">
<Formik
initialValues={initialValues}
onSubmit={handleRegister}
validationSchema={validationSchema}
>
{({ setFieldValue }) => (
<Form>
<div className="form-group">
<label htmlFor="role">Select Role:</label>
<div>
<button
type="button"
onClick={(e) => handleRoleChange(e, setFieldValue)}
value="client"
className="btn btn-secondary"
>
Client
</button>
<button
type="button"
onClick={(e) => handleRoleChange(e, setFieldValue)}
value="freelancer"
className="btn btn-secondary ml-2"
>
Freelancer
</button>
</div>
</div>
{selectedRole === "freelancer" && (
<>
<div className="form-group">
<label htmlFor="email">Email</label>
<Field name="email" type="email" className="form-control" />
<ErrorMessage
name="email"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Field
name="password"
type="password"
className="form-control"
/>
<ErrorMessage
name="password"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="firstName">First Name</label>
<Field
name="firstName"
type="text"
className="form-control"
/>
<ErrorMessage
name="firstName"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="lastName">Last Name</label>
<Field
name="lastName"
type="text"
className="form-control"
/>
<ErrorMessage
name="lastName"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="contactPhone">Contact Phone</label>
<Field
name="contactPhone"
type="text"
className="form-control"
/>
<ErrorMessage
name="contactPhone"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="location">Location:</label>
<Field name="location">
{({ field, form }) => (
<Select
{...field}
options={locationOptions}
isSearchable
placeholder="Search or select a location"
value={locationOptions.find(
(option) => option.value === field.value
)}
onChange={(selectedOption) =>
form.setFieldValue(
"location",
selectedOption ? selectedOption.value : ""
)
}
/>
)}
</Field>
<ErrorMessage
name="location"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="skills">Select Skills:</label>
<Field
name="skills"
as="select"
multiple
className="form-control"
>
{skills.map((skill) => (
<option
key={skill.id || skill.skillName}
value={skill.skillName}
>
{skill.skillName}
</option>
))}
</Field>
</div>
<div className="form-group">
<label htmlFor="portfolio">Portfolio</label>
<Field
name="portfolio"
type="text"
className="form-control"
/>
</div>
<div className="form-group">
<label htmlFor="yearsOfExperience">
Years of Experience
</label>
<Field
name="yearsOfExperience"
type="number"
className="form-control"
/>
</div>
</>
)}
{selectedRole === "client" && (
<>
<div className="form-group">
<label htmlFor="email">Email</label>
<Field name="email" type="email" className="form-control" />
<ErrorMessage
name="email"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Field
name="password"
type="password"
className="form-control"
/>
<ErrorMessage
name="password"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="firstName">First Name</label>
<Field
name="firstName"
type="text"
className="form-control"
/>
<ErrorMessage
name="firstName"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="lastName">Last Name</label>
<Field
name="lastName"
type="text"
className="form-control"
/>
<ErrorMessage
name="lastName"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="contactPhone">Contact Phone</label>
<Field
name="contactPhone"
type="text"
className="form-control"
/>
<ErrorMessage
name="contactPhone"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="location">Location:</label>
<Field name="location">
{({ field, form }) => (
<Select
{...field}
options={locationOptions}
isSearchable
placeholder="Search or select a location"
value={locationOptions.find(
(option) => option.value === field.value
)}
onChange={(selectedOption) =>
form.setFieldValue(
"location",
selectedOption ? selectedOption.value : ""
)
}
/>
)}
</Field>
<ErrorMessage
name="location"
component="div"
className="alert alert-danger"
/>
</div>
</>
)}
{selectedRole && (
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block">
Sign Up
</button>
</div>
)}
</Form>
)}
</Formik>
</div>
{message && (
<div className="form-group">
<div
className={
successful ? "alert alert-success" : "alert alert-danger"
}
role="alert"
>
{message}
</div>
</div>
)}
</div>
);
};
export default Register;
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
const register = (
email,
password,
role,
firstName,
lastName,
contactPhone,
location,
skills,
portfolio,
yearsOfExperience
) => {
return axios.post(API_URL + "signup", {
email,
password,
role,
firstName,
lastName,
contactPhone,
location,
skills,
portfolio,
yearsOfExperience,
});
};
const login = (email, password) => {
return axios
.post(API_URL + "signin", {
email,
password,
})
.then((response) => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
};
const logout = () => {
localStorage.removeItem("user");
};
const authService = {
register,
login,
logout,
};
export default authService;
How I can now include profile picture upload and save it successfully on fronted part?