REST API 스팸 방지 및 CAPTCHA 지원

모델을 REST API를 통해 수정할 수 있는 경우, 스팸 가능성이 있는 속성을 수정할 수 있는 모든 관련 API 엔드포인트에도 지원을 추가해야 합니다. 이는 POSTPUT 변이를 확실히 포함하지만, 모델의 기밀/공개 플래그를 변경하는 것과 관련된 다른 것들 또한 포함할 수 있습니다.

REST 엔드포인트에 지원 추가

주요 단계는 다음과 같습니다.

  1. resourcehelpers SpammableActions::CaptchaCheck::RestApiActionsSupport를 추가합니다.
  2. Update 서비스 클래스 생성자에 perform_spam_check: true를 전달합니다. 이 값은 기본적으로 Create 서비스에서 true로 설정되어 있습니다.
  3. Spammable 모델 인스턴스를 생성하거나 업데이트한 후, #check_spam_action_response!를 호출하고, 생성하거나 업데이트한 인스턴스를 변수에 저장합니다.
  4. 요청의 failure 케이스에 대한 오류 처리 로직을 식별합니다. 생성 또는 업데이트에 성공하지 않은 경우이를 나타낼 수 있습니다. 이는 가능한 스팸 탐지로, Spammable 인스턴스에 오류를 추가합니다. 이 오류는 일반적으로 render_api_error! 또는 render_validation_error!와 유사합니다.
  5. 기존 오류 처리 로직을 with_captcha_check_rest_api(spammable: my_spammable_instance) 호출 내에서 래핑합니다. 이때, spammable:이름 지정된 인수로 변수에 저장된 Spammable 모델 인스턴스를 전달합니다. 이 호출은 다음을 수행합니다.
    1. 모델에서 필요한 스팸 확인을 수행합니다.
    2. 스팸이 감지되면:
      • 설명적인 스팸 특정 오류 메시지와 함께 Grape #error! 예외를 발생시킵니다.
      • 응답에 오류 필드로 추가된 관련 정보를 포함합니다. 이러한 필드에 대한 자세한 내용은 REST API 문서의 스팸으로 감지된 요청 해결 섹션을 참조하십시오.

    참고: 표준 ApolloLink 또는 Axios 인터셉터 CAPTCHA 지원을 사용하는 경우, 자동 처리되므로 이 필드 세부 정보를 무시할 수 있습니다. 이는 잠재적인 스팸 체크에 실패한 경우 GraphQL API를 직접 사용하여 CAPTCHA 응답을 해결하고 요청을 재제출하려는 경우에 관련이 됩니다.

snippets 리소스의 postput 작업에 대한 예제는 다음과 같습니다:

module API
  class Snippets < ::API::Base
    #...
    resource :snippets do
      # 이 헬퍼는 `#with_captcha_check_rest_api`를 제공합니다.
      helpers SpammableActions::CaptchaCheck::RestApiActionsSupport

      post do
        #...
        service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs).execute
        snippet = service_response.payload[:snippet]

        if service_response.success?
          present snippet, with: Entities::PersonalSnippet, current_user: current_user
        else
          # 일반적인 오류 응답을 `with_captcha_check_rest_api(spammable: snippet)` 블록으로 래핑합니다.
          with_captcha_check_rest_api(spammable: snippet) do
            # 가능한 스팸이 감지되면, `#with_captcha_check_rest_api`에 의해 Grape가 처리할 예외가 발생합니다.
            render_api_error!({ error: service_response.message }, service_response.http_status)
          end
        end
      end

      put ':id' do
        #...
        service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs, perform_spam_check: true).execute(snippet)

        snippet = service_response.payload[:snippet]

        if service_response.success?
          present snippet, with: Entities::PersonalSnippet, current_user: current_user
        else
          # 일반적인 오류 응답을 `with_captcha_check_rest_api(spammable: snippet)` 블록으로 래핑합니다.
          with_captcha_check_rest_api(spammable: snippet) do
            # 가능한 스팸이 감지되면, `#with_captcha_check_rest_api`에 의해 Grape가 처리할 예외가 발생합니다.
            render_api_error!({ error: service_response.message }, service_response.http_status)
          end
        end
      end
    end
  end
end