정수 배열을 ASP.NET 웹 API로 전달하시겠습니까?
정수 배열을 전달해야 하는 ASP.NET 웹 API(버전 4) REST 서비스가 있습니다.
다음은 내 작업 방법입니다.
public IEnumerable<Category> GetCategories(int[] categoryIds){
// code to retrieve categories from database
}
그리고 제가 시도한 URL은 다음과 같습니다.
/Categories?categoryids=1,2,3,4
다음과 같이 매개 변수 앞에 추가하면 됩니다.
GetCategories([FromUri] int[] categoryIds)
요청 전송:
/Categories?categoryids=1&categoryids=2&categoryids=3
Filip W가 지적했듯이, 다음과 같은 사용자 정의 모델 바인더에 의존해야 할 수도 있습니다(실제 유형의 매개변수에 바인딩하도록 수정됨).
public IEnumerable<Category> GetCategories([ModelBinder(typeof(CommaDelimitedArrayModelBinder))]long[] categoryIds)
{
// do your thing
}
public class CommaDelimitedArrayModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var key = bindingContext.ModelName;
var val = bindingContext.ValueProvider.GetValue(key);
if (val != null)
{
var s = val.AttemptedValue;
if (s != null)
{
var elementType = bindingContext.ModelType.GetElementType();
var converter = TypeDescriptor.GetConverter(elementType);
var values = Array.ConvertAll(s.Split(new[] { ","},StringSplitOptions.RemoveEmptyEntries),
x => { return converter.ConvertFromString(x != null ? x.Trim() : x); });
var typedValues = Array.CreateInstance(elementType, values.Length);
values.CopyTo(typedValues, 0);
bindingContext.Model = typedValues;
}
else
{
// change this line to null if you prefer nulls to empty arrays
bindingContext.Model = Array.CreateInstance(bindingContext.ModelType.GetElementType(), 0);
}
return true;
}
return false;
}
}
그런 다음 이렇게 말할 수 있습니다.
/Categories?categoryids=1,2,3,4는 ASP.NET Web API를 .categoryIdsvmdk
저는 최근에 이 요구 사항을 접했고, 다음을 구현하기로 결정했습니다.ActionFilter이 일을 처리하기 위해.
public class ArrayInputAttribute : ActionFilterAttribute
{
private readonly string _parameterName;
public ArrayInputAttribute(string parameterName)
{
_parameterName = parameterName;
Separator = ',';
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionArguments.ContainsKey(_parameterName))
{
string parameters = string.Empty;
if (actionContext.ControllerContext.RouteData.Values.ContainsKey(_parameterName))
parameters = (string) actionContext.ControllerContext.RouteData.Values[_parameterName];
else if (actionContext.ControllerContext.Request.RequestUri.ParseQueryString()[_parameterName] != null)
parameters = actionContext.ControllerContext.Request.RequestUri.ParseQueryString()[_parameterName];
actionContext.ActionArguments[_parameterName] = parameters.Split(Separator).Select(int.Parse).ToArray();
}
}
public char Separator { get; set; }
}
그렇게 적용합니다('id'가 아닌 'id'를 사용했습니다. 즉, 경로에 지정되어 있기 때문입니다.):
[ArrayInput("id", Separator = ';')]
public IEnumerable<Measure> Get(int[] id)
{
return id.Select(i => GetData(i));
}
공개 URL은 다음과 같습니다.
/api/Data/1;2;3;4
구체적인 요구 사항을 충족하기 위해 이 문제를 재요인화해야 할 수도 있습니다.
누군가가 필요한 경우 - 다음을 통해 동일하거나 유사한 것(예: 삭제)을 달성합니다.POST에 FromUri,사용하다FromBody 측 변수는 "(JS/jQuery)"입니다.$.param({ '': categoryids }, true)
c#:
public IHttpActionResult Remove([FromBody] int[] categoryIds)
jQuery:
$.ajax({
type: 'POST',
data: $.param({ '': categoryids }, true),
url: url,
//...
});
에 있는 것.$.param({ '': categoryids }, true)에 .net과 같은 이 포함될 입니다.=1&=2&=3매개 변수 이름 및 대괄호를 사용하지 않습니다.
어레이 매개 변수를 웹 API로 쉽게 전송하는 방법
API
public IEnumerable<Category> GetCategories([FromUri]int[] categoryIds){
// code to retrieve categories from database
}
Jquery : JSON 개체를 요청 매개 변수로 전송
$.get('api/categories/GetCategories',{categoryIds:[1,2,3,4]}).done(function(response){
console.log(response);
//success response
});
URL은 ""과 생성됩니다.../api/categories/GetCategories?categoryIds=1&categoryIds=2&categoryIds=3&categoryIds=4
이 코드를 사용하여 웹에서 JSON을 반환하기 위해 쉼표로 구분된 값 / 값 배열을 사용할 수 있습니다.API
public class CategoryController : ApiController
{
public List<Category> Get(String categoryIDs)
{
List<Category> categoryRepo = new List<Category>();
String[] idRepo = categoryIDs.Split(',');
foreach (var id in idRepo)
{
categoryRepo.Add(new Category()
{
CategoryID = id,
CategoryName = String.Format("Category_{0}", id)
});
}
return categoryRepo;
}
}
public class Category
{
public String CategoryID { get; set; }
public String CategoryName { get; set; }
}
출력:
[
{"CategoryID":"4","CategoryName":"Category_4"},
{"CategoryID":"5","CategoryName":"Category_5"},
{"CategoryID":"3","CategoryName":"Category_3"}
]
ASP.NET Core 2.0 솔루션(스웨거 지원)
입력
DELETE /api/items/1,2
DELETE /api/items/1
코드
공급자 작성(MVC가 사용할 바인더를 확인하는 방법)
public class CustomBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(int[]) || context.Metadata.ModelType == typeof(List<int>))
{
return new BinderTypeModelBinder(typeof(CommaDelimitedArrayParameterBinder));
}
return null;
}
}
실제 바인더 작성(요청, 수행, 모델, 유형 등에 대한 모든 종류의 정보에 액세스)
public class CommaDelimitedArrayParameterBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var value = bindingContext.ActionContext.RouteData.Values[bindingContext.FieldName] as string;
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
var ints = value?.Split(',').Select(int.Parse).ToArray();
bindingContext.Result = ModelBindingResult.Success(ints);
if(bindingContext.ModelType == typeof(List<int>))
{
bindingContext.Result = ModelBindingResult.Success(ints.ToList());
}
return Task.CompletedTask;
}
}
MVC에 등록합니다.
services.AddMvc(options =>
{
// add custom binder to beginning of collection
options.ModelBinderProviders.Insert(0, new CustomBinderProvider());
});
Swagger용으로 잘 문서화된 컨트롤러를 사용
/// <summary>
/// Deletes a list of items.
/// </summary>
/// <param name="itemIds">The list of unique identifiers for the items.</param>
/// <returns>The deleted item.</returns>
/// <response code="201">The item was successfully deleted.</response>
/// <response code="400">The item is invalid.</response>
[HttpDelete("{itemIds}", Name = ItemControllerRoute.DeleteItems)]
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(void), StatusCodes.Status404NotFound)]
public async Task Delete(List<int> itemIds)
=> await _itemAppService.RemoveRangeAsync(itemIds);
편집: Microsoft는 이 접근 방식보다 이러한 작업에 유형 변환기를 사용할 것을 권장합니다.따라서 아래 포스터 조언에 따라 SchemaFilter로 사용자 정의 유형을 문서화합니다.
사용자 정의 모델 바인더를 사용하는 대신 유형 변환기와 함께 사용자 정의 유형을 사용할 수도 있습니다.
[TypeConverter(typeof(StrListConverter))]
public class StrList : List<string>
{
public StrList(IEnumerable<string> collection) : base(collection) {}
}
public class StrListConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value == null)
return null;
if (value is string s)
{
if (string.IsNullOrEmpty(s))
return null;
return new StrList(s.Split(','));
}
return base.ConvertFrom(context, culture, value);
}
}
장점은 웹 API 메서드의 매개 변수를 매우 단순하게 만든다는 것입니다.[FromUri]를 지정할 필요도 없습니다.
public IEnumerable<Category> GetCategories(StrList categoryIds) {
// code to retrieve categories from database
}
이지만, 이예는문목대것한다이만같음할이다있수니습과지록제를 할 수 있습니다.categoryIds.Select(int.Parse)또는 단순히 IntList를 작성합니다.
저는 원래 몇 년 동안 @Mrchief라는 솔루션을 사용했습니다(훌륭하게 작동합니다).그러나 API 문서화를 위해 프로젝트에 Swagger를 추가했을 때는 엔드포인트가 나타나지 않았습니다.
시간이 좀 걸렸지만, 제가 생각해낸 것은 이것입니다.Swagger와 함께 작동하며 API 메서드 서명이 더 깨끗해 보입니다.
결국 다음을 수행할 수 있습니다.
// GET: /api/values/1,2,3,4
[Route("api/values/{ids}")]
public IHttpActionResult GetIds(int[] ids)
{
return Ok(ids);
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Allow WebApi to Use a Custom Parameter Binding
config.ParameterBindingRules.Add(descriptor => descriptor.ParameterType == typeof(int[]) && descriptor.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Get)
? new CommaDelimitedArrayParameterBinder(descriptor)
: null);
// Allow ApiExplorer to understand this type (Swagger uses ApiExplorer under the hood)
TypeDescriptor.AddAttributes(typeof(int[]), new TypeConverterAttribute(typeof(StringToIntArrayConverter)));
// Any existing Code ..
}
}
새 클래스 만들기:CommaDelimitedArrayParameterBinder.cs
public class CommaDelimitedArrayParameterBinder : HttpParameterBinding, IValueProviderParameterBinding
{
public CommaDelimitedArrayParameterBinder(HttpParameterDescriptor desc)
: base(desc)
{
}
/// <summary>
/// Handles Binding (Converts a comma delimited string into an array of integers)
/// </summary>
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken)
{
var queryString = actionContext.ControllerContext.RouteData.Values[Descriptor.ParameterName] as string;
var ints = queryString?.Split(',').Select(int.Parse).ToArray();
SetValue(actionContext, ints);
return Task.CompletedTask;
}
public IEnumerable<ValueProviderFactory> ValueProviderFactories { get; } = new[] { new QueryStringValueProviderFactory() };
}
새 클래스 만들기:StringToIntArrayConverter.cs
public class StringToIntArrayConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
}
주의:
- https://stackoverflow.com/a/47123965/862011 은 나를 올바른 방향으로 가리켰습니다.
- Swagger가 [Route] 특성을 사용할 때 쉼표로 구분된 끝점만 선택하지 못했습니다.
public class ArrayInputAttribute : ActionFilterAttribute
{
private readonly string[] _ParameterNames;
/// <summary>
///
/// </summary>
public string Separator { get; set; }
/// <summary>
/// cons
/// </summary>
/// <param name="parameterName"></param>
public ArrayInputAttribute(params string[] parameterName)
{
_ParameterNames = parameterName;
Separator = ",";
}
/// <summary>
///
/// </summary>
public void ProcessArrayInput(HttpActionContext actionContext, string parameterName)
{
if (actionContext.ActionArguments.ContainsKey(parameterName))
{
var parameterDescriptor = actionContext.ActionDescriptor.GetParameters().FirstOrDefault(p => p.ParameterName == parameterName);
if (parameterDescriptor != null && parameterDescriptor.ParameterType.IsArray)
{
var type = parameterDescriptor.ParameterType.GetElementType();
var parameters = String.Empty;
if (actionContext.ControllerContext.RouteData.Values.ContainsKey(parameterName))
{
parameters = (string)actionContext.ControllerContext.RouteData.Values[parameterName];
}
else
{
var queryString = actionContext.ControllerContext.Request.RequestUri.ParseQueryString();
if (queryString[parameterName] != null)
{
parameters = queryString[parameterName];
}
}
var values = parameters.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries)
.Select(TypeDescriptor.GetConverter(type).ConvertFromString).ToArray();
var typedValues = Array.CreateInstance(type, values.Length);
values.CopyTo(typedValues, 0);
actionContext.ActionArguments[parameterName] = typedValues;
}
}
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
_ParameterNames.ForEach(parameterName => ProcessArrayInput(actionContext, parameterName));
}
}
용도:
[HttpDelete]
[ArrayInput("tagIDs")]
[Route("api/v1/files/{fileID}/tags/{tagIDs}")]
public HttpResponseMessage RemoveFileTags(Guid fileID, Guid[] tagIDs)
{
_FileRepository.RemoveFileTags(fileID, tagIDs);
return Request.CreateResponse(HttpStatusCode.OK);
}
요청 uri
http://localhost/api/v1/files/2a9937c7-8201-59b7-bc8d-11a9178895d0/tags/BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63,BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63
정수 목록/열을 표시하려면 쉼표(,)로 구분된 문자열 목록을 수락하고 정수 목록으로 변환하는 것이 가장 쉬운 방법입니다.[FromUri] 속성을 언급하는 것을 잊지 마십시오.당신의 URL은 다음과 같습니다.
...?ID=71&accountID = 1,2,3,289,56
public HttpResponseMessage test([FromUri]int ID, [FromUri]string accountID)
{
List<int> accountIdList = new List<int>();
string[] arrAccountId = accountId.Split(new char[] { ',' });
for (var i = 0; i < arrAccountId.Length; i++)
{
try
{
accountIdList.Add(Int32.Parse(arrAccountId[i]));
}
catch (Exception)
{
}
}
}
쉼표로 구분된 값(원시, 소수, 부동 소수, 문자열만)을 해당 배열로 변환하는 사용자 정의 모델 바인더를 만들었습니다.
public class CommaSeparatedToArrayBinder<T> : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
Type type = typeof(T);
if (type.IsPrimitive || type == typeof(Decimal) || type == typeof(String) || type == typeof(float))
{
ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null) return false;
string key = val.RawValue as string;
if (key == null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type"); return false; }
string[] values = key.Split(',');
IEnumerable<T> result = this.ConvertToDesiredList(values).ToArray();
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Only primitive, decimal, string and float data types are allowed...");
return false;
}
private IEnumerable<T> ConvertToDesiredArray(string[] values)
{
foreach (string value in values)
{
var val = (T)Convert.ChangeType(value, typeof(T));
yield return val;
}
}
}
컨트롤러에서 사용하는 방법:
public IHttpActionResult Get([ModelBinder(BinderType = typeof(CommaSeparatedToArrayBinder<int>))] int[] ids)
{
return Ok(ids);
}
저는 이 문제를 이렇게 다루었습니다.
나는 정수 리스트를 데이터로 보내기 위해 api에 포스트 메시지를 사용했습니다.
그리고 나서 나는 데이터를 ienumerous로 반환했습니다.
송신 코드는 다음과 같습니다.
public override IEnumerable<Contact> Fill(IEnumerable<int> ids)
{
IEnumerable<Contact> result = null;
if (ids!=null&&ids.Count()>0)
{
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:49520/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
String _endPoint = "api/" + typeof(Contact).Name + "/ListArray";
HttpResponseMessage response = client.PostAsJsonAsync<IEnumerable<int>>(_endPoint, ids).Result;
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
result = JsonConvert.DeserializeObject<IEnumerable<Contact>>(response.Content.ReadAsStringAsync().Result);
}
}
}
catch (Exception)
{
}
}
return result;
}
수신 코드는 다음과 같습니다.
// POST api/<controller>
[HttpPost]
[ActionName("ListArray")]
public IEnumerable<Contact> Post([FromBody]IEnumerable<int> ids)
{
IEnumerable<Contact> result = null;
if (ids != null && ids.Count() > 0)
{
return contactRepository.Fill(ids);
}
return result;
}
하나의 레코드 또는 여러 개의 레코드에 대해 올바르게 작동합니다.채우기는 DapperExtensions:를 사용하여 오버로드된 메서드입니다.
public override IEnumerable<Contact> Fill(IEnumerable<int> ids)
{
IEnumerable<Contact> result = null;
if (ids != null && ids.Count() > 0)
{
using (IDbConnection dbConnection = ConnectionProvider.OpenConnection())
{
dbConnection.Open();
var predicate = Predicates.Field<Contact>(f => f.id, Operator.Eq, ids);
result = dbConnection.GetList<Contact>(predicate);
dbConnection.Close();
}
}
return result;
}
이렇게 하면 복합 테이블(ID 목록)에서 데이터를 가져온 다음 대상 테이블에서 정말 관심 있는 레코드를 반환할 수 있습니다.
보기에서도 동일한 작업을 수행할 수 있지만, 이렇게 하면 제어력과 유연성이 조금 더 높아집니다.
또한 데이터베이스에서 찾는 내용의 세부 정보는 쿼리 문자열에 표시되지 않습니다.또한 CSV 파일에서 변환할 필요도 없습니다.
웹 API 2.x 인터페이스와 같은 도구를 사용할 때는 get, put, post, delete, head 등의 함수가 일반적으로 사용되지만 이에 제한되지는 않습니다.
따라서 게시물은 일반적으로 웹 API 인터페이스의 작성 컨텍스트에서 사용되지만 이에 제한되지 않습니다.이것은 html 실습에서 허용하는 모든 목적에 사용할 수 있는 일반적인 html 호출입니다.
게다가, 무슨 일이 일어나고 있는지에 대한 세부 사항은 요즘 우리가 자주 듣는 "탐색하는 눈"으로부터 숨겨져 있습니다.
웹 api 2.x 인터페이스의 유연한 명명 규칙과 일반적인 웹 호출 사용은 스누퍼가 정말로 다른 일을 하고 있다고 착각하게 만드는 웹 api에 호출을 보내는 것을 의미합니다.예를 들어, "POST"를 사용하여 데이터를 실제로 검색할 수 있습니다.
메서드 유형을 [HttpPost]로 만들고 int[] 매개 변수가 하나인 모델을 생성한 다음 json을 사용하여 게시합니다.
/* Model */
public class CategoryRequestModel
{
public int[] Categories { get; set; }
}
/* WebApi */
[HttpPost]
public HttpResponseMessage GetCategories(CategoryRequestModel model)
{
HttpResponseMessage resp = null;
try
{
var categories = //your code to get categories
resp = Request.CreateResponse(HttpStatusCode.OK, categories);
}
catch(Exception ex)
{
resp = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
return resp;
}
/* jQuery */
var ajaxSettings = {
type: 'POST',
url: '/Categories',
data: JSON.serialize({Categories: [1,2,3,4]}),
contentType: 'application/json',
success: function(data, textStatus, jqXHR)
{
//get categories from data
}
};
$.ajax(ajaxSettings);
또는 구분된 항목의 문자열을 전달하여 수신측의 배열 또는 목록에 넣을 수 있습니다.
제 솔루션은 문자열의 유효성을 검사하기 위한 속성을 만드는 것이었습니다. 정규식 유효성 검사를 포함하여 여러 가지 일반적인 기능을 수행합니다. 여기에는 숫자만 확인하는 데 사용할 수 있고 나중에 필요에 따라 정수로 변환할 수 있습니다.
사용 방법은 다음과 같습니다.
public class MustBeListAndContainAttribute : ValidationAttribute
{
private Regex regex = null;
public bool RemoveDuplicates { get; }
public string Separator { get; }
public int MinimumItems { get; }
public int MaximumItems { get; }
public MustBeListAndContainAttribute(string regexEachItem,
int minimumItems = 1,
int maximumItems = 0,
string separator = ",",
bool removeDuplicates = false) : base()
{
this.MinimumItems = minimumItems;
this.MaximumItems = maximumItems;
this.Separator = separator;
this.RemoveDuplicates = removeDuplicates;
if (!string.IsNullOrEmpty(regexEachItem))
regex = new Regex(regexEachItem, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var listOfdValues = (value as List<string>)?[0];
if (string.IsNullOrWhiteSpace(listOfdValues))
{
if (MinimumItems > 0)
return new ValidationResult(this.ErrorMessage);
else
return null;
};
var list = new List<string>();
list.AddRange(listOfdValues.Split(new[] { Separator }, System.StringSplitOptions.RemoveEmptyEntries));
if (RemoveDuplicates) list = list.Distinct().ToList();
var prop = validationContext.ObjectType.GetProperty(validationContext.MemberName);
prop.SetValue(validationContext.ObjectInstance, list);
value = list;
if (regex != null)
if (list.Any(c => string.IsNullOrWhiteSpace(c) || !regex.IsMatch(c)))
return new ValidationResult(this.ErrorMessage);
return null;
}
}
방금 요청에 대한 속성에 쿼리 키(Refit lib)를 추가했습니다.
[쿼리(수집 형식).다중)]
public class ExampleRequest
{
[FromQuery(Name = "name")]
public string Name { get; set; }
[AliasAs("category")]
[Query(CollectionFormat.Multi)]
public List<string> Categories { get; set; }
}
다른 모든 솔루션은 너무 많은 작업이 필요합니다.제가 사용하려고 했던 것입니다.IEnumerable<long>또는long[]순식간에HttpGet메소드 매개 변수, 하지만 핸들러 메소드 매개 변수의 서명을 만들기 위해 모든 작업을 수행하는 것은 의미가 없다고 봅니다.long[]저는 그것을 그냥 문자열로 만들고, 핸들러 안에서 분리했습니다.한 줄을 탔습니다.
public async Task<IActionResult> SomeHandler(string idsString)
{
var ids = idsString.Split(',').Select(x => long.Parse(x));
이제 당신은 다음과 같이 숫자를 전달할 수 있습니다.
.../SomeHandler?idsString=123,456,789,012
언급URL : https://stackoverflow.com/questions/9981330/pass-an-array-of-integers-to-asp-net-web-api
'programing' 카테고리의 다른 글
| T-SQL의 피벗 함수 이해 (0) | 2023.05.20 |
|---|---|
| 링크 대 컴파일 대 컨트롤러 (0) | 2023.05.20 |
| 뷰 컨트롤러가 모듈식으로 표시되는지 내비게이션 스택에서 푸시되는지 확인하는 방법은 무엇입니까? (0) | 2023.05.20 |
| 레벤슈테인 거리(VBA) (0) | 2023.05.20 |
| C++에서 어레이를 어떻게 사용합니까? (0) | 2023.05.20 |