상세 컨텐츠

본문 제목

[ASP.NET Core Web API] Action Method - 요청

.NET/ASP.NET

by 클리엘 클리엘 2021. 3. 25. 16:44

본문

728x90

해당 글은 아래 글에서 이어집니다.

 

[.NET/ASP.NET] - [ASP.NET Core Web API] 프로젝트및 Controller 생성

 

[ASP.NET Core Web API] 프로젝트및 Controller 생성

1. 프로젝트 Web API 기본사항에 대해 알아보기 위한 간단한 테스트 프로젝트를 만들어 봅니다. Visual Studio(2019)에서 프로젝트를 오른쪽 항목에서 'ASP.NET Core Web API (C#)'프로젝트로 선택하고 'Next'버

lab.cliel.com

다음은 예제로 작성된 액션 메서드입니다.

[HttpGet]
public string Get()
{
    return "hello";
}

예제에서 작성된 Get() 메서드는 HttpGet 어트리뷰트에 따라 클라이언트의 Get요청에 응답하게 됩니다. 액션 메서드도 컨트롤러와 마찬가지로 Route를 지정할 수 있는데 액션 메서드명과 동일하게 Route를 지정하려면 [controller]처럼 [action]을 사용하거나 임의의 값을 이용해 직접 Route를 지정할 수 있습니다.

[HttpGet]
[Route("[action]")]
public string abc()
{
    return "hello";
}

필요하다면  메서드는 다음과 같은 방법으로 사용자가 추가한 매개변수를 전달받을 수도 있습니다. 매개변수를 전달받을 수 있는 메서드를 아래와 같이 추가해 줍니다.

[HttpGet("{id}")]
public string Get(int id)
{
    return "Parameter : " + id.ToString();
}

매개변수는 /다음에 필요한 값을 URL에 입력해 전송합니다. 매개변수가 없으면 Get() 메서드가 요청을 받아 'Hello'를 출력하지만 매개변수가 존재하면 Get(int id) 메서드가 받아 'Parameter : '로 URL에 전달된 매개 변숫값을 출력하게 됩니다.

 

새롭게 추가한 Get(int id) 메서드는 URL에 전달된 매개변수를 id라는 변수로 받아서 출력하고 있는데 해당 변수는 int형으로 정의되어 있습니다. 이 말은 매개 변숫값이 int형의 데이터만 받을 수 있다는 것을 의미하는데 만약 데이터 자체를 int가 아닌 'abcdef'와 같은 string형식으로 전달하게 되면 'abcdef'은 int형으로 표시할 수 있는 데이터가 아니기에 400 Bed Request 오류를 발생시키게 됩니다.

 

이러한 문제를 방지하기 위해 API 메서드에서는 특정 매개변수에 데이터 형식을 강제하여 해당 데이터형의 매개변수가 넘어오는 경우에만 요청을 받을 수 있도록 지정할 수 있습니다.

[HttpGet("{id:int}")]
public string Get(int id)
{
    return "Parameter : " + id.ToString();
}

만약 URL을 /api/values/abc처럼 호출한다면 매개변수의 데이터가 int형이 아니므로 Get(int id) 메서드는 해당 요청을 받을 수 없고 매개변수가 없을 때의 메서드는 존재하지만 int형이 아닌 다른 형식의 매개변수를 받은 메서드는 존재하지 않으므로 결국 요청을 처리할 메서드가 없게 됩니다.

 

이에 따라 최종적으로 매개변수를 더 이상 매개변수가 아닌 요청 URL 중 하나로 판단하게 되지만 이 역시 해당 요청을 받을 수 있는 메서드는 어디에도 없기에 마지막에는 404 Not Found 응답을 반환하는 것입니다.

 

또한 매개변수값이 아예 넘어오지 않는 경우를 대비해 기본 값을 다음과 같이 설정해 주거나

{id=123}

생략이 가능하다는 것을 명시적으로 지정해 줄 수 있습니다.

{id?}

물론 생략한다면 해당 매개변수 데이텨형의 기본값이 들어가게 됩니다.

 

예제에서는 매개변수가 하나만 온다는 것을 가정하고 있으나 필요한 만큼을 설정해 값을 가져오는 것 역시 가능하며

[HttpGet("{a}/{b}")]
public string Get(string a, string b)
{
    return a + b;
}

이와 같은 경우도 역시 인라인 Validation을 지정할 수 있습니다.

[HttpGet("{a:int}/{b:int}")]
public string Get(int a, int b)
{
    return a + b;
}

위 예제는 액션 메서드에서 string형식의 데이터를 반환하도록 되어 있지만 사용자가 지정한 특정 형식을 반환하게 할 수도 있습니다.

 

우선 아래와 같은 데이터 클래스를 생성한 뒤에

public class student
{
    public int id { get; set; }
    public string name { get; set; }
}

해당 데이터 클래스의 형식을 반환하는 메서드를 추가합니다.

[HttpGet]
[Route("[action]")]
public IEnumerable<student> GetStudent()
{
    student[] s = new student[] { new student { id = 1, name = "홍길동" }, new student { id = 2, name = "홍길순" } };

    return s;
}

ASP.NET Core Web API에서는 기본적으로 데이터를 json으로 내보내도록 되어 있으므로 위 메서드를 요청하면 json타입의 데이터를 받게 됩니다.

 

물론 원한다면 Produces 특성을 사용해 내려주는 데이터의 포맷을 별도로 지정해 줄 수도 있습니다. 이를 위해 메서드에서 Produces어트리뷰트를 사용해 반환되는 ConentType을 명시하고

[HttpGet]
[Route("[action]")]
[Produces("application/xml")]
public IEnumerable<student> GetStudent()
{
    student[] s = new student[] { new student { id = 1, name = "홍길동" }, new student { id = 2, name = "홍길순" } };

    return s;
}

응용프로그램에서 데이터를 XML로 내려줄 수 있도록 Startup.cs의 ConfigureServices() 메서드를 수정해 AddXmlSerializerFormatters() 메서드를 호출하도록 합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddXmlSerializerFormatters();
}

참고로 Produces 어트리뷰트는 클래스에서 지정하여 클래스 전체에 응답 데이터 형식을 공통적으로 지정해 줄 수 있고, 반대로 요청 데이터까지 데이터 포맷 형식을 지정하려면 Consumes 어트리뷰트를 Produces 어트리뷰트와 동일한 방식으로 사용하면 됩니다.

 

단순한 요청과 메개변수는 Get으로 처리할 수 있으나 Post로의 데이터 전송 요청은 HttpPost 어트리뷰트를 사용해 메서드를 생성해야 합니다.

[HttpPost]
public student AddStudent([FromBody]student std)
{
    student s = new student() { id = std.id, name = std.name };

    return s;
}

HttpPost 어트리뷰트가 적용된 AddStudent() 메서드는 stdent형식의 데이터를 Post로 전달받아 이를 토대로 새로운 student 객체를 생성한 뒤 해당 인스턴스를 다시 반환하도록 하고 있습니다. AddStudent() 메서드는 별도의 Route가 설정되지 않았으므로 values 컨트롤러로 Post를 수행하면 현재까지 Post요청을 받는 유일한 메서드인 AddStudent() 메서드를 호출하게 될 것입니다.

 

AddStudent() 메서드에서는 student형식의 Post데이터를 전달받기 위해 [FromBody]를 선언하였는데 Post자체도 여러 가지 형태로 데이터가 전송될 수 있는 상황에서 FromBody는 Body내부를 통해서 데이터를 받겠다는 것을 의미합니다.

 

Post로 데이터가 전송되어 오면 AddStudent() 메서드에서 사용한 student 객체에 전송되어 오는 id와 name을 키로 하여 알아서 매핑시켜 student 객체를 반환하게 됩니다. 참고로 값의 key와 모델의 속성을 비교해 매핑하는 동작을 모델 바인딩이라고 합니다.

 

위에서 PostMan을 사용해 테스트한 방식은 id와 name을 json방식으로 전송한 방식을 가정한 것입니다. 만약 id와 name에 해당하는 값이 json이 아닌 Form방식을 통해 전송되는 경우라면 AddStudent() 메서드에서 Form데이터를 받을 수 있도록 FromBody를 아래와 같이 FromForm으로 수정해야 합니다.

[HttpPost]
public student AddStudent([FromForm] student std)
{
    student s = new student() { id = std.id, name = std.name };

    return s;
}

이 밖에 URL에서 Query String을 통해 매개변수를 받는 경우라면 별다른 특성이 필요없이 메서드에 key와 알치하는 변수를 선언해 두면 해당 변수에서 값을 바로 읽어올 수 있습니다.

[HttpGet]
public string Get(string username)
{
    return "hello " + username;
}

Form이외에 Query, Header등 다양한 특성이 있으며 전송되는 형태에 따라 액션 메서드에 해당 특성을 맞춰 주면 됩니다.

 

AddStudent() 메서드는 전송받은 Post값을 사실상 그대로 반환하는 형태를 취하고 있는데 필요하다면 다른 메서드를 호출하여 해당 메서드에 실행을 이관시킬 수도 있습니다.

[HttpPost]
public IActionResult AddStudent([FromForm] student std)
{
    student s = new student() { id = std.id, name = std.name };

    return CreatedAtAction("Get", new { id = std.id });
}

AddStudent() 메서드의 반환 형식은 IActionResult로 변경하였으며 return으로 CreatedAtAction() 메서드를 통해서 Get() 메서드를 호출하도록 하고 있습니다. 이때 두 번째 매개변수로 익명 형식을 지정한 뒤 id 값을 전달하고 있기 때문에 최종적으로 Get(int id) 메서드를 호출하게 됩니다.

 

참고로 CreatedAtAction() 메서드는 Http응답 코드를 201 Created로 반환합니다.

 

AddStudent() 메서드에서 Post 데이터를 받을 때 student 모델을 사용해 받도록 처리하고 있는데 이러한 방식은 모델을 사용해 특정 데이터의 유효성을 검증하는 손쉬운 방법을 제공하기도 합니다.

 

예를 들어 AddStudent()가 실행될 때 name이라는 필드 값이 빠져있으면 안 되는 경우 student 모델에 Requred 어트리뷰트를 사용해 필수 입력 상태로 만들고 name필드를 제외하고 POST 요청을 수행하게 되면

public class student
{
    public int id { get; set; }

    [Required(ErrorMessage = "필수입력 사항입니다.")]
    public string name { get; set; }
}

모델 유효성 검증에 따라 다음과 같이 오류를 떨어뜨려주게 됩니다.

 

이와 같이 설정할 수 있는 어트리뷰트 중 Required는 필수로 데이터를 할당하도록 강제합니다. 기타 Mim/MaxLength는 입력값의 최소/최대길이를 제한하고 EmailAddress는 입력값이 메일 형식임을 지정하는 등 설정에 따라 다양한 Validation을 수행할 수 있습니다.

 

만약 액션 메서드 안에서 별도의 모델 Validation여부를 확인해야 한다면 ModelState객체의 IsValid속성을 확인하면 되는데

[HttpPost]
public IActionResult AddStudent([FromForm] student std)
{
    if (ModelState.IsValid)
    {
        //정상
    }
    else
    {
        //오류
        return BadRequest(ModelState);
    }

    student s = new student() { id = std.id, name = std.name };

    return CreatedAtAction("Get", new { id = std.id });
}

IsValid가 false라면 Model에 지정된 형식대로 유효성 검증을 통과하지 못했다는 것을 의미합니다.

728x90

관련글 더보기

댓글 영역