Skip to content

Commit

Permalink
Merge pull request #25 from Team-J9/feature/validate
Browse files Browse the repository at this point in the history
DTO 검증 및 예외 처리
  • Loading branch information
SJ70 authored Jul 19, 2024
2 parents 0ebe11e + 4f772c2 commit 5d070a6
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 3 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/j9/bestmoments/constants/VideoFileTypes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.j9.bestmoments.constants;

import java.util.Arrays;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum VideoFileTypes {
MP4("video/mp4"),
AVI("video/x-msvideo"),
MKV("video/x-matroska"),
WEBM("video/webm"),
MOV("video/quicktime"),
MPEG("video/mpeg"),
FLV("video/x-flv");

private final String contentType;

public static boolean contains(String contentType) {
if (contentType == null) {
return false;
}
String findingContentType = contentType.toLowerCase();
return Arrays.stream(VideoFileTypes.values())
.map(VideoFileTypes::getContentType)
.anyMatch(type -> type.equals(findingContentType));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public ResponseEntity<VideoFindDto> findById(@PathVariable UUID videoId) {

@Operation(summary = "내 동영상 정보 수정")
@PatchMapping("/{videoId}")
public ResponseEntity<VideoFindDto> update(@PathVariable UUID videoId, @RequestBody VideoUpdateDto updateDto) {
public ResponseEntity<VideoFindDto> update(@PathVariable UUID videoId, @RequestBody @Valid VideoUpdateDto updateDto) {
UUID memberId = UUID.fromString(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
Video video = videoService.findByIdAndUploaderId(videoId, memberId);
videoService.update(video, updateDto);
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/j9/bestmoments/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.ArrayList;
Expand Down Expand Up @@ -44,7 +45,7 @@ public class Member implements UserDetails {
private String email;
private MemberRole role;
private String profileImageUrl;

@Lob
private String description = "";

@CreatedDate
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/j9/bestmoments/domain/Video.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
import java.time.LocalDateTime;
import java.util.ArrayList;
Expand Down Expand Up @@ -39,6 +40,7 @@ public class Video {
private UUID id;
private String fileUrl;
private String title;
@Lob
private String description;
@Enumerated(EnumType.STRING)
private VideoStatus videoStatus;
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/j9/bestmoments/dto/request/MemberUpdateDto.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
package com.j9.bestmoments.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public record MemberUpdateDto (

@NotNull
@NotBlank
@Size(max = 20, message = "이름은 20자 이내로 입력해주세요.")
String name,

@NotNull
@NotBlank
@Size(max = 1000, message = "자기소개는 1000자 이내로 입력해주세요.")
String description

) {

}
17 changes: 17 additions & 0 deletions src/main/java/com/j9/bestmoments/dto/request/VideoCreateDto.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
package com.j9.bestmoments.dto.request;

import com.j9.bestmoments.domain.VideoStatus;
import com.j9.bestmoments.validator.VideoTypeCheck;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.springframework.web.multipart.MultipartFile;

public record VideoCreateDto(

@VideoTypeCheck
MultipartFile file,

@NotNull
@NotBlank
@Size(max = 100, message = "제목은 100자 이내로 입력해주세요.")
String title,

@NotNull
@NotBlank
@Size(max = 2000, message = "상세설명은 2000자 이내로 입력해주세요.")
String description,

@NotNull
VideoStatus videoStatus

) {

}
14 changes: 13 additions & 1 deletion src/main/java/com/j9/bestmoments/dto/request/VideoUpdateDto.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
package com.j9.bestmoments.dto.request;

import com.j9.bestmoments.domain.VideoStatus;
import org.springframework.web.multipart.MultipartFile;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public record VideoUpdateDto(

@NotNull
@NotBlank
@Size(max = 100, message = "제목은 100자 이내로 입력해주세요.")
String title,

@NotNull
@NotBlank
@Size(max = 2000, message = "상세설명은 2000자 이내로 입력해주세요.")
String description,

@NotNull
VideoStatus videoStatus
) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.j9.bestmoments.exception;

import jakarta.persistence.EntityNotFoundException;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

@ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<String> handleEntityNotFoundException(EntityNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
}

@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult()
.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(", "));
log.info("handled MethodArgumentNotValidException : {}", msg);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(msg);
}

// 잘못된 형식, 매핑 실패, 필드 누락
@ExceptionHandler(HttpMessageNotReadableException.class)
protected ResponseEntity<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}

}
17 changes: 17 additions & 0 deletions src/main/java/com/j9/bestmoments/validator/VideoTypeCheck.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.j9.bestmoments.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = VideoTypeValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VideoTypeCheck {
String message() default "Invalid Video File Type";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
16 changes: 16 additions & 0 deletions src/main/java/com/j9/bestmoments/validator/VideoTypeValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.j9.bestmoments.validator;

import com.j9.bestmoments.constants.VideoFileTypes;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.constraints.NotNull;
import org.springframework.web.multipart.MultipartFile;

public class VideoTypeValidator implements ConstraintValidator<VideoTypeCheck, MultipartFile> {

@Override
public boolean isValid(@NotNull MultipartFile file, ConstraintValidatorContext context) {
String contentType = file.getContentType();
return VideoFileTypes.contains(contentType);
}
}

0 comments on commit 5d070a6

Please sign in to comment.