The introduction
Recently, due to business needs, it is necessary to access an interface of Ali Cloud. After opening the document, I have a look at this interface. It can be done in an hour or more than a simple visual inspection, but the access process is still bumpy. First OF all, I looked at the example he gave. First, I downloaded the demo recommended by Ali Cloud document, ran its example, and replaced several necessary parameters such as the secret key. These secret key general company will have full-time personnel and Ali cloud to docking, as long as you are responsible for the tube he wants to go. However, there are also companies need to docking Ali Cloud. Speaking of which, I would like to make fun of it. When docking ali Cloud, the technical support group is Dingding, so it is necessary to download a Dingding for their support. Inexplicably, there is a need to install more software on the computer. Getting too far, we still go back to the topic, download the demo, and then replace the corresponding secret key and other parameters, and then run the demo to see if it can return the results normally. This step is mainly to ensure that the secret key and other parameters given by the product are correct. If we can drop the interface, that means there’s nothing wrong with the parameters and then we can start writing business code. Access ali cloud two factor authentication market.aliyun.com/products/57… Download the demo from the official website and run it. The sample is fairly simple, which is to package an Apache httplcient class with a large chunk of code. I still use Feign to call because feign code is clean and tidy. Although the underlying implementation is also done through HttpClient, the implementation is nonsensical to me because the business code looks clean and tidy. The demo is as follows:
public static void main(String[] args) {
String host = "https://safrvcert.market.alicloudapi.com";
String path = "/safrv_2meta_id_name/";
String method = "GET";
String appcode = "Your own AppCode";
Map<String, String> headers = new HashMap<String, String>();
/ / the last in the header format (the middle English space) for Authorization: 83359 fd73fe94948385f570e3c139105 APPCODE
headers.put("Authorization"."APPCODE " + appcode);
Map<String, String> querys = new HashMap<String, String>();
querys.put("__userId"."__userId");
querys.put("customerID"."customerID");
querys.put("identifyNum"."identifyNum");
querys.put("identifyNumMd5"."identifyNumMd5");
querys.put("userName"."userName");
querys.put("verifyKey"."verifyKey");
try {
/** * Important tips are as follows: * HttpUtils from * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtil S.j ava download * * * corresponding rely on refer to * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml * /
HttpResponse response = HttpUtils.doGet(host, path, method, headers, querys);
// See the x-ca-error-message field for Error information
System.out.println(response.toString());
// Get the body of response
System.out.println(EntityUtils.toString(response.getEntity()));
} catch(Exception e) { e.printStackTrace(); }}Copy the code
HttpResponse response = HttpUtils.doGet(host, path, method, headers, querys);
Copy the code
HttpClient: httpUtils httpClient: httpUtils httpClient: httpUtils httpClient: httpUtils httpClient: httpUtils httpClient: httpUtils httpClient: httpUtils httpClient:
@FeignClient(name = "verifyIdCardAndNameFeignClient", url = "https://safrvcert.market.alicloudapi.com")
public interface VerifyIdCardAndNameFeignClient {
@RequestMapping(value = "/safrv_2meta_id_name/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
Response verifyIdCardAndNameMap(@RequestParam Map<String,String> app, @RequestHeader("Authorization") String authorization);
Copy the code
Compare that to the following oneHttpClientUtils
Is the code relatively cleanAccording to thisdemo
The function is indeed realized, to tell the truth, I still do not like to usemap
As a parameter,map
As an input parameter, the parameter depends on the guessing readability and maintainability is a bit poor, I still used to encapsulate onejavaBean
As an entity. The Ali document actually mentions a little bit, although it only mentions the data query layer.Let’s change the request parameter to onejavaBean
, the changed code
@RequestMapping(value = "/safrv_2meta_id_name/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
Response verifyIdCardAndNameDTO(@RequestBody AliyunVerifyIdCardAndNameReq app, @RequestHeader("Authorization") String authorization);
Copy the code
The request was not successful, and the parameters were not received. We are aGET
The way the request was made and then the parameter was passed to the entity causing it not to be received.feignClient
Does not supportget
Method to pass entity classes? Later, a note was found after searching the data@SpringQueryMap
Let’s put the above code@RequestBody
replace@SpringQueryMap
Perfect solution to this problem
@SpringQueryMap
inSpring cloud 2.1 x
The above version provides a new annotation @SpringQueryMap
Why does this annotation help us do that? There are no secrets under the source code, we can turn it overfeign
The source code should be relatively simple, we can simply look at the source code. Look at the source code is also don’t know where to look, from the beginning to see the end is certainly not realistic, don’t start from the beginning, and don’t know where the source code, there is a very simple way we directly take this annotation global search, see where it is used, in each place hit a breakpoint tryWell, we did a global search and found that it’s mostly used inQueryMapParameterProcessor
In this class. So we can try to create a breakpoint in this class.
/ * * * {@link SpringQueryMap} parameter processor.
*
* @author Aram Peres
* @see AnnotatedParameterProcessor
*/
public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<SpringQueryMap> ANNOTATION = SpringQueryMap.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int paramIndex = context.getParameterIndex();
MethodMetadata metadata = context.getMethodMetadata();
if (metadata.queryMapIndex() == null) {
metadata.queryMapIndex(paramIndex);
metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded());
}
return true; }}Copy the code
We found that this class will be loaded when the container is started and the processArgument method will be executed. Let’s ignore this method and look at where Feign actually calls to find the SynchronousMethodHandler#invoke method
public RequestTemplate create(Object[] argv) {... Omit some code/ / metadata. QueryMapIndex () method is QueryMapParameterProcessor # processArgument assignment
if(metadata.queryMapIndex() ! =null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
// Get the object that needs special processing by subscript, there is a problem that only handles the first @SpringQueryMap annotation of the method argument,
/ / reason is QueryMapParameterProcessor # processArgument this method only the first subscript assignment, then here will take the first subscript, so can only deal with the first @ SpringQueryMap annotation
Object value = argv[metadata.queryMapIndex()];
The FieldQueryMapEncoder class is the default parser class so it's not going to parse the parent class's parameters, To resolve the arguments of the parent class we need to specify QueryMapEncoder as FieldQueryMapEncoder in feign Config
Map<String, Object> queryMap = toQueryMap(value);
// The concatenated object is the URL parametertemplate = addQueryMapQueryParameters(queryMap, template); }... Omit some code}Copy the code
The above code logic is quite understandable
- First decide if you need to deal with it
querymap
- Objects requiring special treatment are obtained by subscripting
- Convert the object to
map
(There is a pit that does not resolve fields of the parent class by default.) - Stitching additional
map
tourl
In the
conclusion
- Through the above
@SpringQueryMap
Annotations implementget
Pass parameters, but if you need to pass more than one@SpringQueryMap
How can we implement annotations? - Or we could make one of our own
SpringQueryMap
How can we achieve this? @SpringQueryMap
By default, annotations do not parse the arguments of the parent class. If you need to parse the arguments of the parent class, you need to modify themFeign
theconfig
#QueryMapEncoder
forFieldQueryMapEncoder
.- If we implement one ourselves
AnnotatedParameterProcessor
All defaultPathVariableParameterProcessor
,
RequestParamParameterProcessor fails, RequestHeaderParameterProcessor, QueryMapParameterProcessor, Why does it fail? Let’s look at SpringMvcContract. So the custom AnnotatedParameterProcessor need careful.
The end of the
- As a result of their talent and learning, it is inevitable that there will be mistakes, if you found the wrong place, but also hope that the message to me pointed out, I will correct it.
- If you think the article is good, your forwarding, sharing, appreciation, like, message is the biggest encouragement to me.
- Thank you for reading. Welcome and thank you for your attention.