[ 기타 ]/오픈소스 라이브러리

[Java / Playground] Rest API 호출 모듈 만들기 / RestTemplate

HSRyuuu 2024. 12. 15. 22:55

Github: RestApiService


https://github.com/HSRyuuu/Playground/tree/master/src/main/java/com/example/playground/spring/restapi

 

Playground/src/main/java/com/example/playground/spring/restapi at master · HSRyuuu/Playground

Contribute to HSRyuuu/Playground development by creating an account on GitHub.

github.com

 

이 코드는 Lombok에 의존합니다. 참고하세요.

 

사용법

        RestApiService restApiService = new RestApiService();
        
        // 1. url
        String url = "https://jsonplaceholder.typicode.com/comments";
        // 2. query parameters
        QueryParams queryParams = new QueryParams()
                .add("postId", "1")
                .add("page", "2");
        // 3. Http Headers
        ExtraHeaders headers = new ExtraHeaders()
                .add("Content-Type", "application/json")
                .add("access_token", "test-123-123");
        // 4. Request Body
        PostAddDto postUpdateDto = new PostAddDto(); // some-object
        // 5. execute
        ApiResponse response = restApiService.post(url, queryParams, headers, postUpdateDto);

1. url

base url을 세팅해줍니다.

물론 이 url에 query parameters까지 모두 포함시켜도 됩니다. 

ex) https://jsonplaceholder.typicode.com/comments?postId=1&page=2

 

2. QueryParams

Map<String, String>을 감싸고 있습니다.

query parameter 들을 key - value 형태로 추가할 수 있습니다.

add와 동시에 validation이 되어 잘못된 값은 무시합니다.

 

3. ExtraHeaders

Map<String, String>을 감싸고 있습니다.

Http Header에 추가할 값들을 key - value 형태로 추가할 수 있습니다.

마찬가지로, add와 동시에 validation이 되어 잘못된 값은 무시합니다.

 

4. Request Body

 HttpRequest Body에 해당하는 객체를 넣습니다.

GET, DELETE의 경우에도 사용은 가능하게 해 뒀습니다.

 

5. 실행

RestApiService는 get(), post(), put(), delete() 메서드를 지원합니다.

여러 가지 상황으로 오버로딩 되어있고, 없다면 매개변수를 넣고, 사용하지 않는 매개변수는 null로 대입하여 사용할 수 있습니다.

 

소스코드


class RestApiService

@Slf4j
public class RestApiService {

    private final RestTemplate restTemplate = new RestTemplate();

    /**
     * execute HTTP GET Request
     *
     * @param url          : base url
     * @param queryParams  : query parameters
     * @param extraHeaders : Http Headers
     * @param data         : Http Body (optional)
     * @return ApiResponse(status, body, errorMessage)
     */
    public ApiResponse get(String url, QueryParams queryParams, ExtraHeaders extraHeaders, Object data) {
        return this.getResult(HttpMethod.GET, url, queryParams, extraHeaders, data);
    }

    public ApiResponse get(String url) {
        return this.get(url, null, null, null);
    }

    public ApiResponse get(String url, ExtraHeaders extraHeaders) {
        return this.get(url, null, extraHeaders, null);
    }

    // POST
    public ApiResponse post(String url, QueryParams queryParams, ExtraHeaders extraHeaders, Object data) {
        return this.getResult(HttpMethod.POST, url, queryParams, extraHeaders, data);
    }

    public ApiResponse post(String url, Object body) {
        return this.post(url, null, null, body);
    }

    public ApiResponse post(String url, ExtraHeaders extraHeaders, Object body) {
        return this.post(url, null, extraHeaders, body);
    }

    // PUT
    public ApiResponse put(String url, QueryParams queryParams, ExtraHeaders extraHeaders, Object data) {
        return this.getResult(HttpMethod.PUT, url, queryParams, extraHeaders, data);
    }

    public ApiResponse put(String url, Object body) {
        return this.put(url, null, null, body);
    }

    public ApiResponse put(String url, ExtraHeaders extraHeaders, Object body) {
        return this.put(url, null, extraHeaders, body);
    }

    // DELETE
    public ApiResponse delete(String url, QueryParams queryParams, ExtraHeaders extraHeaders, Object data) {
        return this.getResult(HttpMethod.DELETE, url, queryParams, extraHeaders, data);
    }

    public ApiResponse delete(String url, Object body) {
        return this.delete(url, null, null, null);
    }

    public ApiResponse delete(String url, ExtraHeaders extraHeaders) {
        return this.delete(url, null, extraHeaders, null);
    }

    /**
     * HTTP Request
     *
     * @param httpMethod      GET / POST / PUT / DELETE
     * @param baseUrl         Request URL
     * @param queryParameters query parameter map
     * @param extraHeaders    http header map
     * @param body            Request Body
     * @return ApiResponse(status, body, errorMessage)
     */
    private ApiResponse getResult(HttpMethod httpMethod,
                                  String baseUrl,
                                  QueryParams queryParameters,
                                  ExtraHeaders extraHeaders,
                                  Object body) {
        //baseUrl validation
        if (baseUrl == null || baseUrl.isEmpty()) {
            throw new IllegalArgumentException("Base URL cannot be null or empty value");
        }
        try {
            String url = RestApiUtils.buildFullUrl(baseUrl, queryParameters);
            HttpHeaders headers = RestApiUtils.generateHttpHeaders(extraHeaders);

            log.info("[{}] API Request: URL=[{}], Headers=[{}], Body=[{}]", httpMethod, url, headers, body);
            long startTime = System.currentTimeMillis();

            HttpEntity<Object> request = new HttpEntity<>(body, headers);
            ResponseEntity<String> response = restTemplate.exchange(url, httpMethod, request, String.class);

            log.info("[{}] API Response: Status=[{}], TimeElapsed={}ms, Body=[{}]",
                    httpMethod, response.getStatusCode(), System.currentTimeMillis() - startTime, response.getBody());
            return new ApiResponse(response.getStatusCode(), null, response.getBody());
        } catch (HttpClientErrorException | HttpServerErrorException e) {
            return new ApiResponse(e.getStatusCode(), e.getResponseBodyAsString(), e.getMessage());
        } catch (RestClientException e) {
            return new ApiResponse(HttpStatus.INTERNAL_SERVER_ERROR, null, e.getMessage());
        } catch (Exception e) {
            return ApiResponse.unexpectedError();
        }
    }

}

class QueryParams

/**
 * Http Request Url 에 포함될 query parameter 값들을 감싼 클래스
 * <br> - 설계시, Map data 에는 잘못된 값이 들어갈 수 없도록 한다.
 */
@ToString
@Getter
public class QueryParams {

    private Map<String, String> data;

    public QueryParams() {
        this.data = new HashMap<>();
    }

    public QueryParams(Map<String, String> data) {
        if(data == null){
            this.data = new HashMap<>();
            return;
        }
        for(Map.Entry<String, String> entry : data.entrySet()){
            this.add(entry.getKey(), entry.getValue());
        }
    }

    public QueryParams add(String key, String value) {
        if(!RestApiUtils.validateKeyValue(key, value)){
            return this;
        }
        this.data.put(key, value);
        return this;
    }

    public String getQueryParameterString(){
        return "";
    }
}

 

class ExtraHeaders

/**
 * Http Request Header 에 포함될 값들을 감싼 클래스
 * <br> - 설계시, Map data 에는 잘못된 값이 들어갈 수 없도록 한다.
 */
@ToString
@Getter
public class ExtraHeaders {

    Map<String, String> data;

    public ExtraHeaders() {
        this.data = new HashMap<>();
    }

    public ExtraHeaders(Map<String, String> data) {
        if(data == null){
            this.data = new HashMap<>();
            return;
        }
        for(Map.Entry<String, String> entry : data.entrySet()){
            this.add(entry.getKey(), entry.getValue());
        }
    }

    public ExtraHeaders add(String key, String value){
        if(!RestApiUtils.validateKeyValue(key, value)){
            return this;
        }
        this.data.put(key, value);
        return this;
    }
}

 

class ApiResponse

@ToString
@Getter
@Setter
@AllArgsConstructor
public class ApiResponse {
    private HttpStatus status;
    private String errorMessage;
    private Object body;

    public static ApiResponse unexpectedError(){
        return new ApiResponse(HttpStatus.INTERNAL_SERVER_ERROR, "unexpected error",null);
    }

}

 

class RestApiUtils

public class RestApiUtils {

    /**
     * HTTP Header 객체 생성
     * @param extraHeaders ExtraHeaders Object
     * @return
     */
    public static HttpHeaders generateHttpHeaders(ExtraHeaders extraHeaders) {
        if (extraHeaders == null || extraHeaders.getData() == null || extraHeaders.getData().isEmpty()) {
            return new HttpHeaders();
        }

        HttpHeaders headers = new HttpHeaders();
        for (Map.Entry<String, String> entry : extraHeaders.getData().entrySet()) {
            headers.add(entry.getKey(), entry.getValue());
        }
        return headers;
    }

    /**
     * 전체 URL 생성 (base url + query params)
     * @param queryParams QueryParams Object
     * @return full url with query parameters
     */
    public static String buildFullUrl(String baseUrl, QueryParams queryParams){
        if(baseUrl == null || baseUrl.isEmpty()){
            throw new IllegalArgumentException("Base Url must not be null or empty");
        }
        if(queryParams == null || queryParams.getData() == null || queryParams.getData().isEmpty()){
            return baseUrl;
        }
        //make queryParameter String
        UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
        for (Map.Entry<String, String> entry : queryParams.getData().entrySet()) {
            builder.queryParam(entry.getKey(), entry.getValue());
        }
        String queryParameterString = builder.build().toString();

        if(baseUrl.contains("?")){
            return baseUrl + queryParameterString.replaceAll("\\?", "&");
        }

        return baseUrl + queryParameterString;
    }

    /**
     * key, value String validation
     * @param key Map key
     * @param value Map value
     * @return Boolean result
     */
    public static boolean validateKeyValue(String key, String value){
        if(key == null || key.isEmpty()){
            return false;
        }
        if( value == null || value.isEmpty()) {
            return false;
        }
        return true;
    }

}

 

반응형