httpclient调接口服务端与客户端开发实战

​​本文已参与「新人创作礼」活动,一起开启掘金创作之路。

项目场景:

项目场景:A系统(客户端)定时向B系统(服务端)推送xx数据。

注意事项:

  • http + json 传输方式

  • 定时任务

  • 传输安全:

    • 传输前:客户端携带认证信息向服务端获取token,token90秒有效。
    • 传输时:客户端携带token,数据加密,服务端验证token并解密文件。

开发文档:

一、Tonken获取 接口

接口说明:获取访问接口的认证凭证。使用认证签名调用本接口来获取token

地址:http://测试地址/getToken

**认证签名: **MD5( SecurityKey)

数据 格式

Content-Type:application/json;charset=UTF-8;Authorization: Basic 认证签名

返回数据

参数名 参数描述 是否必传 参数类型 备注
status 状态标识 Y String success 成功、fail 失败
message 错误信息 N String 失败必传
Token Token令牌 Y String 成功必传

返回报文

扫描二维码关注公众号,回复: 14280903 查看本文章
{        "status": "success",        "Token": "ea9b7b8fe2f14555953a7963323b40df",        "message": ""}

注意事项:token有效期为90秒,每次调用保单接口需要重新获取token

二、xx数据推送 接口

接口说明:A系统(客户端)将xx数据推送到B系统(服务端);

接口地址http://测试地址/pushData

数据

请求数据

参数名 参数描述 是否必传 参数类型 长度 备注
sendtime 发送时间 Y String
data.id xx数据id Y String
data.name xx数据名称 Y String

返回数据

参数名 参数描述 是否必传 参数类型 备注
status 状态标识 Y String success 成功、fail 失败
message 错误信息 N String 失败必传
name xx数据名称 Y String

报文实例

数据 格式

Content-Type:application/json;charset=UTF-8;Authorization: Bearer token

请求报文

{

        "sendTime":"2021-08-19 16:20:00",

        "data": "AESUtil.encrypt(json 字符串,securityKey)"

}

**注: **json 字符串为以下内容:

{     "data":[         {             "id":"",            "name":""        }     ] }

返回报文

{        "status": "success",        "name": "xxName...",        "message": ""}

服务端代码开发

开发思路:

一、token接口开发

  1. 提供接口认证,验证【认证信息】。
  2. 生成token,并保存90秒:用本地缓存保存,带失效时间。

二、数据接收接口开发

  1. 验证token
  2. 解析数据
  3. 保存数据、并返回返回值

开发代码

一、token接口

1、提供接口认证,验证【认证信息】

/**
 * @Description : TODO 获取token接口
 **/
@Controller
public class GetTokenServer {

  private final static Logger LOGGER = LoggerFactory.getLogger(GetTokenServer.class);
  private String SECURITY_KEY = "6666";

  @RequestMapping(value = "${path}/getToken", method = RequestMethod.POST)
  public @ResponseBody ResultToken getToken(HttpServletRequest request) {
    //Authorization: Basic 认证签名
    String signaturePost = request.getHeader("authorization");
    //截取认证签名,与 SECURITY_KEY 用 MD5 加密后的内容,比对
    String signature = MD5Util.encrypt32(SECURITY_KEY);
    if(!signaturePost.substring(6).equals(signature)){
        //认证不通过返回
    	return new ResultToken(StatusEnum.SIGN_ERROR.code(),StatusEnum.SIGN_ERROR.message(),"");
    }
    //认证通过,生成token返回,并本地缓存带超时时间保存
    String token = TokenUtil.createToken();
    LOGGER.info("【获取token接口】系统生成token[{}]",token);
    return new ResultToken(StatusEnum.SIGN_SUCCESS.code(),StatusEnum.SIGN_SUCCESS.message(),token);
  }
}
复制代码

2、生成token,并保存90秒:用本地缓存保存,带失效时间。

即 String token = TokenUtil.createToken(); 方法

/**
 * @Description TODO token工具类
 */
public class TokenUtil {
	private final static Logger LOGGER = LoggerFactory.getLogger(TokenUtil.class);
    private static final int EXPIRE_DATE= 90;
    /**
     * 生成并保存token,【过期时间90s】
     */
    public static String createToken (){
      //此处用uuid当作token
      String token = IdGen.uuid();
      CacheUtil.put(token,token,EXPIRE_DATE);
      return token;
    }
    /**
     * 验证token有效性: true有效,false无效
     */
    public static boolean verifyToken(String token){
    	  LOGGER.info("缓存中是否存在该token?=[{}]",CacheUtil.isContain(token));
        if(!CacheUtil.isContain(token)){
          return false;
        }else {
          return true;
        }
    }

}
复制代码

3、存放token的缓存实现

/**
 * @Description : TODO 缓存
 **/
public class CacheUtil {
  //默认大小
  private static final int DEFAULT_CAPACITY = 1024;
  // 最大缓存大小
  private static final int MAX_CAPACITY = 10000;
  //默认缓存过期时间
  private static final long DEFAULT_TIMEOUT = 3600;
  //1000毫秒 = 1秒
  private static final long SECOND_TIME = 1000;
  //存储缓存的Map
  private static final ConcurrentHashMap<String, Object> map;

  private static final Timer timer;

  static {
    map = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
    timer = new Timer();
  }

  //私有化构造方法
  private CacheUtil() {

  }

  /**
   * @Description TODO 缓存清除
   * @Param
   * @return
   */
  static class ClearTask extends TimerTask {
    private String key;

    public ClearTask(String key) {
      this.key = key;
    }

    @Override
    public void run() {
      CacheUtil.remove(key);
    }

  }

  /**
   * @Description TODO 添加缓存
   * @Param [java.lang.String, java.lang.Object]
   * @return boolean
   */
  public static boolean put(String key, Object object) {
    if (checkCapacity()) {
      map.put(key, object);
      //默认缓存时间
      timer.schedule(new ClearTask(key), DEFAULT_TIMEOUT);
      return true;
    }
    return false;
  }

  /**
   * @Description TODO 添加缓存,带过期时间
   * @Param [java.lang.String, java.lang.Object, int]
   * @return boolean
   */
  public static boolean put(String key, Object object, int time_out) {
    if (checkCapacity()) {
      map.put(key, object);
      //默认缓存时间
      timer.schedule(new ClearTask(key), time_out * SECOND_TIME);
    }
    return false;
  }

  /**
   * 判断容量
   * @param
   */
  public static boolean checkCapacity() {
    return map.size() < MAX_CAPACITY;
  }

  /**
   * 删除缓存
   * @param key
   */
  public static void remove(String key) {
    map.remove(key);
  }

  /**
   * 清除缓存
   */
  public void clearAll() {
    if (map.size() > 0) {
      map.clear();
    }
    timer.cancel();
  }

  /**
   * 获取缓存
   * @param key
   * @return
   */
  public static Object get(String key) {
    return map.get(key);
  }

  /**
   * 是否包含某个缓存
   * @param key
   * @return
   */
  public static boolean isContain(String key) {
    return map.contains(key);
  }
}
复制代码

二、接收数据

  1. 验证token
  2. 解析数据
  3. 保存数据、并返回返回值
@Controller
@RequestMapping(value = "${path}/")
public class xxController{

  private static final Logger logger = LoggerFactory.getLogger(xxController.class);

  @Autowired
  private xxService xxService;

  /**
   * @Description TODO 接收推送的接口
   */
  @RequestMapping(value = "pushData", method = RequestMethod.POST)
  @ResponseBody
  public ResultPolicy pushData(HttpServletRequest request) {
    String authorization = request.getHeader("authorization");
    logger.info("【authorization】[{}];",authorization);
    //用本地缓存校验是否包含此token
    if(!TokenUtil.verifyToken(authorization.substring(7))){
      logger.info("token验证失败");
      return new ResultPolicy(StatusEnum.TOKEN_NUll.code(),"",StatusEnum.TOKEN_NUll.message());
    }
    //token存在,解析数据
    Map<String, String> json = HttpUtil.getJSON(request);
    logger.info("【json数据为】[{}]",json);
    
    if(json == null || json.size()==0){
      return new ResultPolicy(StatusEnum.PARAM_NULL.code(),"",StatusEnum.PARAM_NULL.message());
    }

    //业务代码保存数据
    return xxService.parsingAndSaveData(json);
  }

}
复制代码



客户端代码开发:

开发思路:

一、http post请求 携带认证信息获取token

二、推送数据开发

  1. 解析到token放入推送请求的请求头中
  2. 发送请求
  3. 解析返回值

开发代码

总的方法概览

	/**
	 * 推送数据总方法
	 */
	public void push() {
        //需要推送的数据
		List<PushEntity> pushEntityList = xxDao.findPushEntityList();
		// 判断有需要推送的数据
		if (pushEntityList != null && pushEntityList.size() > 0) {
			// 获取token
			String token = "";
			try {
				token = HttpUtil.sendPostForToken();
			} catch (IOException e) {
				logger.info("【推送保单获取token失败】");
			}

            // token获取成功
			if (StringUtils.isNotBlank(token)) {
				//逐条推送数据
				pushItemByItem(token,pushEntityList);
			}
		}
	}
复制代码

一、获取token方法

	/**
	 * 发送POST请求,获取token
	 * 使用apache的httpclient方式发送请求
	 * @param
	 * @return token
	 */
	public static String sendPostForToken() throws IOException {

        //构建post请求:
        //1、请求地址
		HttpPost httpPost = new HttpPost(GET_TOKEN_URL);
        //2、请求头按照开发文档规定填写:md5 对密钥加密作为认证信息
		httpPost.setHeader("Content-type", "application/json");
		httpPost.setHeader("charset", "UTF-8");		
		httpPost.setHeader(AUTHORIZATION, "Basic " + MD5Util.encrypt32(securityKey));


        //发送post请求:
		try {
			responseLogin = client.execute(httpPost);
		} catch (IOException ioException) {
			logger.info("token请求失败!");
			ioException.printStackTrace();
			return null;
		}

        //获取请求返回结果,body:
		HttpEntity entity = responseLogin.getEntity();
		String stringEntity = null;
		try {
			stringEntity = EntityUtils.toString(entity, "UTF-8");
		} catch (IOException ioException) {
			ioException.printStackTrace();
			return null;
		}

        //用fastjson将返回body转成map对象:
		Map<String, String> resultParams = JSON.parseObject(stringEntity, Map.class);
		String token = resultParams.get("token");
		String status = resultParams.get("status");
		String message = resultParams.get("message");
		if (StringUtils.isNotBlank(status)) {
			if (status.equals("success")) {
				logger.info("【获取到token】:[{}]", token);
				return token;
			} else {
				logger.info("【未获取到token,错误信息】:[{}]", message);
				return token;
			}
		} else {
			logger.info("【无返回值内容】");
			return token;
		}

	}
复制代码

 二、携带token发送数据

1、方法总览

	/**
	 * 逐条推送数据
	 * 
	 * @param token
	 * @param pushEntityList
	 */
	public void pushItemByItem(String token, List<PushEntity> pushEntityList) {
		// 逐条数据处理
		for (PushEntity entity : pushEntityList) {
			//组装数据,后面方法介绍
			String data = HttpUtil.entityProcessing(entity);
			// 携带token、数据,http推送数据 封装接收返回值的实体类PushResult,后面方法介绍
			PushResult result = HttpUtil.sendPostForData(token, data);
			//解析返回参数:返回参数为[name]
			String nameList = result.getName();
			if(result.getStatus().equals("success")) {
				//推送成功
				if(StringUtils.isNotBlank(nameList)) {
					String name = nameList.substring(1, nameList.length() - 1);
					if(StringUtils.isNotBlank(name)) {
						//更新本地状态:推送成功
						xxDao.updateXX(name,"1","success");
						logger.info("【推送成功】name:[{}]", name);
					}
				}
			}else {
				//推送失败
				if(StringUtils.isNotBlank(nameList)) {
					String name = nameList.substring(1, nameList.length() - 1);
					if(StringUtils.isNotBlank(name)) {
						//更新本地状态:推送失败
						xxDao.updateXX(name,"2",result.getMessage());
						logger.info("【推送失败】name:[{}]", name);
						logger.info("【推送失败】错误信息:[{}]", result.getMessage());
					}
				}
			}
		}
	}
复制代码

2、 组装数据HttpUtil.entityProcessing(entity);

	/**
	 * 组装数据
	 * @param policy
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static String entityProcessing(PushEntity pushEntity) {
		//第一步:构建加密data数据
        //单条数据构建list集合
		@SuppressWarnings("rawtypes")
		List<PushEntity> list = new ArrayList<PushEntity>();
		list.add(pushEntity);
        String jsonStrEntityList = JSON.toJSONString(list);
		
        //集合作为map的value , map的key为“data”(依据开发文档)
		Map<String,String> dataMap = new HashMap<String,String>();
		dataMap.put("data", jsonStrEntityList);
		
        //将map用fastjson转为string类型
		String dataMapJsonString = JSON.toJSONString(dataMap);

        //依据文档加密
		String data = AESUtil.encrypt(dataMapJsonString, securityKey);

        //第二步:构建请求发送数据

		Map<String,String> resultMap = new HashMap<String,String>();
		
		SimpleDateFormat sdfAll = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		resultMap.put("sendTime", sdfAll.format(System.currentTimeMillis()));
		resultMap.put("data", data);
		
		String result = JSON.toJSONString(resultMap);
		
		
		return result;
	}
复制代码

3、httpclient发送数据HttpUtil.sendPostForData(token, data); 

	/**
	 * 发送POST请求,推送数据,成功后获取name
	 * @param token
	 * @param data
	 * @return String[] name数组
	 */
	public static PushEntityResult  sendPostForData(String token, String data) {
		//构建请求地址
        HttpPost httpPost = new HttpPost(PUSH_POLICY_URL);
        //构建请求头:token信息
		httpPost.setHeader(HTTP.CONTENT_TYPE, "application/json");
		httpPost.setHeader("charset", "UTF-8");
		httpPost.setHeader(AUTHORIZATION, "Bearer " + token);
		//构建请求体,组装好的数据
		StringEntity paramsString = new StringEntity(data, "utf-8");
	    paramsString.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,"application/json"));
	    httpPost.setEntity(paramsString);
	    
        //发送请求
		try {
			responseLogin = client.execute(httpPost);
		} catch (IOException ioException) {
			logger.info("推送请求失败!");
			ioException.printStackTrace();
		}

        //解析返回参数body
		HttpEntity entity = responseLogin.getEntity();
		String stringEntity = null;
		try {
			stringEntity = EntityUtils.toString(entity, "UTF-8");
			
		} catch (IOException ioException) {
			
			ioException.printStackTrace();
		}

        //body转为map对象
		
		@SuppressWarnings("unchecked")
		Map<String, String> resultParams = JSON.parseObject(stringEntity, Map.class);
		
		String name = resultParams.get("name");
		logger.info("【推送请求返回值name】:[{}]", name);
		String status = resultParams.get("status");
		logger.info("【推送请求返回值status】:[{}]", status);
		String message = resultParams.get("message");
		logger.info("【推送请求返回值message】:[{}]", message);
		
        //构建返回值对象:也可以将json返回值直接转为返回值对象
		PushEntityResult result = new PushEntityResult(status,name,message);
		return result;
	}
复制代码


总结:

        httpclient还是很好用的工具!

猜你喜欢

转载自juejin.im/post/7109451905734180872