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请求的会话信息进行了广播。