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 oneHttpClientUtilsIs the code relatively cleanAccording to thisdemoThe function is indeed realized, to tell the truth, I still do not like to usemapAs a parameter,mapAs an input parameter, the parameter depends on the guessing readability and maintainability is a bit poor, I still used to encapsulate onejavaBeanAs 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 aGETThe way the request was made and then the parameter was passed to the entity causing it not to be received.feignClientDoes not supportgetMethod to pass entity classes? Later, a note was found after searching the data@SpringQueryMapLet’s put the above code@RequestBodyreplace@SpringQueryMapPerfect solution to this problem

@SpringQueryMap

inSpring cloud 2.1 xThe above version provides a new annotation @SpringQueryMapWhy does this annotation help us do that? There are no secrets under the source code, we can turn it overfeignThe 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 inQueryMapParameterProcessorIn 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 itquerymap
  • Objects requiring special treatment are obtained by subscripting
  • Convert the object tomap(There is a pit that does not resolve fields of the parent class by default.)
  • Stitching additionalmaptourlIn the

conclusion

  • Through the above@SpringQueryMapAnnotations implementgetPass parameters, but if you need to pass more than one@SpringQueryMapHow can we implement annotations?
  • Or we could make one of our ownSpringQueryMapHow can we achieve this?
  • @SpringQueryMapBy 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 themFeigntheconfig# QueryMapEncoderforFieldQueryMapEncoder.
  • If we implement one ourselvesAnnotatedParameterProcessorAll 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.