中文 English

Feign usage examples: @Body annotation, http request body

Published: 2021-03-05
javascript feign openfeign demo json

There is a situation where the interface uses the http request body to pass parameters, so I studied feign and found that the @Body annotation can generally be implemented. Although it is said that the request body is used to pass parameters, in fact, the request body still contains data in json format. Of course, if you don’t want to be limited to the json format, you can set it to a universal format. See the end of the article for details.

        <dependency><!-- 更容易的调用第三方接口 -->
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
            <version>11.0</version>
        </dependency>
        <dependency><!-- 更容易的调用第三方接口 -->
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>11.0</version>
        </dependency>
        <dependency><!-- 更容易的调用第三方接口 -->
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-gson</artifactId>
            <version>11.0</version>
        </dependency>

OpenFeign client initialization code

        Feign.builder()
                .client(new OkHttpClient())
                .encoder(new GsonEncoder())
                .decoder(new GsonDecoder())
                .logger(new Slf4jLogger())
                .logLevel(feign.Logger.Level.FULL)
                .target(ApplyV3Api.class, server);

Let’s start with a simple example

Use the request body in predefined template mode

Use the @Body annotation to define the template. The client sends a request to the server through feign. Client code:

interface FeignClientAPI {
	@RequestLine("POST /jsonBody")
	@Headers("Content-Type: application/json")
	//因为'{}'在模板中有特殊作用(占位符),所以json的首尾中括号需要转义。'{'=>'%7B','}'=>'%7D'
	@Body("%7B\"id\": \"{id}\", \"name\": \"{name}\"%7D") 
	String jsonBody(@Param("id") String id, @Param("name") String name);
}
```java
//Client call
feignClient.jsonBody("xxId", "xxName");

服务端的代码:

@RequestMapping("/jsonBody")
@ResponseBody
public Object jsonBody(@RequestBody JSONObject body){
	System.out.println(body);
	return "success";
}

看了上面一个例子,你会感觉很鸡肋,这json还要预定义,这怎么玩嘛。

使用参数模板方式的请求体

使用String作为方法参数

上面例子我们知道,@Body注解支持"{}“形式的占位符,会将方法中的参数填充到模板中,嘿嘿嘿,我们直接将参数当做模板如何,见下 客户端的代码:

interface FeignClientAPI {
	@RequestLine("POST /jsonBody")
	@Headers("Content-Type: application/json")
	@Body("{body}")
	String jsonBody(@Param("body") String body);
}

发现区别了吗,我们直接给@Body注解定义了一个”{body}"。 那么客户端调用时,相应的传参就不是一个个字段了,而是一整个json。

//Client call
JSONObject body = new JSONObject();
body.put("id", "xxId");
body.put("name", "xxName");
feignClient.jsonBody(body.toJSONString());

服务端的代码不需要变化。

Using Bean as Method Parameter (toString)

Using String for Feign method parameters always feels inconvenient. After checking the code (see feign.template.Expressions.SimpleExpression#expand(java.lang.Object, boolean)), I found that you can also pass an object parameter directly. However, the object must override toString, because the internal implementation converts the object to a string using toString.

Client code:

interface FeignClientAPI {
	@RequestLine("POST /jsonBody")
	@Headers("Content-Type: application/json")
	@Body("{body}")
	String jsonBody(@Param("body") InitBean bean);
}

@lombok.Data
public class InitBean {
	private String id;
	private String name;

	public String toString() {
		return com.alibaba.fastjson.JSON.toJSONString(this);
	}
}

When the client calls, pass the Bean directly:

// Client call
InitBean bean = new InitBean().setId("xxId").setName("xxName");
feignClient.jsonBody(bean);

The server code does not need to change.

Go one step further and combine the request body with other parameter types

The following three extension examples each include client code and server code. The client call code is omitted because it should be self-explanatory.

/***************** Extension 1: Request body + REST-style URL ******************/
// Client code
@RequestLine("POST /jsonBody/{path}")
@Headers("Content-Type: application/json")
@Body("{body}")
String jsonBodyAndPath(@Param("body") String body, @Param(value = "path") String path);

// Server code
@RequestMapping("/jsonBody/{path}")
@ResponseBody
public Object jsonBodyAndPath(@RequestBody JSONObject body, @PathVariable String path) {
	System.out.println(body);
	System.out.println(path);
	return "success";
}
/***************** Extension 2: Request body + request parameters ******************/
// Client code
@RequestLine("POST /jsonBodyWithParam")
@Headers("Content-Type: application/json")
@Body("{body}")
String jsonBodyWithParam(@Param("body") String body, @QueryMap Map map);

// Server code
@RequestMapping("/jsonBodyWithParam")
@ResponseBody
public Object jsonBodyWithParam(@RequestBody JSONObject body, @RequestParam String param) {
	System.out.println(body);
	System.out.println(param);
	return "success";
}
/***************** Extension 3: Request body + REST-style URL + request parameters ******************/
// Client code
@RequestLine("POST /jsonBodyAndPathWithParam/{path}")
@Headers("Content-Type: application/json")
@Body("{body}")
String jsonBodyAndPathWithParam(@Param("body") String body, @Param(value = "path") String path, @QueryMap Map map);

// Server code
@RequestMapping("/jsonBodyAndPathWithParam/{path}")
@ResponseBody
public Object jsonBodyAndPathWithParam(@RequestBody JSONObject body, @PathVariable String path, @RequestParam String param) {
	System.out.println(body);
	System.out.println(path);
	System.out.println(param);
	return "success";
}
I hope the layout of the above code will not confuse you. Writing it all together is a bit cramped, but it works.

# Expanding to a non-JSON request body
The examples above all assume that the request body is JSON. What if the request body is not JSON, for example XML?
Here I simply changed the client request header to `@Headers("Content-Type: text/plain;")`, which means the request body is plain text, as shown below:

```java
@RequestLine("POST /stringBody")
// Set the request body to plain text
@Headers("Content-Type: text/plain;")
@Body("{body}")
String stringBody(@Param("body") String body);

Correspondingly, the server code uses String as the receiving parameter to capture the request body content. After the server receives the string, it converts it to the corresponding type.

@RequestMapping("/stringBody")
@ResponseBody
public Object stringBody(@RequestBody String body) throws DocumentException {
	System.out.println(body);
	// string to json
	JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(body);
	// or string to xml
	Document document = org.dom4j.DocumentHelper.parseText(body);
	// or other...
	return "success";
}

Basically this is the approach above. I have not found another implementation yet. If you know a better way, feel free to share it.

Reference article

https://blog.csdn.net/qq_31772441/article/details/100176834 https://blog.csdn.net/sinat_36553913/article/details/104469418 https://blog.csdn.net/sinat_36553913/article/details/104527072 https://www.jianshu.com/p/0834508b7a6d