-
Notifications
You must be signed in to change notification settings - Fork 12
项目整体设计
描述上说其实很简单,就是模拟登陆获取cookie并使用该cookie请求上传图片的接口,但是做起来很容易就这么干了
写一个伪代码,核心逻辑大概是这个样子,的确就很简单,确实没有必要像我这样写这么多东西。
public String uploadImageToSina(File file){
if(!file.exists()){return null;}
String cookie = getCookieFromCache();
if(cookie == null){
// 登陆获取cookie
cookie = login(username,password);
// 如果登陆成功
if(cookie != null){
setCookieToCache(cookie);
}else {
return "登陆失败";
}
}
res = doUpload(file);
if (res.success){
return res.url;
}
return "上传失败";
}
上述伪代码完全足以实现上传图片到微博图床的功能,但是,这也就仅仅实现了这样一个功能。提供的库一般是供给外部稳定使用的,外部是无法修改内部的内容的。比如最新的上传接口url改了,或者有可能上传图片需要多加一个请求头等等,如上代码基本上说是没办法改的。还有一个比较重要的功能就是重试了。
重试这个部分需要拿出来讲,毕竟是比较重要的部分。因为上传图片需要有两步动作,第一是登陆获取cookie,其次才是上传图片。假如登陆失败了,目前来说一般是没办法处理的,只能抛出一个LoginFailedException
,然而上传图片有一种情况(目前只知道这种情况,不排除之后会增加)是库可以处理的,即是cookie过期,上传图片失败。
上述伪代码中并未对该问题进行处理,假如需要对上述代码进行添加重试要怎么做呢,大概如下
public String uploadImageToSina(File file){
// 这里与上述代码一致,为了简短篇幅不贴出来了
...
// 后续添加的,重新登陆,重新上传。
if (res.cookieExpiration){
clearCookieCache();
cookie = login(username,password);
setCookieToCache(cookie);
// 重新上传
uploadImageToSina(file);
}
return "上传失败";
}
如果采用上述这样的伪代码这样的方案,会发现操作缓存(CacheUtils),登陆操作(LoginUtils),上传操作全都柔和在一块了。虽然我一开始的代码就是这么写的,重试也是上述那么写的,甚至递归调用那个上传的方法。在我重构现在这个版本的时候,写着写着发现我又把登陆放在上传接口上了,疯狂查看23种设计模式找灵感,到底该如何实现,最后灵机一动,引入了 拦截器模式。
说一下我的解决方案(不过真的是想了好久才想到这么干,可能还会有更好的方案,目前先这样了)
拦截器的内容可以查看这里
先用伪代码表达一下
// 先定义4个关键的拦截器,关键的获取cookie,登陆,重试都需要利用这4个拦截器
// 1. 初始化上传参数的拦截器 2. 检查缓存中的cookie 3. 登陆操作 4. 用于重试机制的二检操作
class InitRequestDataIntercepor implements UploadInterceptor{}
class CookieInterceptor implements UploadInterceptor{}
class LoginInterceptor implements UploadInterceptor{}
class ReCheckCookieInterceptor implements UploadInterceptor{}
public UploadResponse upload(File file){
assertTrue(file.exists());
// 跑一遍定义好的拦截器,如果有返回false的就不往下执行了
for(UploadInterceptor ui:UploadInterceptors){
if(!ui.processBefore()){
return failedUploadResponse;
}
}
// 真正的上传操作,doUpload是一个抽象方法,需要在子类中实现
res = doUpload(file);
// 再跑一遍拦截器,可以对响应的结果进行处理
for(UploadInterceptor ui:UploadInterceptors){
ui.processAfter(res);
}
// 结束
return res;
}
换成中文来描述的话,就是上传的方法或者类只应该跟上传相关操作有关,所以别的操作在拦截器中操作,正真的上传操作需要继承上述伪代码的类,因为上传的操作也有可能变化,不变的只有这一套遍历拦截器的逻辑,所以这里引入 模板方法模式 ,具体方法交由子类去实现。
整体逻辑大概是这样