天天看點

Sping之RestTemplate一 介紹二 正文三 例子五 bug參考

目錄

  • 一 介紹
  • 二 正文
  • 三 例子
  • 五 bug
  • 參考

一 介紹

預備知識:
  • HttpEntity含有headers和body資訊;子類ResponseEntity添加了狀态碼;子類RequestEntit添加了method和url資訊。
  • 都有對象的Builder類,友善建構對象。
  • 在一些HTTP用戶端庫的基礎上提供更高層的API,使得更容易的通路rest風格的http請求。
  • http請求用戶端、線程安全、同步請求
  • 底層基于JDK HttpURLConnection(預設)或Apache HttpComponents等等

二 正文

  • 方法:
    • 通用方法:

      exchange

      execute

      ,用于支援更少見的請求,可定制的功能更多(可定制化程度:

      execute

      >

      exchange

      ),一個比較底層的方法。
    • Rest方法:該類的方法被組織成rest方式的風格,命名方式有規律,部分方法如下所示:
      HTTP RestTemplate
      DELETE

      delete(String, String...)

      GET

      getForObject(String, Class, String...)

      HEAD

      headForHeaders(String, String...)

      OPTIONS

      optionsForAllow(String, String...)

      POST

      postForLocation(String, Object, String...)

      PUT

      put(String, Object, String...)

      • 基本上,方法名第一部分表示http請求方法,第二部分表示傳回值。
      • 基本上,第一個參數為url,接着(請求對象)request(if any),接着傳回對象類型(if any),接着第三個為url參數(if any)。
        • request參數一般為

          HttpEntity

          ,或者

          MultiValueMap

          (可寫入

          HttpEntity

          中),但不可為其他類型對象 也可為其他對象, 隻要存在對應轉化器(Converter)
        • 當不想要傳回值時,傳回類型可設為

          Void.class

      • 這些方法底層都是調用了

        doExecute

        方法實作的
  • 初始化:底層預設使用JDK的

    HttpURLConnection

    實作,也可在構造函數上切換其他HTTP庫。内置支援:
    • Apache HttpComponents
    • Netty
    • OkHttp
    使用Apache的例子:
  • URI:方法中第一個參數指定URI,它是一個模闆,支援路徑變量,方法的最後一個參數設定變量值,并且會自動進行url編碼。一個例子如下所示:
  • Headers:

    exchange

    方法可以設定請求頭部,也會擷取響應頭部。為啥rest方法不用設定呢?因為能自動推斷,是以通常情況下不用設定,見Body部分。
  • Body(消息體):請求消息體中填充對象或響應消息體中擷取對象,都是通過

    HttpMessageConverter

    完成的。通常情況下,你是不必顯示設定

    Content-Type

    Accept

    ,因為僅通過傳入的Java類型就可以确定轉換器,然後由轉換器自己設定這些頭字段。
    • 方法參數的請求對象

      Object

      的類型,可以确定序列化請求對象的轉換器,繼而轉換器确定

      Content-Type

    • 方法參數的結果對象類型

      responseType

      可以确定解析響應消息體的轉換器,繼而轉換器确定

      Accept

    • 當然可以通過

      exchange

      手動指定,這會影響使用的轉換器的選擇。
  • HttpMessageConverter:Spring為常用的MIME類型和Java類型注冊了一些預設的轉換器,至于該轉化器是否生效,取決于classpath下是否有對應的jar包。預設轉化器如下所示:
    MessageConverter Description

    StringHttpMessageConverter

    An

    HttpMessageConverter

    implementation that can read and write

    String

    instances from the HTTP request and response. By default, this converter supports all text media types (

    text*

    ) and writes with a

    Content-Type

    of

    application/octet-stream

    . You can override this by setting the

    supportedMediaTypes

    property and overriding

    getContentType(byte[])

    .

    MarshallingHttpMessageConverter

    An

    HttpMessageConverter

    implementation that can read and write XML by using Spring’s

    Marshaller

    and

    Unmarshaller

    abstractions from the

    org.springframework.oxm

    package. This converter requires a

    Marshaller

    and

    Unmarshaller

    before it can be used. You can inject these through constructor or bean properties. By default, this converter supports

    text/xml

    and

    application/xml

    .

    MappingJackson2HttpMessageConverter

    An

    HttpMessageConverter

    implementation that can read and write JSON by using Jackson’s

    ObjectMapper

    . You can customize JSON mapping as needed through the use of Jackson’s provided annotations. When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom

    ObjectMapper

    through the

    ObjectMapper

    property. By default, this converter supports

    application/json

    .

    MappingJackson2XmlHttpMessageConverter

    An

    HttpMessageConverter

    implementation that can read and write XML by usingJackson XML extension’s

    XmlMapper

    . You can customize XML mapping as needed through the use of JAXB or Jackson’s provided annotations. When you need further control (for cases where custom XML serializers/deserializers need to be provided for specific types), you can inject a custom

    XmlMapper

    through the

    ObjectMapper

    property. By default, this converter supports

    application/xml

    .

    SourceHttpMessageConverter

    An

    HttpMessageConverter

    implementation that can read and write

    javax.xml.transform.Source

    from the HTTP request and response. Only

    DOMSource

    ,

    SAXSource

    , and

    StreamSource

    are supported. By default, this converter supports

    text/xml

    and

    application/xml

    .

    BufferedImageHttpMessageConverter

    An

    HttpMessageConverter

    implementation that can read and write

    java.awt.image.BufferedImage

    from the HTTP request and response. This converter reads and writes the media type supported by the Java I/O API.
    從描述中,可以看到轉換器會自動設定頭字段。其中注意的是,請求體中直接轉入pojo對象,會被jackson轉化為JSON資料,如果想使用

    FormHttpMessageConverter

    生成前端表單post請求格式的資料,需要使用

    MultiValueMap

    類存儲資料。
    spring mvc(服務端)和RestTemplate(用戶端)中都用到了

    HttpMessageConverter

    RequestEntity

    /
  • form表單:表單的消息體由

    FormHttpMessageConverter

    轉換器進行轉換,當請求資料為MultiValueMap<String,?>類時會使用該轉換器,該類可以使用MultipartBodyBuilder更友善的建立出來。表單有兩種編碼方式:
    • application/x-www-form-urlencoded

      :當

      MultiValueMap

      的值(value,非key、非請求參數值)全為字元串時,轉換器會使用這種編碼方式。
    • multipart/form-data

      :其他情況都使用這種編碼方式。每個part都是一個HttpEntity,可能會有頭字段,如檔案須有

      Content-Type

      ,但一般不必手動設定,每個part的轉換器會自動設定,如下面的例子所示:
      MultipartBodyBuilder builder = new MultipartBodyBuilder();
      builder.part("fieldPart", "fieldValue");
      builder.part("filePart", new FileSystemResource("...logo.png"));
      builder.part("jsonPart", new Person("Jason"));
      
      template.postForObject("https://example.com/upload", builder.build(), Void.class);
                 
      上面的序列化檔案時,使用的,,是哪個轉換器??不知道,,反正使用的對象必須有對應的轉換器存在才行!

三 例子

測試時所用的,全貼下來了,,

@RunWith(SpringRunner.class)
@SpringBootTest
public class BlogApplicationTests {

    

    static class A{
        int a;
        int b;

        public int getA() {
            return a;
        }

        public void setA(int a) {
            this.a = a;
        }

        public int getB() {
            return b;
        }

        public void setB(int b) {
            this.b = b;
        }
    }



    @Test
    public void test3(){
        A a=new A();
        a.setA(1);
        a.setB(2);
        new RestTemplate().postForObject("http://localhost:8080/image/test",a,Void.class);
    }
    @Test
    public void test4(){
        /*MultiValueMap<String,Object> map=new LinkedMultiValueMap<>();
        map.add("a","a");
        map.add("b","23");*/
        MultipartBodyBuilder builder=new MultipartBodyBuilder();
        builder.part("a","a");
        builder.part("b","b");
        builder.part("file",new FileSystemResource("C:\\Users\\Administrator\\Pictures\\王通.jpg"));

        new RestTemplate().postForObject("http://localhost:8080/image/test3",builder.build(),Void.class);

    }
    @Test
    public void test5(){
        Map<String,Object> map=new HashMap<>();
        map.put("a","aa");
        map.put("b",23);

        new RestTemplate().postForObject("http://localhost:8080/image/test2",new HttpEntity<Map>(map),Void.class);
    }

    @Test
    public void contextLoads() throws IOException {
        RestTemplate restTemplate = new RestTemplate();
        String url
                = "http://localhost:8080/user/login";
        MultiValueMap<String,String> param= new LinkedMultiValueMap<>();
        param.add("username","千霜");
        param.add("password","123456");

        HttpHeaders headers=new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Map> entity=new HttpEntity<>(param,headers);

        ResponseEntity<Status> responseEntity=restTemplate.postForEntity(url,entity,Status.class);
        Status status=responseEntity.getBody();
        System.out.println(status);
    }



    @Test
    public void test2() throws FileNotFoundException, URISyntaxException {
        MultipartBodyBuilder builder=new MultipartBodyBuilder();
        builder.part("file",new FileSystemResource("C:\\Users\\Administrator\\Pictures\\王通.jpg"));

        Status status=new RestTemplate().postForObject("http://localhost:8080/image/upload",builder.build(),Status.class);
        System.out.println(status);
    }
    //new FileInputStream("C:\\Users\\Administrator\\Pictures\\王通.jpg")
}
           

五 bug

使用過程中發現了點spring boot的bug,就是必須添加了如下依賴後,一些功能才能使用:

<dependency>
	<groupId>org.reactivestreams</groupId>
	<artifactId>reactive-streams</artifactId>
	<version>1.0.2</version>
</dependency>
           

見issue12579

參考

  • REST Endpoints office doc
  • RestTemplate office post
  • RestTemplate Baeldung
  • MIME
  • on Basic Auth with RestTemplate:進階閱讀,儲存憑證credentials