HttpClient之HttpContext使用

Posted z-qinfeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HttpClient之HttpContext使用相关的知识,希望对你有一定的参考价值。

  Multiple request sequences that represent a logically related session should be executed with the same HttpContext instance to ensure automatic propagation of conversation context and state information between requests.

  上面这段话摘自httpclient官网,大体意思是逻辑会话相关的多个请求序列应该使用同一个HttpContext实例,这样就可以让会话信息和状态信息在多个请求之间自动广播。

  官网上还给出一段示例代码 ,我们仿着它的示例代码,重新整一个,以便于观察。

 

(1) 使用springboot快迅搭建一个目标服务

@RestController
public class RequestController {
    @PostMapping("/request")
    public void request(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession();
        //1. 从session中获取username
        String username = (String) session.getAttribute("username");
        String ret;
        if (username == null) {
            // 2. 从请求参数中获取username的值
            username = request.getParameter("username");
            session.setAttribute("username", username);
            ret = "login success!";
        } else {
            ret = "Having been logined " + username;
        }
        // 将ret 内容写回到response响应体中
        ServletOutputStream outputStream = null;
        PrintWriter pw = null;

        try {
            outputStream = response.getOutputStream();
            pw = new PrintWriter(outputStream);
            pw.write(ret);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (pw != null) {
                pw.close();
            }
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

启功类省略,server.port = 9999

(2)  HttpClient测试类

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class HttpContextTest {
    public static void main(String[] args) throws IOException {
        
        HttpContext httpContext = new BasicHttpContext();
        HttpClientContext httpClientContext = HttpClientContext.adapt(httpContext);
        
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("http://localhost:9999/request");
        // 模仿form表单请求,设置请求参数
        List<NameValuePair> nvp = new ArrayList<>();
        nvp.add(new BasicNameValuePair("username", "admin"));
        // 第一次请求时,设置请求参数
        httpPost.setEntity(new UrlEncodedFormEntity(nvp));

        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(httpPost, httpClientContext);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String ret = EntityUtils.toString(entity);
                System.out.println("第一次请求响应:"+ ret);
            }
        }finally {
            response.close();
        }

        System.out.println("=================第二次请求====================");
        // 重新创建一个HttpPost对象,但是此次该对象中不设置请求参数
        httpPost = new HttpPost("http://localhost:9999/request");
        try {
            response = httpclient.execute(httpPost, httpClientContext);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String ret = EntityUtils.toString(entity);
                System.out.println("第二次请求响应:"+ ret);
            }
        }finally {
            response.close();
        }
    }
}

(3)启动目标项目,然后再运行测试代码,打印结果如下

  

第一次请求响应:login success!
=================第二次请求====================
第二次请求响应:Having been logined admin

Process finished with exit code 0

感觉浏览器请求一模一样了, 保存了请求会话信息,事实上确实如此,此处就是保存jsessionid

 

(4) 简单看下源码

 

<1>  ProtocolExec#execute()

技术图片

 

 

<2> ResponseProcessCookies#process(final HttpResponse response, final HttpContext context)

  

@Override
public void process(final HttpResponse response, final HttpContext context)
        throws HttpException, IOException {
 
    final HttpClientContext clientContext = HttpClientContext.adapt(context);

    // Obtain actual CookieSpec instance
    final CookieSpec cookieSpec = clientContext.getCookieSpec();
    // Obtain cookie store
    final CookieStore cookieStore = clientContext.getCookieStore();
    
    //.......
    // see if the cookie spec supports cookie versioning.
    if (cookieSpec.getVersion() > 0) {
        // process set-cookie2 headers.
        // Cookie2 will replace equivalent Cookie instances
        // 就是将cookie信息拷到cookieStore中
        it = response.headerIterator(SM.SET_COOKIE2);
        processCookies(it, cookieSpec, cookieOrigin, cookieStore);
    }
}

技术图片

 

 技术图片

 

 <3> 断点查看第二次请求时HttpContext对象

技术图片

 

 

通过debug可以很明显看到,HttpContext将诸多的Http请求的会话信息进行了广播。

 

以上是关于HttpClient之HttpContext使用的主要内容,如果未能解决你的问题,请参考以下文章

将 cookie 从当前 HttpContext 传输到新创建的 HttpClient 请求的简单方法

Android HttpClient Cookie

小5聊Asp.Net Core 2.1基础之开启HttpContext内容请求

原缓存之 HttpRuntime.Cache

HttpClient之基本使用

HttpClient总结一之基本使用