1. Write at the front

The company develops a new project, because this project is more financial direction, related to money, so the interface is more rigorous. Just to give you the whole story,

  • I use OKhttp+Retrofit for Http
  • The background is divided into three types of tokens, namely real-name tokens (accessToken), anonymous tokens (oauthToken) and refreshtokens (refreshToken). Different tokens have different uses, some are placed in the request body as request parameters, and some are used as headers

2. Solve

If you’re like me, congratulations, you can solve some of your problems with this blog post. If not, you can check it out and maybe get some inspiration. OauthToken is used separately, because it is relatively simple, just register or forget the password to use, generally speaking, the background will give a separate GET request interface to get oauthToen, and then we sp cache it. This token is usually placed in the request body as a parameter to register or forget the password. Note that this is a parameter rather than a header. AccessToken is generally put as a Header in an Http request, for example in our Header rule

key—>Authorization

value–>token_type access_token

Believe that everyone can read, is not the same, our value is more than a accessToken, but a other variables and its joining together, and, of course, the impact is not big, because these values will be returned to us after we login successfully, we need only after login successfully with SP they cache, so here are my head configuration code is as follows

// Add the header token.addheader ("Authorization",getDefaultTokenType()+""+ SPUtils. GetSharedStringData (BaseApplication getAppContext (), AppConstant. ACCESS_TOKEN_KEY)) / * * * from the Sp in TokenType, If not, the default fixed value * @ is returnedreturn
     */
    public String getDefaultTokenType(){
        String tokenType = SPUtils.getSharedStringData(BaseApplication.getAppContext(), AppConstant.TOKEN_TYPE_KEY);
        if(TextUtils.isEmpty(tokenType)){
            tokenType = "token"; / / is best stored in SP SPUtils setSharedStringData (BaseApplication. GetAppContext (), AppConstant. TOKEN_TYPE_KEY, tokenType);return tokenType;
        }
            return tokenType;
    }
Copy the code

This should be the first step. Configure the header according to the rules defined in the background. Some people are worried that some interface requests don’t need headers, and if we configure it, like the login interface, we don’t even have access to accessToken, why would a request need headers? Don’t worry about it here. It’s okay. Let’s take a look at the rules defined in the background. Here is the schematic diagram I draw according to the rules defined in the background.

  • 200 indicates that the request is successful. The front end does not need to do any extra processing, but can directly obtain data parsing
  • 401 is returned to me by the background according to the regulations. The background may give different returns for each project. The background can control the specific changes, but the impact is not significant. When this happens, we have a lot of work to do on the front end, parsing from the body of the intercepted response. Note that even if I say 401, the background will return me the data, such as the following json data that we return

    Just get the response JSON data, parse it, judge it, that sort of thing. It’s pretty easy here.

  • Now there is only one important question, which is how do we know when the interface is 200 and when it is 401? Let’s assume that we will judge in the final return, for example, assuming OKHTTP, then it is in onResponse. We need to get the RresponseCode first, and then write a different bean to parse according to the different code. If Retrofit is used, it is too inconsistent with the actual project situation. Let alone the network request framework encapsulated in our project, generally speaking, we can directly write the bean, and directly parse the bean. Here, the efficiency is too slow, because if we parse the status code in the case of 401, and if we need to refresh the token, we need to request the interface, so we need to use the interceptor. After using interceptor, I feel more and more powerful. Firstly, it can intercept Request data, such as Request line, Request header and Request body, and also intercept Response line, Response header and Response body. The most important thing is that we can manipulate Request and Response. Speaking in code, try out the okHttpClient configuration first
.addInterceptor(new TokenInterceptor()) // Add token interceptorCopy the code

Implement the Interceptor interface and write the following code in the Intercept method

//        Request request = chain.request();
//
//        long t1 = System.nanoTime();
//
//        LogUtils.logd(String.format("Sending request %s on %s%n%s",
//                request.url(), chain.connection(), request.headers()));
//
//        Response response = chain.proceed(request);
//
//        long t2 = System.nanoTime();
//
//
//        LogUtils.logd(String.format("Received response for %s in %.1fms%n%s",
//                response.request().url(), (t2 - t1) / 1e6d, response.headers()));
//
//        LogUtils.logd("---- response code --- :"+response.code());
//
//        return response;
Copy the code

This basically prints both the request data and the response data. So this is the interceptor, so we need to achieve the desired result logic is not to intercept Request, intercept each Request Response, and then determine the Response code in the Response line, if it is 200, do not care, directly return the Response to the front end, if it is 401, intercept the Response body, parse the Response body, We then judge the status by the background’s own rules. We divide the status into two types, one is 402 403 405 406 and the other is 404. The former requires the same logical operation, Toast message, and then logs out and returns to the login page. You need to refresh the token interface to obtain a new token and send the request again. We’ll talk about the first category, since it’s impossible to operate from an interceptor, so let’s look at the second category. Intercept the response code first

Request request = chain.request(); // try the request Response originalResponse = chain.proceed(request); Originalresponse.body ().string() * {originalResponse.body().string()}} ResponseBody = originalResponse.body(); ResponseBody = originalResponse.body(); Int responseCode = originalResponse.code();Copy the code

We get the json string of the responseBody, which is the responseBody string, and we can’t parse it until we get it

       BufferedSource source = responseBody.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            Buffer buffer = source.buffer();
            Charset charset = UTF8;
            MediaType contentType = responseBody.contentType();
            if(contentType ! = null) { charset = contentType.charset(UTF8); BodyString = buffer.clone().readString(charset); LogUtils.logd("body---------->" + bodyString);
Copy the code

parsing

// Parse string into bean, TokenResponse responseB = (TokenResponse) jsonUtils.fromjson (bodyString, tokenResponse.class); int status = responseB.getStatus(); String message = responseB.getMessege();Copy the code

Judge status

      if(status == 402 || status ==403  || status==405  || status ==406){  
                LogUtils.logd("status:"+status+" message: "+message);
                return originalResponse;
            }else if(status == 404){// Token validity expires, Need to refresh token / / remove the local refreshToken String refreshToken = SPUtils. GetSharedStringData (BaseApplication. GetAppContext (), AppConstant.REFRESH_TOKEN_KEY); // HashMap<String, Object> params = new HashMap<>(); params.put("refresh_token", refreshToken);
                String jsonString = JsonUtils.toJson(params);
                RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonString); // Get a new token through a specific interface, The synchronous retrofit request Call<RefreshTokenBean> Call = api.getapiservice ().refreshToken(api.getcachecontrol (),body); // Update TokenBean bean= call.execute().body(); LogUtils.logd("Refresh new bean obtained by token"+bean.toString()); // Save the new data with sp update SPUtils.setSharedStringData(BaseApplication.getAppContext(),AppConstant.ACCESS_TOKEN_KEY,bean.getAccess_token()); SPUtils.setSharedStringData(BaseApplication.getAppContext(),AppConstant.TOKEN_TYPE_KEY,bean.getToken_type()); SPUtils.setSharedStringData(BaseApplication.getAppContext(),AppConstant.USER_ID,bean.getUserinfoId()); String newToken = SPUtils.getSharedStringData(BaseApplication.getAppContext(),AppConstant.ACCESS_TOKEN_KEY); // create a new request and modify it accordingly using the new token Request newRequest = request.newBuilder().header("Authorization",SPUtils.getSharedStringData(BaseApplication.getAppContext(),AppConstant.TOKEN_TYPE_KEY)+""+newToken)
                        .build();

                originalResponse.body().close();
                return chain.proceed(newRequest);
            }
Copy the code

This part of the code, it may be a little bit different for each person, but it says, if we don’t care, we can get a request from the interceptor and return a response

 Request request = chain.request();
Copy the code

If we feel that the request is wrong and needs to be modified, then we can modify it and return to the new RESONse with the new Request

Request newRequest = request.newBuilder().header("Authorization",SPUtils.getSharedStringData(BaseApplication.getAppContext(),AppConstant.TOKEN_TYPE_KEY)+""+newToken)
                        .build();
Copy the code


ResponseBody is available in the onError method, and the responseBody is available in the onError method.

       HttpException error = (HttpException) e;
        String errorBody = "";
        try {
            errorBody  =   error.response().errorBody().string();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
Copy the code

Toast Message Finish Activity, initent Login Activity

3. Stay with friends

It was a very difficult project for a number of reasons when I was building the framework a long time ago, when the interceptor was trying to get the responseBody String method, which is to convert the responseBody to a String, and I went through a lot of data, and a lot of methods didn’t work, and I learned from a great god, I appreciate it, but it’s been too long to find the source, THX

    BufferedSource source = responseBody.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            Buffer buffer = source.buffer();
            Charset charset = UTF8;
            MediaType contentType = responseBody.contentType();
            if(contentType ! = null) { charset = contentType.charset(UTF8); }Copy the code

Hope can help you!!