GET + POST만 있는 RESTful API 클라이언트

현재 마이크로 서비스와 웹 애플리케이션을 기반으로 프로젝트를 진행하고 있습니다. 제품은 다음 기술로 개발되었습니다.
  • 마이크로 서비스: ASP.NET Core의 API
  • 웹 응용 프로그램: React 및 TypeScript(axios 포함)

  • 클라이언트 서버에서 프로덕션을 시작하는 동안 구성이 다른 몇 가지 문제가 발생했습니다. 실제로 거의 모든 클라이언트에서 모든 요청이 결과를 반환했습니다.

    200 OK
    


    한 특정 서버에서 PUT , PATCHDELETE와 같은 RESTful 동사가 있는 요청이 다음 상태로 반환되었습니다.

    405 Method not allowed
    


    실제로 그들 중 일부는 보안상의 이유로 간단한 GET + POST 동사를 사용하지 않는 HTTP 요청을 차단했습니다.

    구성



    제품은 서비스 제공업체의 전용 서버에서 호스팅됩니다. 이것은 모든 공급자의 서버에 전역인 Nginx 역방향 프록시 뒤에 있습니다. 그때부터 두 가지 솔루션을 사용할 수 있었습니다.
  • 역방향 프록시에서 PUT , PATCH , DELETE 요청 허용
  • X-Method-Override 헤더를 사용하여 마이크로 서비스 조정

  • 첫 번째 대안이 불가능하므로 변경으로 인해 공급자가 호스팅하는 제품의 보안이 변경될 수 있으므로 두 번째 대안이 선택됩니다.

    Typescript HTTP 클라이언트



    웹앱은 axios 라이브러리가 있는 React 및 TypeScript를 기반으로 합니다. 목적은 axios를 기반으로 사용자 정의 API 클라이언트를 정의하고 X-Method-Override 헤더를 설정하는 것입니다.

    클라이언트 인터페이스



    다음은 사용자 정의 API 클라이언트의 인터페이스가 나타납니다.

    interface ApiClient {
      instance(): ApiClient;
      get<T>(url: string): Promise<T>;
      post<T>(url: string, body: any): Promise<T>;
      put<T>(url: string, body: any): Promise<T>;
      patch<T>(url: string, body: any): Promise<T>;
      delete<T>(url: string): Promise<T>;
    }
    


    이 인터페이스를 통해 클라이언트는 5가지 주요 HTTP 동사인 GET , POST , PUT , PATCHDELETE 를 사용할 수 있습니다.
    클라이언트는 싱글톤 패턴으로 설계되었습니다(인스턴스 방법을 알 수 있습니다!).

    클라이언트 구현



    import Axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
    
    type HttpVerb = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
    
    class ApiClient {
      private static singleton: ApiClient;
      private readonly instanceAxios: AxiosInstance;
      private URL: string = "https://localhost:44300/";
    
      private constructor() {
        this.instanceAxios = Axios.create({
          baseURL: `${this.URL}api/`,
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json",
          },
        });
      }
    
      public static get instance(): ApiClient {
        if (!ApiClient.singleton) {
          this.singleton = new ApiClient();
        }
    
        return ApiClient.singleton;
      }
    
      private async request<T>(method: HttpVerb, url: string, body?: any): Promise<T> {
        const requestConfig: AxiosRequestConfig = {
          method: method === "GET" ? "GET" : "POST",
          url: url,
          headers: {/*... */},
        };
        // Use X-HTTP-Method-Override to use all HTTP verbs
        // Verbs will be encapsulated in POST Header
        if (method !== "GET" && method !== "POST")
          requestConfig.headers["X-HTTP-Method-Override"] = method;
    
        if (body) {
          requestConfig.data = body;
        }
    
        try {
          const response: AxiosResponse = await this.instanceAxios(requestConfig);
          return response.data as T;
        } catch (error) {
          throw new Error(error.response.data);
        }
      }
    
      public async get<T>(url: string): Promise<T> {
        try {
          const response: AxiosResponse = await this.instanceAxios.get(url);
          return response.data as T;
        } catch (error) {
          throw new Error(/*..*/);
        }
      }
    
      public async post<T>(url: string, body: any): Promise<T> {
        return this.request("POST", url, body);
      }
    
      public async put<T>(url: string, body: any): Promise<T> {
        return this.request("PUT", url, body);
      }
    
      public async patch<T>(url: string, body?: any, ): Promise<T> {
        return this.request("PATCH", url, body);
      }
    
      public delete<T>(url: string): Promise<T> {
        return this.request("DELETE", url);
      }
    }
    
    export default ApiClient.instance;
    

    이제 다른 모든 *.ts 파일에서 이 클라이언트를 쉽게 사용할 수 있습니다.

    import { ApiClient} from "@services/ApiClient"
    var data1 = await ApiClient.get("/myUrl");
    var patchData = {
        "property": "value"
    };
    // The PATCH method will add a X-HTTP-Method-Override header
    var responseToPatch = await ApiClient.patch("/myUrl2", patchData);
    


    ASP.NET Core의 마이크로서비스



    이제 ASP.NET Core에 X-HTTP-Method-Override 헤더를 확인하도록 지시하는 것이 중요합니다. IApplicationBuilder.UseHttpMethodOverride 방법으로 간단하게 끝!

    // All using...
    
    namespace My.Awesome.Namespace.Api
    { 
        public class Startup
        {
            // ...
            public void ConfigureServices(IServiceCollection services)
            {
                // ...
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                // ...
                // ASP.NET Core API will handle X-HTTP-Method-Override header !
                app.UseHttpMethodOverride();
    
                app.UseRouting();
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }
    


    결론



    HTTP 동사가 REST 원칙을 준수하는 것을 허용하지 않는 서버를 만나더라도 걱정하지 마십시오. 이 기사에서 설명한 것처럼 X-HTTP-Method-Override 헤더에 REST 동사를 캡슐화하는 것이 가능합니다!

    리소스


  • 일러스트: fullvector에 의해 생성된 비즈니스 벡터 — www.freepik.com
  • 좋은 웹페이지 즐겨찾기