Spring Boot 發起 HTTP 請求

起步

新年目標Spring Cloud開始實施,打開慕課網。html

clipboard.png

剛學了一章,大致就是調用中國天氣網的api,使用Spring Boot構建本身的天氣預報系統,而後使用Spring Cloud,一步一步使用微服務的思想來演進架構。java

小目標spring

昨天去百度搶了新年紅包,感嘆百度的高併發作的也是如此優秀。apache

阿里的雙十一,百度的新年。(發現了兩者的共同點,可能也是解決併發的一種思路,併發的時候只容許增長數據。)json

感嘆歸感嘆,指望着學習完Spring Cloud也能設計出優秀的架構,解決併發的一些問題。api

遇到的問題

學習時也跟着課程進行編碼,講師講的很是好,可是本課程的重點是後面的微服務架構,因此前面的功能有一些瑕疵,特此提出本身的實現,供你們學習交流。架構

功能描述

最初的功能很簡單,由於後臺是沒有任何數據的,因此前臺有請求,就直接去天氣網要數據,而後再返回去。併發

clipboard.png

數據序列化問題

這是天氣網api返回來的數據格式,乍一看沒啥毛病。mvc

{
    "data": {
        "yesterday": {
            "date": "4日星期一", 
            "high": "高溫 26℃", 
            "fx": "無持續風向", 
            "low": "低溫 18℃", 
            "fl": "<![CDATA[<3級]]>", 
            "type": "多雲"
        }, 
        "city": "深圳", 
        "forecast": [
            {
                "date": "5日星期二", 
                "high": "高溫 25℃", 
                "fengli": "<![CDATA[<3級]]>", 
                "low": "低溫 18℃", 
                "fengxiang": "無持續風向", 
                "type": "多雲"
            }, 
            {
                "date": "6日星期三", 
                "high": "高溫 26℃", 
                "fengli": "<![CDATA[<3級]]>", 
                "low": "低溫 17℃", 
                "fengxiang": "無持續風向", 
                "type": "多雲"
            }, 
            {
                "date": "7日星期四", 
                "high": "高溫 27℃", 
                "fengli": "<![CDATA[<3級]]>", 
                "low": "低溫 18℃", 
                "fengxiang": "無持續風向", 
                "type": "多雲"
            }, 
            {
                "date": "8日星期五", 
                "high": "高溫 26℃", 
                "fengli": "<![CDATA[<3級]]>", 
                "low": "低溫 17℃", 
                "fengxiang": "無持續風向", 
                "type": "多雲"
            }, 
            {
                "date": "9日星期六", 
                "high": "高溫 24℃", 
                "fengli": "<![CDATA[<3級]]>", 
                "low": "低溫 14℃", 
                "fengxiang": "無持續風向", 
                "type": "小雨"
            }
        ], 
        "ganmao": "相對今天出現了較大幅度降溫,較易發生感冒,體質較弱的朋友請注意適當防禦。", 
        "wendu": "23"
    }, 
    "status": 1000, 
    "desc": "OK"
}

缺點1:有拼音;ganmaowenduapp

缺點2:名稱不一致;理論上來講yesterdayforecast應該是同一個實體,都表示一天的天氣狀況,只是名稱不一樣。可是在yesterday中,風向和風力是fxfl,在forecast中,名稱倒是fenglifengxiang

解決此問題,想到的思路就是使用jackson進行序列化與反序列化時進行配置的一些註解。

最初使用此種方法實現:

@JsonProperty("wendu")
private Float temperature;

一個對象中的名字,一個json數據中的名字。

能夠實現,可是很差。

舉個例子,天氣api返回給我wendu,添加了@JsonProperty,而後wendu就綁定到了temperature上,可是若是我前臺再返回該對象,序列化後生成的名稱仍是wendu。很差!

目標是實現,反序列化時:從wendu能綁定到個人temperature,序列化時直接使用個人字段名。

get、set嘗試

猜想是否是和getset方法有關。

就把@JsonProperty("wendu")添加到set方法上,發現並無用。

JsonAlias

後來通過查詢,原來是註解用錯了,此種狀況應使用別名。

關於JsonPropertyJsonAlias的詳細講解,請參考Jackson @JsonProperty and @JsonAlias Example

@JsonAlias("wendu")
private Float temperature;

同時,能夠應用多個別名:

@JsonAlias({"fengli", "fl"})
private String windForce;

發起請求

發起請求的示例代碼,供之後參考。

@Autowired
private RestTemplate restTemplate;

@Override
public Weather getWeatherByCityName(String cityName) {
    return this.getWeatherByUrl(BASE_URL + "?" + CITY_NAME + "=" + cityName)
            .getData();
}

private Response getWeatherByUrl(String url) {
    // 發起Get請求
    ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
    // 若是狀態碼非200, 拋異常
    if (response.getStatusCodeValue() != 200) {
        throw new YunzhiNetworkException("數據請求失敗");
    }
    // 實例化對象映射對象
    ObjectMapper mapper = new ObjectMapper();
    // 初始化響應數據
    Response data;
    // 從字符串轉換爲Response對象
    try {
        data = mapper.readValue(response.getBody(), Response.class);
    } catch (IOException e) {
        throw new YunzhiIOException("json數據轉換失敗");
    }
    // 返回
    return data;
}

RestTemplate配置

這裏與正常的RestTemplate構建有些不一樣,一般的RestTemplate是使用Spring工具類構造的,此處使用ApacheHttp組件構造,以支持更多的數據格式。

implementation 'org.apache.httpcomponents:httpclient'

同時去除了默認的對StringHttp消息轉換器,默認的轉換器使用的不是UTF-8編碼。

講師原文章:Spring RestTemplate 調用天氣預報接口亂碼的解決

@Configuration
public class BeanConfiguration {

    @Bean
    public RestTemplate restTemplate() {
        // 使用Apache HttpClient構建RestTemplate, 支持的比Spring自帶的更多
        RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
        // 去除默認的String轉換器
        restTemplate.getMessageConverters().removeIf(converter -> converter instanceof StringHttpMessageConverter);
        // 添加自定義的String轉換器, 支持UTF-8
        restTemplate.getMessageConverters()
                .add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
        return restTemplate;
    }
}

更完善的單元測試

同時,在編寫單元測試的時候,看了一篇關於AssertJ的文章。Testing with AssertJ assertions - Tutorial

以前學Junit5的時候,以爲這個東西挺好使的啊?爲何被開源社區拋棄而使用AssertJ呢?

原來以前用的斷言都太簡單,其實AssertJ遠比咱們使用的更強大。

@Test
public void getWeatherByCityName() throws Exception {
    final String cityName = "深圳";
    MvcResult mvcResult = this.mockMvc
            .perform(MockMvcRequestBuilders.get(BASE_URL + "/cityName/" + cityName))
            .andExpect(MockMvcResultMatchers.status().isOk())
            .andReturn();
    String json = mvcResult.getResponse().getContentAsString();
    Assertions.assertThat(json)
            .contains("cityName", "cold", "temperature", "windDirection", "windForce")
            .doesNotContain("ganmao", "wendu", "fx", "fl", "fengxiang", "fengli");
}

總結

多看英文文章, Tutorial寫得都特別好。
相關文章
相關標籤/搜索