반응형

문제 발생

Enum

@AllArgsConstructor
@Getter
public enum Foo {

  A("에이", "a"),
  B("비", "b"),
  C("씨", "c");

  private String korean;
  private String small;

}

 

Controller

@RestController
public class EnumController {

  @GetMapping("/enum/{code}")
  private RestResponse showFoo(@PathVariable String code){
    return new RestResponse(true, Foo.valueOf(code));
  }
}

 

Response 객체

@Getter
public class RestResponse {

  private boolean success;

  private Foo data;

  public RestResponse(boolean success, Foo data) {
    this.success = success;
    this.data = data;
  }
}

위와 같이 코드 값을 주소에 입력받으면,

코드에 해당하는 전체 객체를 리턴해주고 싶은데, 아래와 같이 출력됐다.

해결 방법

1. Enum 객체 전체 리턴해주기

@JsonFormat(shape = Shape.OBJECT) // 추가
@AllArgsConstructor
@Getter
public enum Foo {

  A("에이", "a"),
  B("비", "b"),
  C("씨", "c");

  private String korean;
  private String small;

}

Enum 객체에 @JsonFormat 어노테이션을 추가해준다.

리턴받는 기댓값은 객체 형태이기 때문에 Shape.OBJECT로 설정해준다.

 

2. 값만 리턴해주기

@AllArgsConstructor
@Getter
public enum Foo {

  A("에이", "a"),
  B("비", "b"),
  C("씨", "c");

  @JsonValue // 추가
  private String korean;
  private String small;

}

만약 특정 값만 리턴하고 싶다면, 변수에 @JsonValue 어노테이션을 추가해준다.

상황에 따라 골라서 쓰면 될 것 같다. 😁


출처 : https://shinsunyoung.tistory.com/73

반응형
,
반응형

1. 개요

  • 라이브러리가 OpenAPI 3.0 스펙에 맞는 JSON을 자동으로 만들어 주면, Swagger UI는 만들어진 JSON을 바탕으로 화면에 표시 해줍니다.
  • Spring Boot 2.0.9.RELEASE로 테스트 하였습니다.

2. 문제 해결

  • messageconverter가 설정되어 있어서 Swagger UI가 /v3/api-docs/ end-point로 접근시 String으로 감싼 JSON 형으로 리턴됨.
    1. HttpServletRequest 객체의 getRequestURI 메소드를 사용하여 요청한 Uri를 가져옴.
    2. Uri가 /v3/api-docs/ 또는 /v3/api-docs/swagger-config 인지 확인하여 원본 내용을 리턴하게 예외처리. (e.g. json이면 json으로)

3. build.gradle 파일에 dependency 추가

dependencies {
    implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.2.30'
}

4. 설정 파일에 관련 설정 추가

springdoc:
  api-docs:
    groups:
      enabled: true
  swagger-ui:
    path: /swagger-ui.html
    displayRequestDuration: true
    groups-order: DESC

5. OpenApiConfig.java 파일 작성

package kr.webgori.lolien.discord.bot.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@OpenAPIDefinition(
    info = @Info(title = "lolien-discord-bot API 명세서",
        description = "API 명세서",
        version = "v1",
        contact = @Contact(name = "webgori", email = "webgori@gmail.com"),
        license = @License(name = "Apache 2.0",
            url = "http://www.apache.org/licenses/LICENSE-2.0.html")
    )
)
@Configuration
public class OpenApiConfig {
  /**
   * customGameOpenApi.
   * @return GroupedOpenApi
   */
  @Bean
  public GroupedOpenApi customGameOpenApi() {
    String[] paths = {"/custom-game/**"};
    return GroupedOpenApi.builder().setGroup("내전 관련 API").pathsToMatch(paths)
        .build();
  }

  /**
   * leagueOpenApi.
   * @return GroupedOpenApi
   */
  @Bean
  public GroupedOpenApi leagueOpenApi() {
    String[] paths = {"/league/**"};
    return GroupedOpenApi.builder().setGroup("리그 관련 API").pathsToMatch(paths)
        .build();
  }
}

6. Swagger UI 확인

  • http://IP 또는 도메인:포트/swagger-ui.html 으로 접속 후 확인


출처 : https://webgori.github.io/spring/2020/02/09/OpenAPI-3.0%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-Spring-REST-API-%EB%AC%B8%EC%84%9C%ED%99%94.html

반응형
,
반응형

스프링에서 rest api call 하기 / spring 에서 rest call / spring 에서 http request



RestTemplate

Spring framework 에서 Rest API 호출을 위해 RestTemplate 이라는 것을 제공한다. ref. 2, ref. 3 에서 간단한 예제를 확인할 수 있다.


postForObject

Server 에서 RESTful API server 에 request 를 POST 방식으로 form 의 data 는 json 형식으로 보낼 때 방법. ElasticSearch 등을 사용할 때 활용할 수 있다. 혹시나 해서 적어놓는데, ElasticSearch 는 Java API 를 따로 제공한다.(참고)

@Override
public JsonResult retrieve() {

    String JSONInput = ("{\n" +
            "    \"aggs\": {\n" +
            "       \"aggs_stats\" : {\n" +
            "           \"date_histogram\":{\n" +
            "             \"field\" : \"event_timestamp\",\n" +
            "             \"interval\" : \"hour\"\n" +
            "           }\n" +
            "       }\n" +
            "    }\n" +
            "} ");

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity param= new HttpEntity(JSONInput, headers);


    RestTemplate restTemplate = new RestTemplate();
    String result = restTemplate.postForObject(url, param, String.class);

    return new ElasticSearchResult(result);

}



setMessageConverters

ref. 5ref. 6 을 통해서 MessageConverters 를 설정해서 postForObject 를 이용하는 방법을 알아볼 수 있다.


// RestTemplate 에 MessageConverter 세팅
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new FormHttpMessageConverter());
converters.add(new StringHttpMessageConverter());

RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(converters);

// parameter 세팅
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("name", "xx");
map.add("password", "xx");

// post
String result = rest.postForObject("http://localhost:8080/soa-server/user/", map, String.class);
System.out.println(result);



getForObject 의 한계

Accept 를 따로 정해주지 않는 경우에 RestTemplate#getForObject 에서는 기본적으로 아래 header 를 이용한다.
Accept : application/json, application/*+json

그래서 getForObject 를 이용해서 만약에 다른 Accept 를 사용하고 싶다면, 다른 함수를 이용해야 한다.(execute, exchange)

getForObject 에서 param 을 주는 방법

ref. 4 를 참고하자. 대략 아래와 같은 방식을 이용한다.

UriComponentsBuilder urlWithoutParam 
    = UriComponentsBuilder.fromUriString("http://dd.net")
                .path("/path1/ddd");
        
for(Map.Entry<String, Object> e: param.entrySet()){
    urlWithoutParam = urlWithoutParam.queryParam(e.getKey(), e.getValue());
}
URI reqUrl = urlWithoutParam.build().toUri();


RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(reqUrl, String.class);




RestTemplate#execute

좀 더 자유로운 Http request 를 위해서 execute 를 사용하게 되었다. 그리고 이 RestTemplate 의 doExecute 에서 Extractor 를 사용하기 때문에 이 녀석만 제대로 맞춰 주면 된다.

protected <T> T doExecute(URI url, HttpMethod method, 
                        RequestCallback requestCallback,
                        ResponseExtractor<T> responseExtractor) 
                        throws RestClientException {
    try {
        ClientHttpRequest request = createRequest(url, method);
        if (requestCallback != null) {
            requestCallback.doWithRequest(request);
        }
        response = request.execute();
        if (!getErrorHandler().hasError(response)) {
            logResponseStatus(method, url, response);
        }
        else {
            handleResponseError(method, url, response);
        }
        if (responseExtractor != null) {
            return responseExtractor.extractData(response);
        }



Example


// ApiCallResponseExtractor.java
public class ApiCallResponseExtractor extends HttpMessageConverterExtractor<String> {

    public ApiCallResponseExtractor (Class<String> responseType,
                                List<HttpMessageConverter<?>> messageConverters) {
        super(responseType, messageConverters);
    }

    @Override
    public String extractData(ClientHttpResponse response) throws IOException {

        String result;

        if (response.getStatusCode() == HttpStatus.OK) {
            Scanner scanner = new java.util.Scanner(response.getBody()).useDelimiter("[\\(\\)]");
            scanner.next(); // callback name,
            String json = scanner.next();
            result = json;

        } else {
            result = null;
        }

        return result;
    }
}


// MyController.java
final String url = "http://10.10.2.100:8080/portal/customer/getCustomerList?" +
                JsonKey.CALLBACK + "={" + JsonKey.CALLBACK + "}" + "&" +
                JsonKey.USER_ID + "={" + JsonKey.USER_ID + "}";
Map<String, String> vars = new HashMap<String, String>();
vars.put(JsonKey.CALLBACK, JsonKey.API_CALLBACK_NAME);
vars.put(JsonKey.USER_ID, "test_id");

RestTemplate restTemplate = new RestTemplate();
ResponseExtractor<String> responseExtractor =
        new ApiCallResponseExtractor(String.class, restTemplate.getMessageConverters());

String response = restTemplate.execute(url, HttpMethod.GET, null, responseExtractor, vars);



Header field Accept

참고로 execute 를 사용하면 기본 Accept 는 아래를 사용하게 된다.

Accept : text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2



RestTemplate 을 이용해서 json request

ref. 7 에 잘 나와 있다.



Reference

  1. RestTemplate (Spring Framework 4.1.3.RELEASE API)
  2. http://spring.io/guides/gs/consuming-rest/#initial
  3. http://spring.io/blog/2009/03/27/rest-in-spring-3-resttemplate
  4. android - How to send a getForObject request with parameters Spring MVC - Stack Overflow
  5. java - Spring RestTemplate postForObject with Header: webservice can't find my header parameters - Stack Overflow
  6. Sending POST parameters with RestTemplate requests - Spring Forum
  7. Rest Template Json Request | Spring Tutorials


반응형
,
반응형

자바에서 HttpURLConnection은 대단히 자주 사용하는 클래스라고 할 수 있다!

다음은 가장 기본이 되는 호출 예제이다!<rest의 시대(?)를 살고 있는 개발자에게 필수가 아닐까?>



출처: http://nine01223.tistory.com/229 [스프링연구소(spring-lab)]

반응형
,