- MessageValidateRequestDto에 JsonPropertyName 추가 (snake_case 통일) - MessageValidateRequestDto.Data 타입 string? → object? 변경 - MessageValidationService.ValidateData 파라미터 타입 변경 - Swagger Description 업데이트 (save/validate 엔드포인트) Closes #222
137 lines
4.7 KiB
C#
137 lines
4.7 KiB
C#
using System.Text.Json;
|
|
using SPMS.Application.DTOs.Message;
|
|
using SPMS.Application.Interfaces;
|
|
using SPMS.Domain.Common;
|
|
|
|
namespace SPMS.Application.Services;
|
|
|
|
public class MessageValidationService : IMessageValidationService
|
|
{
|
|
private const int MaxTitleLength = 100;
|
|
private const int MaxBodyLength = 2000;
|
|
private const int MaxDataSizeBytes = 4096;
|
|
private static readonly string[] AllowedLinkTypes = ["deeplink", "web", "none"];
|
|
|
|
public MessageValidationResultDto Validate(MessageValidateRequestDto request)
|
|
{
|
|
var errors = new List<FieldError>();
|
|
|
|
ValidateTitle(request.Title, errors);
|
|
ValidateBody(request.Body, errors);
|
|
ValidateImageUrl(request.ImageUrl, errors);
|
|
ValidateLinkUrl(request.LinkUrl, errors);
|
|
ValidateLinkType(request.LinkType, errors);
|
|
ValidateData(request.Data, errors);
|
|
|
|
return new MessageValidationResultDto
|
|
{
|
|
IsValid = errors.Count == 0,
|
|
Errors = errors
|
|
};
|
|
}
|
|
|
|
private static void ValidateTitle(string title, List<FieldError> errors)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(title))
|
|
{
|
|
errors.Add(new FieldError { Field = "title", Message = "제목은 필수입니다." });
|
|
return;
|
|
}
|
|
|
|
if (title.Length > MaxTitleLength)
|
|
errors.Add(new FieldError { Field = "title", Message = $"제목은 {MaxTitleLength}자를 초과할 수 없습니다." });
|
|
}
|
|
|
|
private static void ValidateBody(string body, List<FieldError> errors)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(body))
|
|
{
|
|
errors.Add(new FieldError { Field = "body", Message = "본문은 필수입니다." });
|
|
return;
|
|
}
|
|
|
|
if (body.Length > MaxBodyLength)
|
|
errors.Add(new FieldError { Field = "body", Message = $"본문은 {MaxBodyLength}자를 초과할 수 없습니다." });
|
|
}
|
|
|
|
private static void ValidateImageUrl(string? imageUrl, List<FieldError> errors)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(imageUrl))
|
|
return;
|
|
|
|
if (!Uri.TryCreate(imageUrl, UriKind.Absolute, out var uri) ||
|
|
(uri.Scheme != "http" && uri.Scheme != "https"))
|
|
errors.Add(new FieldError { Field = "image_url", Message = "유효한 URL 형식이 아닙니다." });
|
|
}
|
|
|
|
private static void ValidateLinkUrl(string? linkUrl, List<FieldError> errors)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(linkUrl))
|
|
return;
|
|
|
|
if (!Uri.TryCreate(linkUrl, UriKind.Absolute, out _))
|
|
errors.Add(new FieldError { Field = "link_url", Message = "유효한 URL 형식이 아닙니다." });
|
|
}
|
|
|
|
private static void ValidateLinkType(string? linkType, List<FieldError> errors)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(linkType))
|
|
return;
|
|
|
|
if (!AllowedLinkTypes.Contains(linkType.ToLowerInvariant()))
|
|
errors.Add(new FieldError { Field = "link_type", Message = "link_type은 deeplink, web, none 중 하나여야 합니다." });
|
|
}
|
|
|
|
private static void ValidateData(object? data, List<FieldError> errors)
|
|
{
|
|
if (data is null)
|
|
return;
|
|
|
|
// object? → JSON 문자열로 변환
|
|
string jsonString;
|
|
if (data is JsonElement jsonElement)
|
|
{
|
|
if (jsonElement.ValueKind == JsonValueKind.Null || jsonElement.ValueKind == JsonValueKind.Undefined)
|
|
return;
|
|
|
|
if (jsonElement.ValueKind != JsonValueKind.Object)
|
|
{
|
|
errors.Add(new FieldError { Field = "data", Message = "data는 JSON 객체여야 합니다." });
|
|
return;
|
|
}
|
|
|
|
jsonString = jsonElement.GetRawText();
|
|
}
|
|
else if (data is string strData)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(strData))
|
|
return;
|
|
|
|
try
|
|
{
|
|
using var doc = JsonDocument.Parse(strData);
|
|
if (doc.RootElement.ValueKind != JsonValueKind.Object)
|
|
{
|
|
errors.Add(new FieldError { Field = "data", Message = "data는 JSON 객체여야 합니다." });
|
|
return;
|
|
}
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
errors.Add(new FieldError { Field = "data", Message = "유효한 JSON 형식이 아닙니다." });
|
|
return;
|
|
}
|
|
|
|
jsonString = strData;
|
|
}
|
|
else
|
|
{
|
|
// 기타 타입은 직렬화하여 검증
|
|
jsonString = JsonSerializer.Serialize(data);
|
|
}
|
|
|
|
if (System.Text.Encoding.UTF8.GetByteCount(jsonString) > MaxDataSizeBytes)
|
|
errors.Add(new FieldError { Field = "data", Message = $"data는 {MaxDataSizeBytes / 1024}KB를 초과할 수 없습니다." });
|
|
}
|
|
}
|