So.. I have a method that consumes multipart/form-data. I'm trying to pass an object named User (ignoring some fields), and user avatar file
@ExecuteOn(TaskExecutors.IO)
@Operation(summary = "Endpoint for user registration")
@Post(uri = "/register",consumes = {MediaType.MULTIPART_FORM_DATA},produces = MediaType.APPLICATION_JSON)
@Requires(bean = User.class)
public HttpResponse<DefaultAppResponse> register(
@Part("credential") User credential,
@Part("avatar") @Nullable CompletedFileUpload avatar
){
try {
if(avatar != null) {
Files saved = filesService.save(avatar, dirPattern + avatars);
saved.setOid(transactionalRepository.genOid().orElseThrow());
filesRepository.save(saved);
credential.setAvatarPath(saved);
}
credential.setUserRegDate(new Date(System.currentTimeMillis()));
userRepository.save(credential);
return HttpResponse.ok(
errorService.success()).status(201
);
} catch (Exception e) {
registerLog.error(e.getMessage(), e.getStackTrace());
throw new InternalExceptionResponse("Error: " +e.getMessage() , errorService.error("error: " +e.getMessage()));
}
}
User.class
@Entity
@Table(name = "users", schema = "public")
@Introspected
@JsonView(Default.class)
public class User extends BaseEntity{
@JsonInclude
@JsonProperty("user_name")
@Column(name = "user_name")
@JsonAlias("userName")
private String userName;
@JsonInclude
@JsonProperty("user_birthday")
@Column(name = "user_birthday")
@JsonAlias("userBirthday")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date userBirthday;
@JsonInclude
@JsonFormat(pattern = "yyyy-MM-dd")
@JsonProperty("user_reg_date")
@Column(name = "user_reg_date")
@JsonAlias("userRegDate")
private Date userRegDate;
@JsonInclude
@JsonProperty("user_email")
@Column(name = "user_email")
@JsonAlias("userEmail")
private String userEmail;
@JsonProperty("user_password")
@Column(name = "user_password")
@JsonAlias("userPassword")
@JsonView(WithPassword.class)
private String userPassword;
@ManyToOne
@JoinColumn(name = "avatar_path")
@JsonProperty("avatar_path")
@JsonInclude
@JsonAlias("avatarPath")
private Files avatarPath;
@JsonInclude
@JsonProperty("user_phone_number")
@Column(name = "user_phone_number")
@JsonAlias("userPhoneNumber")
private String userPhoneNumber;
@JsonInclude
@JsonProperty("user_is_confirm")
@Column(name = "user_is_confirm")
@JsonAlias("userIsConfirm")
private Boolean userIsConfirm;
public User(
String oid, String userName,
Date userBirthday, Date userRegDate, String userEmail, String userPassword, Files avatarPath,
String userPhoneNumber, Boolean userIsConfirm
) {
super(oid);
this.userName = userName;
this.userBirthday = userBirthday;
this.userRegDate = userRegDate;
this.userEmail = userEmail;
this.userPassword = userPassword;
this.avatarPath = avatarPath;
this.userPhoneNumber = userPhoneNumber;
this.userIsConfirm = userIsConfirm;
}
public User() {
}
public Files getAvatarPath() {
return avatarPath;
}
public void setAvatarPath(Files avatarPath) {
this.avatarPath = avatarPath;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public Date getUserRegDate() {
return userRegDate;
}
public void setUserRegDate(Date userRegDay) {
this.userRegDate = userRegDay;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public Boolean getUserIsConfirm() {
return userIsConfirm;
}
public void setUserIsConfirm(Boolean userIsConfirm) {
this.userIsConfirm = userIsConfirm;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserPhoneNumber() {
return userPhoneNumber;
}
public void setUserPhoneNumber(String userPhoneNumber) {
this.userPhoneNumber = userPhoneNumber;
}
}
But, When I trying to transfer an object, it simply cannot accept / parse / map it (underline correctly) What do I get in response
Request(Swagger)
Response
{
"message": "Bad Request",
"_links": {
"self": {
"href": "/api/reg/register",
"templated": false
}
},
"_embedded": {
"errors": [
{
"message": "Required Part [credential] not specified",
"path": "/credential"
}
]
}
}
If I remove @Part("credentails")
, then it will contain an entity with fields equivalent to null.
See screenshot
Debug Evaluation
In my research, I saw that type:application/json
is not being passed.But avatar file has type:image/png
. See CURL request(generated from swagger)
curl -X 'POST' \
'http://localhost:8080/api/reg/register' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'credential={
"oid": "null",
"avatar_path": "null"
"user_reg_date": "null",
"user_name": "User FUllName"
"user_birthday": "2001-10-28",
"user_email": "email@gmail.com",
"user_password": "123123",
"user_phone_number": "some-valid-phone-number"
}' \
-F 'avatar=@sticker.png;type=image/png'
Question: what could be the problem?
I had same issue recently.
The solution that I found (works fine):
Request handler:
@Post
@Consumes(value = [MULTIPART_FORM_DATA])
fun createOrganisation(
principal: PrincipalUser,
@Body request: RequestCreateOrg,
@Part image: CompletedFileUpload? = null,
)
My dto:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
data class RequestCreateOrg(
val name: String,
@JsonDeserialize(converter = RequestCreateOrgOwnerConverter::class)
val owner: RequestCreateOrgOwner? = null,
)
With implemented class RequestCreateOrgOwnerConverter : Converter<String, RequestCreateOrgOwner>