はじめに
Springについて初めて学び、実践的取り組みとして「飲み会での幹事支援アプリ」というWebアプリケーションを開発しました。
このアプリでは、飲み会に適した店舗の情報を取得することができ、その情報の取得には「ホットペッパーAPI」を利用しています。
この記事では、ホットペッパーAPIを使用して店舗情報を取得する方法や、その際に直面した問題について詳しく紹介していきたいと思います。
ホットペッパーAPIとは?
ホットペッパーAPIとは、飲食店の基本情報、メニュー、クーポン情報などのデータを取得することができる飲食店検索サービスです。
このサービスは、リクルートが無料で提供しており、誰でも簡単に使用することができます。
新規登録方法
ホットペッパーAPIを使用するには、新規登録を行います。
新規登録を行うには、ホームページにある新規登録ボタンを押します。
新規登録ボタンを押すと以下のような画面になり、メールアドレスで登録を行います。
登録が完了すると個別のAPIキーが割り当てられ、このAPIキーを用いることでホットペッパーAPIの使用が可能なります。
使用方法
ホットペッパーAPIにはベースとなるリクエストURLが提供されています。
そのURLのクエリパラメータに上記で取得したAPIキーと任意のパラメータを加えることで情報の取得ができるようになります。
http://webservice.recruit.co.jp/hotpepper/gourmet/v1/
また、任意のパラメータは、ホームページのグルメサーチAPIに記載されています。
全部で63個ものパラメータが提供されており、この中から自分が検索したい条件に合ったパラメータを組み合わせます。
いずれか最低一つ選択する必要があります。
サンプルとして、東京にあるお店(大エリアコード=Z011)を検索したいときのURLは以下のようになります。
http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=[APIキー]&large_area=Z011
このURLをたたくと以下のように表示されます。
このように東京にある店舗の情報を取得することができました。また、レスポンスの形式はデフォルトではxmlなのですが、format=json
をクエリパラメータに加えることでjson形式で受け取ることもできます。
http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=[APIキー]&large_area=Z011&format=json
SpringBootを使ってホットペッパーAPIをたたいてみる
ここまでで、ホットペッパーAPIの使用方法を簡単に説明しました。
次は実際にSpring Bootを用いて、上記のように作成したURLをたたき、店舗の情報を取得する方法を説明していきます。
実装したアプリの構造
今回作成したアプリはAPI通信を行っているため、Restfulインターフェースに基づいて設計されています。
また、Spring Bootは3層アーキテクチャを採用しており、Repository層でホットペッパーAPIを呼び出す仕組みになっています。
アプリで行うこと
今回のアプリでは、「ユーザからGETリクエストが発生されたらホットペッパーAPIを用いて横浜にあるすべての居酒屋情報を取得し、取得した一覧情報の中から必要な情報をピックアップしてユーザに返す」ということを行います。
そこで、この一連の流れを行うAPIを作成しました。(エンドポイントは/shop
。)
今回のAPIでは、リクエストで受け取るパラメータはなしと設定し、GETリクエストを受け取るとホットペッパーAPIを用いて自動で横浜の居酒屋情報のみを返す仕組みになっています。
今回設計したAPIの詳細は以下の通りです。
メソッド | エンドポイント | パラメータ | 説明 |
---|---|---|---|
GET | /shop | なし | 横浜にある居酒屋の「店舗ID、店舗名、店舗の住所、予算、店舗URL、営業時間」をリストで返すAPI |
また、APIのレスポンス例は以下の通りです。
[
{
"shopId": "J999999999",
"shopName": "居酒屋 ホットペッパー",
"shopAddress": "神奈川県横浜市...",
"budgetName": "~1000",
"url": "店舗URL",
"open": "月~金/11:30~14:00"
}
]
アプリが実行するホットペッパーAPIのIF
上記で説明したように、今回のアプリでは横浜の居酒屋情報のみを取得します。
そこで、ホットペッパーAPIのクエリパラメータに条件にあうパラメータを加えていきます。
具体的に、今回は以下の5つを使用しました。
パラメータ | 項目名 | 説明 | 値 |
---|---|---|---|
format | レスポンス形式 | レスポンスをXMLかJSONかを指定。 | xmlまたはjson 初期値:xml |
key | APIキー | APIを利用するために新規登録方法で取得したキー。 | |
genre | お店ジャンルコード | 絞り込みたいお店のジャンル。指定できるコードについてはホットペッパーAPI公式ページに記載されているジャンルマスタAPIを参照。 | (例)G001 |
lat | 緯度 | ある地点からの範囲内のお店の検索を行う場合の緯度。 | (例)35.465981 |
lng | 経度 | ある地点からの範囲内のお店の検索を行う場合の経度。 | (例)139.622062 |
formatを用いることでjson形式のレスポンスにし、genreで居酒屋に、lat、lngで場所を横浜に設定しています。
場所の指定に緯度経度を用いた理由
場所の指定はエリアコードを用いることも可能だが、エリアコードの指定範囲が公式ページ内に記載されていないため緯度経度を使用して場所を特定しました。
また、緯度経度での場所指定ではデフォルトでrangeクエリが指定されており、特定の場所から半径1000m以内を示しています。指定範囲は変えることもできます。
パラメータ | 項目名 | 説明 | 値 |
---|---|---|---|
range | 検索範囲 | ある地点からの範囲内のお店の検索を行う場合の範囲を5段階で指定可能。 | 1: 300m、2: 500m、 3: 1000m (初期値)、4: 2000m、5: 3000m (例) 1 |
これらをのパラメータを使用した横浜の居酒屋情報を取得するURLは以下のようになります。
https://webservice.recruit.co.jp/hotpepper/gourmet/v1/?format=json&key=[APIキー]&genre=G001&lat=35.465981&lng=139.622062
実際にこのURLをたたいた結果の一部はこのようになります。
{
"results": {
"api_version": "1.30",
"results_available": 310,
"results_returned": "1",
"results_start": 1,
"shop": [
{
"id": "J000243924",
"name": "鹿児島黒豚専門店 黒ぶたや ルミネ横浜店",
"logo_image": "https://imgfp.hotp.jp/IMGH/54/31/P022575431/P022575431_69.jpg",
"name_kana": "かごしまくろぶたせんもんてん くろぶたや るみねよこはま",
"address": "神奈川県横浜市西区高島2‐16‐1ルミネ横浜店7F",
"station_name": "横浜",
"ktai_coupon": 0,
"large_service_area": {
"code": "SS10",
"name": "関東"
},
"service_area": {
"code": "SA12",
"name": "神奈川"
},
"large_area": {
"code": "Z012",
"name": "神奈川"
},
"middle_area": {
"code": "Y135",
"name": "横浜"
},
"small_area": {
"code": "X270",
"name": "横浜駅"
},
"lat": 35.4661179661,
"lng": 139.6230811444,
"genre": {
"name": "居酒屋",
"catch": "横浜駅◆鹿児島県産の六白黒ぶたの専門店",
"code": "G001"
},
"sub_genre": {
"name": "和食",
"code": "G004"
},
"budget": {
"code": "B002",
"name": "2001~3000円",
"average": "3500円(通常平均)1500円(ランチ平均)"
},
"catch": "黒ぶたやのこだわり丼もの しゃぶしゃぶセット!",
"capacity": 58,
"access": "JR横浜駅直結!!ルミネの7階です★雨にぬれる心配もありません♪",
"mobile_access": "JR横浜駅東口徒歩1分 JR横浜駅直結!!ルミネの7階",
"urls": {
"pc": "https://www.hotpepper.jp/strJ000243924/?vos=nhppalsa000016"
},
"photo": {
"pc": {
"l": "https://imgfp.hotp.jp/IMGH/95/96/P042279596/P042279596_238.jpg",
"m": "https://imgfp.hotp.jp/IMGH/95/96/P042279596/P042279596_168.jpg",
"s": "https://imgfp.hotp.jp/IMGH/95/96/P042279596/P042279596_58_s.jpg"
},
"mobile": {
"l": "https://imgfp.hotp.jp/IMGH/95/96/P042279596/P042279596_168.jpg",
"s": "https://imgfp.hotp.jp/IMGH/95/96/P042279596/P042279596_100.jpg"
}
},
"open": "月~日、祝日、祝前日: 11:00~22:30 (料理L.O. 21:30 ドリンクL.O. 22:00)",
"close": "不定休(ルミネ横浜店に準ずる)",
"party_capacity": 30,
"other_memo": "ご要望などございましたらご相談ください",
"shop_detail_memo": "当日、宴会承ります!ご予約時間は17時~19時の間で承ります。※リニューアルにより内装が画像と異なります",
"budget_memo": "チャージ(お通し代) なし",
"wedding": "ご相談ください",
"course": "あり",
"free_drink": "あり :焼酎・フルーツサワー・ソフトドリンクなど、老若男女お楽しみ頂けるドリンクメニューをご用意♪",
"free_food": "なし :鹿児島県産の六白黒ぶたのしゃぶしゃぶ食べ放題をご用意いたします。",
"private_room": "なし :貸切ご利用も可能です。ご宴会にご利用ください。",
"horigotatsu": "なし :掘りごたつ",
"tatami": "なし :座敷あり",
"card": "利用可",
"non_smoking": "全面禁煙",
"charter": "貸切可 :貸切承ります!お問い合わせください。",
"parking": "あり :ルミネ駐車場、東口",
"barrier_free": "あり :バリアフリーあり",
"show": "なし",
"karaoke": "なし",
"band": "不可",
"tv": "なし",
"lunch": "あり",
"midnight": "営業していない",
"english": "あり",
"pet": "不可",
"child": "お子様連れOK :片側ソファのお席がございます。",
"wifi": "あり",
"coupon_urls": {
"pc": "https://www.hotpepper.jp/strJ000243924/map/?vos=nhppalsa000016",
"sp": "https://www.hotpepper.jp/strJ000243924/scoupon/?vos=nhppalsa000016"
}
}
]
}
}
実装例
上記を踏まえて、Spring Bootを用いてホットペッパーAPIを呼び出すコードを紹介していきます。
Controller層
package com.example.backend.controller;
import com.example.backend.exception.ShopNotFoundException;
import com.example.backend.output.ShopOutput;
import com.example.backend.service.ShopService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@CrossOrigin
@RestController
@RequestMapping("/shop")
public class ShopController {
private final ShopService shopService;
@Autowired
public ShopController(ShopService shopService) {
this.shopService = shopService;
}
/**
* GET /shop : 店舗の取得
* 横浜の居酒屋情報一覧を返すAPI
*
* @return successful operation (status code 200)
* or Invalid status value (status code 400)
* or Shop not found (status code 404)
*/
@GetMapping()
public ResponseEntity<List<ShopOutput>> getFindShops() {
List<ShopOutput> shopOutputs = shopService.findFindShops();
if (shopOutputs.isEmpty()) {
throw new ShopNotFoundException(); // 店舗が一つもない場合の例外処理
}
return ResponseEntity.ok()
.body(shopOutputs);
}
Controller層は主にクライアントから受け取った通信をService層に与える橋渡しのような役割を担っています。
Service層
package com.example.backend.service;
import com.example.backend.exception.ShopNotFoundException;
import com.example.backend.output.*;
import com.example.backend.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Transactional
@Service
public class ShopService {
private final HotPepperRepository hotPepperRepository;
@Autowired
public ShopService(HotPepperRepository hotPepperRepository) {
this.hotPepperRepository = hotPepperRepository;
}
/**
*店舗一覧をホットペッパーAPIをもちいて返す処理。
*
* @return List<ShopOutput> すべての横浜の店舗
*/
public List<ShopOutput> findFindShops() {
// ジャンルを検索用のコードに変更
String genre = "G001"; // 居酒屋の検索コード
// 検索場所を検索用コードに変更
// 横浜駅の緯度経度
String lat = "35.465981";
String lng = "139.622062";
// ホットペッパーAPIを用いて店舗の検索
Map<String, Object> hotPepperMap = hotPepperRepository.findShop(genre, lat, lng);
Map<String, Object> results = (Map<String, Object>) hotPepperMap.get("results");
// 店舗情報が取得できなかった場合
if((int) results.get("results_available") == 0) {
throw new ShopNotFoundException();
}
// 必要情報を取得
List<ShopOutput> shopOutputs = new ArrayList<>();
List<Map<String, Object>> shops = (List<Map<String, Object>>) results.get("shop");
for(Map<String, Object> shop : shops) {
ShopOutput shopOutput = new ShopOutput();
shopOutput.setShopId((String) shop.get("id"));
shopOutput.setShopName((String) shop.get("name"));
shopOutput.setShopAddress((String) shop.get("address"));
Map<String, Object> budgetMap = (Map<String, Object>) shop.get("budget");
shopOutput.setBudgetName((String) budgetMap.get("name"));
Map<String, Object> urls = (Map<String, Object>) shop.get("urls");
shopOutput.setUrl((String) urls.get("pc"));
shopOutput.setOpen((String) shop.get("open"));
shopOutputs.add(shopOutput);
}
return shopOutputs;
}
Service層では、データの処理を行っています。
まず、前処理を行い、Repository層に処理後のデータを渡しています。
そして、Repository層から受け取った値を展開し、必要な値のみをピックアップしています。
Repository層
package com.example.backend.repository;
import com.example.backend.exception.ShopNotFoundException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
@Repository
public class HotPepperRepository {
@Value("${hotpepper.api.key}")
private String hotPepperApiKey;
private final String baseUrl = "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/?format=json";
private final RestTemplate restTemplate;
@Autowired
public HotPepperRepository(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* ホットペッパーAPIを用いて店舗情報を取得する
*
* @param genre お店のジャンル
* @param lat 緯度
* @param lng 経度
*
* @return Map<String, Object> 店舗情報一覧
*/
public Map<String, Object> findShop(String genre, String lat, String lng) {
try {
ObjectMapper objectMapper = new ObjectMapper();
// 検索用のURLの作成
String url = String.format("%s&key=%s&genre=%s&lat=%s&lng=%s", baseUrl, hotPepperApiKey, genre, lat, lng);
// ホットペッパーAPIを用いて情報を取得
String hotPepperString = restTemplate.getForObject(url, String.class);
Map<String, Object> hotPepperMap = objectMapper.readValue(hotPepperString, Map.class);
return hotPepperMap;
} catch (JsonProcessingException e) {
throw new ShopNotFoundException();
} catch (Exception e) {
e.printStackTrace();
throw new ShopNotFoundException();
}
}
}
Repository層でホットペッパーAPIを用いた情報の取得を行っています。
ホットペッパーAPI(外部API)をSpring Boot上でたたくにはrestTemplate
を用います。
String hotPepperString = restTemplate.getForObject(url, String.class);
ここでは、restTemplate
のgetForObject
を使用し、引数にString.class
を使用することでホットペッパーAPIから取得した情報をString型で受け取っています。
※また、詳細な説明は省くのですが、restTemplate
でGETメソッドを実装するにはgetForObject
やgetForEntity
、exchange
を用いる方法があります。
String型で受け取った情報はObjectMapper
を用いてMap型に変更し、Service層に返しています。
ホットペッパーAPIをたたく際に生じた問題
ここからはrestTemplate
を用いてホットペッパーAPIをたたいた際に生じた問題とその解決方法を記載していきます。
問題① プロキシの設定
今回、実行環境の制約があり、以下のようなホットペッパーAPIから値が持ってこれないというエラーが発生しました。
エラー: リソースへのアクセスに失敗しました。 I/O error on GET request for "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/": webservice.recruit.co.jp
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/": webservice.recruit.co.jp
at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:915)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:895)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:790)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:431)
...
Caused by: java.net.UnknownHostException: webservice.recruit.co.jp
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:567)
...
この問題に対し、restTemplate
をカスタムしてプロキシ設定を追加することで解決しました。
この時のコードは以下のようになります。
package com.example.backend.config;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.core5.http.HttpHost;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
public class MyRestTemplateCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
// プロキシ設定
final HttpHost proxy = new HttpHost(
"my.proxy.host", // プロキシサーバのホスト
"my.proxy.port"); // プロキシサーバのポート
// HTTPクライアント設定
final HttpClient client = HttpClientBuilder.create()
.setProxy(proxy)
.build();
// HTTPクライアントファクトリを定義
final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
}
}
MyRestTemplateCustomizer.java
では、RestTemplateCustomizer
をインターフェースとしてMyRestTemplateCustomizer
を実装しています。
具体的には、customize
をオーバーライドし、プロキシ情報を追加しています。
また、このとき新たにclient5
とcore5
を依存関係に追加します。
今回のアプリではMavenを用いているため、pom.xml
ファイルに以下を追加しました。
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5</artifactId>
</dependency>
次に、カスタムしたrestTemplate
を以下のようにBean定義し、repository層でカスタム後のrestTemplate
を使えるようにしました。
package com.example.backend.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration(proxyBeanMethods = false)
public class RestTemplateConfiguration {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder, MyRestTemplateCustomizer customizer) {
return builder.customizers(customizer).build();
}
@Bean
public MyRestTemplateCustomizer myRestTemplateCustomizer() {
return new MyRestTemplateCustomizer();
}
}
以上でプロキシ設定が追加され、上記のエラーを解消することができました。
client5とcore5のバージョン不一致
client5とcore5のバージョンが一致していないと以下のエラーが発生しました。わたしはバージョンを指定しないことで解決したのですが、依存関係でバージョンを指定しないのは本来はあまりよろしくないらしいです。。
java.lang.NoSuchMethodError: 'org.apache.hc.core5.http.ClassicHttpResponse org.apache.hc.client5.http.classic.HttpClient.executeOpen(org.apache.hc.core5.http.HttpHost, org.apache.hc.core5.http.ClassicHttpRequest, org.apache.hc.core5.http.protocol.HttpContext)'
at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:99) ~[spring-web-6.1.13.jar:6.1.13]
at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70) ~[spring-web-6.1.13.jar:6.1.13]
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-6.1.13.jar:6.1.13]
at org.springframework.http.client.BufferingClientHttpRequestWrapper.executeInternal(BufferingClientHttpRequestWrapper.java:77) ~[spring-web-6.1.13.jar:6.1.13]
...
問題➁ SSL認証の設定
プロキシ設定が完了後、今度は下記のような異なるエラーが発生しました。
エラー: リソースへのアクセスに失敗しました。 I/O error on GET request for "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/": (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/": (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.springframework.web.client.RestTemplate.createResourceAccessException(RestTemplate.java:915)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:895)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:790)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:431)
...
Caused by: javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130)
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:388)
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:148)
...
この原因を調べてみるとSSL認証がされていないことでエラーが発生していました。
どうやらホットペッパーAPIを使用するにはSSL証明書をインポートする必要があるらしいです。
そこで、以下の手順で証明書をインポートしました。
- chromeで https://webservice.recruit.co.jp/hotpepper/gourmet/v1/ を開く。
- URLの横のボタン→この接続は保護されています→証明書は有効です→詳細→エクスポートでcrtファイルをダウンロード。
- 以下のコマンドを打ってファイルをインポートを行う。また、
your-alias-name
は任意の名前、path-to-cacerts
は${JAVA}/lib/security/cacerts
、path-to-your-certificate
はcrtファイルの保存場所。
keytool -import -alias <your-alias-name> -keystore <path-to-cacerts> -file <path-to-your-certificate>
※ 保存したSSL証明書を削除したいときは-delete
オプションを使用します。
また、本来行うのはよくないですが、下記のコードでSSL認証の無効化ができます。
package com.example.backend.config;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
public class MyRestTemplateCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
// SSLを無効化するためのTrustStrategyを定義
TrustStrategy trustStrategy = (chain, authType) -> true;
try {
// SSLの設定
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(trustStrategy)
.build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
// プロキシ設定
final HttpHost proxy = new HttpHost(
"my.proxy.host", // プロキシサーバのホスト
"my.proxy.port"); // プロキシサーバのポート
// HTTPクライアント設定
final HttpClient client = HttpClientBuilder.create()
.setProxy(proxy)
.setConnectionManager(connectionManager)
.build();
// HTTPクライアントファクトリを定義
final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
} catch (KeyManagementException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
}
}
}
補足:Springの基礎学習に用いた参考書
今回初めてSpringについて学び、アプリを作成していきましたが、Springの基礎知識習得には参考書を活用しました。
参考程度にどの本を利用したのかご紹介します。
スッキリわかるJava入門
Springを行う前にまずJavaもほぼ触ったことがない方やあまり自信がない方はここから!
Javaの入門。
Javaの文法からオブジェクト指向の考えまで一冊でかかれており、話し口調なので説明がわかりやすいのが特徴です。
スッキリわかるJava入門 実践編
スッキリわかるJava入門の続編。
基礎をもとにした実践の方法が記載されており、個人的には入門をしっかり読んで実践編はざっとよむぐらいで大丈夫な印象を持ちました。
プロになるためのSpring入門
Javaはわかるという方はこの本を!
Springを一から始めたいならこれ。
Springとはどういったものなのか、アノテーション、テストまで一冊の本で書かれており、すべての章でハンズオンができるので実践的に学ぶことができます。