他のサーバへのリクエスト機能の実装


今日SpringサーバからPythonサーバにリクエストを送信します.
一人で協力する仮定の下で...シミュレーションをします!

🚗目次


  • サーバ間の関係の理解

  • Gitブランチの作成

  • テストの準備

  • 製品コントローラMvcテスト

  • DBTest-selectテスト

  • Git Merge
  • 🧐サーバ間の関係の理解


    まずサーバー間の関係を見てみましょう.
  • ユーザーが検索を行います.
  • Spring Serverは、データベースを検索値でクエリーします.
  • DBにデータが存在する場合、ユーザに直接応答する.ない場合は、PythonサーバにHttpリクエストを送信します.
  • リクエストを受信したPythonサーバは、データを収集します.
  • Python Serverは、収集したデータをデータベースに挿入します.
  • Insertが完了すると、Javaサーバに「check:true」が送信されます.
  • Javaサーバは、ユーザデータに応答するためにDBを再クエリします.
  • ここでは、データベースをクエリーします.データがなければ、PythonサーバにHttpリクエストを送信します.

    🧐Gitブランチの作成


    まずGitブランチを作成します.
    機能実装では、「feature/request to python server with http-chanYoungho」が生成されます.

    🌈ブランチの作成


    Developブランチを除去し、ブランチを作成します.


    🌈Upstream設定



    🌈git checkout



    いいですね.今からコーディングを始めましょう

    🧐テストの準備


    まず、テストで実現する内容を見てみましょう.
  • ユーザー要求がコントローラに正しく送信されたかどうかをテストします.
  • ユーザの要求がデータベースによって正常に選択されたかどうかをテストする
  • .
  • DBのデータに異なる論理があるかどうかをテストします.

    🌈testクラスの作成

    * Test를 Controller 테스트, Select & Request 테스트 2개로 한다.
    * Controller 테스트 : ProductControllerTest.java
    * Select & Request 테스트     : DBTest.java

    🌈test正常運転確認!

    *ProductController.java - void queryParameterTest(){}
    
    ![](https://media.vlpt.us/images/jkijki12/post/f3f32ac2-2d50-4d03-af59-ba83726576ae/1.PNG)
    
    * DBTest.java - void getData(){}
    
    ![](https://media.vlpt.us/images/jkijki12/post/4d251785-d70f-41a8-b2d1-87568cedd0d0/3.PNG)
    各テストを順番に見ましょう.

    🧐ProductControllerTest


    このテストはMVCを駆動する必要がある.だからMockMvcを利用しよう!
    コードは次のとおりです.
    
    @SpringBootTest
    @AutoConfigureMockMvc
    public class ProductControllerTest {
    
    
        @Autowired // Autowired 없으면 아래에 있는 BeforeEach로 MockMvc를 생성해주어야 한다.
        private MockMvc mockMvc;
        
        
        /*
        @BeforeEach로 MockMvc를 생성할 경우, 
        해당 컨트롤러에 존재하는 @Autowired 객체를 제대로 못읽어온다.
        */
        
        /*
        @BeforeEach //테스트 시작하기 전에 기초작업
        public void before(){  // 보통 before, setup을 메서드명으로 사용한다.
    
            // mock 컨트롤러를 생성해야한다.
            mockMvc =
                    MockMvcBuilders
                    .standaloneSetup(ProductController.class) // 테스트를 진행할 Controller를 입력
                    .alwaysExpect(status().isOk()) // 필수 조건, 굳이 없어도 될 듯?
                    .build();
    
        }
        */
        
        //본격 테스트 메서드
        @Test
        public void queryParameterTest() throws Exception{
    
            mockMvc.perform( // 컨트롤러를 호출해야한다.
                    MockMvcRequestBuilders  // 보낼 Request를 생성한다.
                    .get("/products?query=바나나우유") // get 메서드를 이용한다.
                    .accept(MediaType.ALL)) // 모든 타입을 받는다.
                    // 여기부터는 받는 값을 테스트한다. status : ok를 예상, 위에서 필수 조건으로 ok 넣어서 무조건 통과할 듯
            .andExpect(status().isOk())
                    // 받는 Response의 body에 content를 json으로 고쳐서 받는 값이랑 비교한다.
                    .andExpect(content().json("{\"query\":\"바나나우유\"}"));
    
    
    
        }
    };
    
    もちろん失敗します.どうしたんですか.(実際には、コメントを抜き、コードを正しくリストする必要があります.)
    ProductControllerには「/pruducts?query=バナナミルク」のマッピングがないから!
  • ProductControlにマッピングし、論理を追加します.
  • @RestController
    @Slf4j
    @RequiredArgsConstructor
    public class ProductController {
    
    
        //이전 테스트에서 사용했던 Service
        @Autowired
        private final ProductService productService;
    
    
    
        @GetMapping("/products") // 해당 url로 get 매핑한다.
        @ResponseBody // body에 return 값을 입력하여 Response 한다.
        public HashMap<String, String> getProduct(@RequestParam(value = "query", required = false) String query){
            // 쿼리 파라미터로 넘어온 "query"를 @RequestParam으로 받는다.
    
            HashMap<String, String> map = new HashMap<>();
            map.put("query", query); //map으로 받는다.
    
            return map; // 그대로 리턴한다. -> jackson이 json으로 수정
        }
    };
    

    成功!!
    jsonに正しく変換します.

    🧐DBTest-selectテスト


    受信したクエリーでデータを選択するテストを行います.
    さらに理解が必要な場合は以下の通りです.
    データベースから「クエリー」を選択します.
    選択した場合、データがある場合、リストはNULLではありません.
    選択すると、データがない場合は、Httpメッセージが生成され、他のサーバに送信されます.
    対応する検索語を渡す.その後、他のサーバは「check」:「true」}に応答します.
    (別のサーバが作成されました)->その他のサーバによる記事の生成
    DBTest.Javaですべてが存在すると仮定してコードを記述する方法を作成しましょう.

    🌈テストの作成


    DBTest.JAva生成とテスト実施
    @SpringBootTest
    public class DBTest {
    
        @Autowired
        private ProductService productService;
    
        @Test
        public void getData(){
    
            //given
            String query = "바나나우유";
    
            List<Product> list = productService.findByDataType(query);
    
            //when
    
            if(list.isEmpty()){
                CommunicatePython communicatePython = new CommunicatePython();
                HashMap<String, String> map = communicatePython.createHttpRequestAndSend(query);
                Assertions.assertEquals("true", result);
            }else{
                Assertions.assertNotNull(list);
            }
        }
    
    };
    
    テストを見てみましょう.
    1.クエリーを指定します.
    2.検索語でDBで選択し、リストに入れる.
    リストがNULLの場合、PythonサーバにDB INSERTリクエストを発行します.
    4.PythonサーバがINSERTを完了したら、{"check":"true"}をjsonとして応答します.
    5.assertEqualsで「true」をテストします.
    6.リストがNULLでない場合、assertNotNullを使用してリストがNULLであるかどうかをテストします.
    はい、いつですか.その时はよく知りませんでした.私は国語が話せません.
    もちろん失敗します.エラーの原因はSymbolです.
    では、必要な相手を記入しましょう!!

    🌈Go To Symbolの作成


    🔎ProductService.Javaの変更

    @Service
    @RequiredArgsConstructor
    public class ProductService {
    
        @Autowired
        private final ProductRepository productRepository;
    
        public List<Product> findByDataType(){
            List<Product> productList = productRepository.findByDataType();
    
            return productList;
        }
    };

    🔎CommunicatePython.Javaの作成と実装

    
    public class CommunicatePython {
    
        public void  createHttpRequestAndSend(String query){
    
            //필요한 객체 생성
            RestTemplate restTemplate = new RestTemplate();
    
            //body 생성
            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("query", query);
    
            //header 생성
    
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
    		
            // header, body 합치기
            HttpEntity<MultiValueMap<String,String>> entity = new HttpEntity<>(params, headers);
    		
            // POST 요청 보내고, 응답값을 JSONObject로 받기
            JSONObject jsonObject = new JSONObject(restTemplate.postForObject("http://python서버 ip:포트번호/get_data", entity, String.class));
            
            // Json 파싱
            String result = jsonObject.getString("check");
            return result;
            
        }
    
    };
    
    HTTPを生成しPythonサーバに送信する.そして回答料を受け取ります.
    テストしてみましょう.
    間違いが爆発した.

    もちろんです.
    サービス中のRepositoryにfindByDataType()メソッドは存在しないから!
    では、Repositoryに行ってfindByDataType()を作成しましょう.

    🔎ProductRepository.Javaの変更

    
    @Repository
    public interface ProductRepository extends JpaRepository<Product, Long> {
    
        List<Product> findByDataType(String data_type);
    };
    
    いくつかのエラーのため、メソッド名はすべて変更されました.
    Data_type -> DataType
    Spring Data JPA参考になりました!
    もう一度テストします.
    うん.間違いがあった...理由は.

    このリクエストは拒否されました.
    他のサーバーをつけずに回りました.サーバーを起動しない

    サーバーを実行し、テストします.

    成功
    他のサーバもqueryパラメータを正しくスキップしました.

    🔎データがあれば


    現在mysqlには「緑茶」のデータがあります.

    テストでqueryを修正して、もう一度テストします!

    テストに合格しました!

    🧐Git Merge


    機能が実現しました.
    提出する
    「開発」に移動
    git checkout develop
    merge
    git merge features/request_to_python_server_with_http-ChaYoungHo

    マージ完了

    📋の最後の部分


    2台のサーバをテスト中に多くのエラーが発生しました.経験した誤りは以下の通りである.
  • MockMvcのConstructor問題
  • Spring Data Jpa-Naming問題(Camel->Snake)
  • このエラーに関する記事をアップロードしましょう!!

    🧷Reference

  • https://tech.devgd.com/12
  • https://n1tjrgns.tistory.com/221