These days in doing a function, in fact, very simple. is to call several external APIs, return the data, assemble it, and then become the new interface. One of the APIs is a wonderful API, though HTTP-based, but neither soap-based nor restful-style interfaces. Fortunately there is no complicated scene to use it. Just construct the URL, send an HTTP GET request, and then give me back an XML structure of data.
I used resttemplate in spring MVC as the client, and then introduced the Jackson-dataformat-xml as an XML-mapped tool library. Since the integration of the external API has been done many times, the integration of this API is pro, rinsed is done.
Next, in order to verify connectivity, I first configured a test environment for the external API in Soapui, tried to send a GET request, and received the response successfully. Then I ran my program and tried to invoke the API through my own program, and the result returned an HTTP 500 error, "Internal server error."
This is odd. My first reaction is that the configuration of the external API in the program is not the same as the configuration in Soapui. I have carefully contrasted the URL of the sending request, the HTTP header required and the username and password used for authentication are all identical. The problem was ruled out.
Next I want to take a closer look at response, can you find any clues. Carefully check the response header and body, found that the header everything is normal, body is an empty body, no information available.
Then another solution I could think of was the team that contacted the external API and asked them to help see why the server returned 500 after I sent the request. But unfortunately this is a very old service, find the people of the team and arrange for me to watch the log for at least a few days. And since SOAPUI can invoke success and the application is unsuccessful, the problem is mostly ours.
Next I think that since the problem may be in our case, then there must be a difference in request. Since I'm sending a GET request, without a body entity, and the URL is exactly the same, the problem is likely to be on the header of the request. This API requires that the request contains two custom headers, which I have already configured in SOAPUI and in my own program. Where is the problem?
Since this problem cannot be reproduced in soapui, I have used the chrome plugin version of postman to configure the call to the API. Then there was a miracle, and I had to reproduce the problem in the postman. When I saw the postman also returned to the error, I thought for 5 seconds, guessed the reason. The problem is likely to be on the authentication header.
To talk about this, start with the Basic authentication of HTTP. Basic authentication is the simplest technique for HTTP to implement access control. The HTTP client will use BASE64 encryption after combining the username and password to generate an HTTP header with key ' authentication ' and value ' Basic base64code '. Sent to the server side for Basic authentication methods.
But this classic Basic authentication is going to go through two steps. In the first step, the client sends an HTTP request without the authentication header, and the server checks that the requested resource needs authentication, returns HTTP Status 401, indicates unauthorized, and after the client discovers that the server returns 401, it constructs a new request. This time contains the authentication header, the server receives the authentication pass, returns the resource.
So the reason I called the internal server error in my own application and postman was that when the server sent the HTTP request without the authentication header for the first time, the server returned the HTTP Status 500. In fact, it should return 401, so that the HTTP client will send a new request containing the authentication. Because it returned to the 500,http client that the server had a problem, it stopped processing.
Then why in the SOAPUI call can succeed that? That is because the HTTP client used by SOAPUI has already set the authentication header on the first request, so there is no problem. This avoids the recurrence of a request to be made. This behavior is called ' Preemptive authentication ' (preemptive validation), and in soapui you can choose whether to enable the behavior. See how to authenticate SOAP requests in SoapUI for details.
So the root of the problem is that the external API does not fully conform to the specification when implementing Basic authentication, which we do not back .
There are two types of solutions. The first is to have the external API conform to the Basic authentication specification, which should return 401 instead of 500 if the request is not authorized. But I said this is a very old API, let them change to wait until the SE years.
The second is that my application sets the authentication header the first time it sends a request to the external API. We're using resttemplate, and Resttemplate is using the Apache Http Client 4.0+ version. To inject this header is simple, after instantiating the resttemplate, add a intecepter to it.
1 2 |
restTemplate.getInterceptors().add( new BasicAuthorizationInterceptor("username", "password"));
|
Add this line of code, run the program, smoothly got the response, the world is quiet.
The last question, why does the HTTP client not actively enable ' preemptive authentication ' when the username and password are configured? After all, you can make a lot less requests. This is the reason that Apache is officially given:
HttpClient does not preemptive authentication out of the box, because if misused or used incorrectly the Preemptiv E authentication can leads to significant security issues, such as sending user credentials in clear text to an unauthorize D Third party. Therefore, users expected to evaluate potential benefits of preemptive authentication versus security risks in the con Text of their specific application environment. Nonetheless one can configure HttpClient to authenticate preemptively by prepopulating the authentication data cache.
Extended reading:
- Chapter 4. HTTP Authentication
- Basic Authentication with the Resttemplate
- How to authenticate SOAP requests in SoapUI
- Basic access authentication
- HttpClient Basic Authentication
Exception thrown by an HTTP Basic authentication