本blog的代码库:HttpUtil2
请求正确时:
{
"data": {
"uid": "898997899788997"
},
"code": "0",
"msg": "success!",
"success": true,
"errorData": null
}
请求错误时错误原因千奇百怪,应使用map来解析errorData,避免解析异常.或直接使用optJSONObject("errorData")
{
"data": null,
"code": "user.login.401",
"msg": "unlogin",
"success": false,
"errorData": {
"reason":"kickout",
"time":1689799989
}
}
为了debug方便,在开发/测试环境,后台500时,应将异常栈信息直接塞在msg里返回给前端.
底层
从urlconnection到httpclient到okhttp
封装层
从volley/asyncHttpClient到retrofit
如今基本上是okhttp一统底层,上层retrofit+rxjava.
即使用retrofit,仍然有很多重复代码要写,需要更进一层的封装,方便日常crtl+c ,ctrl+v.
即使是crtl+c,也希望代码能少一点是一点.
那么一个封装完善的网络框架,还需要哪些功能?先看看几个star比较多的封装库: OkGo:
大强子的NET
rxHttp
结合日常开发经验,总结一下,其实有如下可塞入框架中:
其实,再想想,一个完善的客户端网络库,应该像postman一样基于配置,傻瓜易用.
封装网络框架,无非是吧这些个gui变成api而已.
下面开始讲讲每个关键点的实现和使用
HttpUtil.requestAsJsonArray("article/getArticleCommentList/v1.json",PostStandardJsonArray.class)
.addParam("pageSize","30")
.addParam("articleId","1738")
.addParam("pageIndex","1")
.post()
.setCacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)
// .setCacheMode(CacheStrategy.REQUEST_FAILED_READ_CACHE)
.callback(new MyNetCallback<ResponseBean<List<PostStandardJsonArray>>>(true,null) {
@Override
public void onSuccess(ResponseBean<List<PostStandardJsonArray>> response) {
MyLog.json(MyJson.toJsonStr(response.data));
}
@Override
public void onError(String msgCanShow) {
MyLog.e(msgCanShow);
}
});
String url2 = "https://kiwivm.64clouds.com/dist/openvpn-install-2.4.5-I601.exe";
HttpUtil.download(url2)
.setFileDownlodConfig(
FileDownlodConfig.newBuilder()
.verifyBySha1("76DAB206AE43FB81A15E9E54CAC87EA94BB5B384")
.isOpenAfterSuccess(true)
.build())
.callback(new MyNetCallback<ResponseBean<FileDownlodConfig>>() {
@Override
public void onSuccess(ResponseBean<FileDownlodConfig> response) {
MyLog.i("path:"+response.data.filePath);
}
@Override
public void onError(String msgCanShow) {
MyLog.e(msgCanShow);
}
});
HttpUtil.requestString("article/getArticleCommentList/v1.json")
.post()
.setSync(true)//同步执行
.addParam("pageSize","30")
.addParam("articleId","1738")
.addParam("pageIndex","1")
.callback(new MyNetCallback<ResponseBean<String>>(true,null) {
@Override
public void onSuccess(ResponseBean<String> response) {
MyLog.i(response.data);
}
@Override
public void onError(String msgCanShow) {
MyLog.e(msgCanShow);
}
});
/**
* 取消请求,常在activity ondestory处调用.直接传入activity即可,不会保存引用,直接识别其名字作为tag
*
* @param obj
*/
public static void cancelByTag(Object obj) {
if (obj == null) {
return;
}
List<retrofit2.Call> calls = callMap.remove(obj);//从gc root引用中删除
if (calls != null && calls.size() > 0) {
for (retrofit2.Call call : calls) {
try {
if (call.isCanceled()) {
return;
}
call.cancel();
} catch (Exception e) {
ExceptionReporterHelper.reportException(e);
}
}
}
}
ExceptionFriendlyMsg.init(context, new IFriendlyMsg() {
Map<String,Integer> errorMsgs = new HashMap<>();
{
errorMsgs.put("user.login.89899",R.string.httputl_unlogin_error);
}
@Override
public String toMsg(String code) {
Integer res = errorMsgs.get(code);
if(res != null && res != 0){
return context.getResources().getString(res);
}
return "";
}
});
内部已配置文案:(中文+英文)
String errorMsg = BeanValidator.validate(bean);
//返回的errorMsg为空就说明校验通过
if(!TextUtils.isEmpty(errorMsg)){
//Toast.makeText(this,errorMsg,Toast.LENGTH_LONG).show();
Observable.error(xxx)//把errorMsg和指定errorCode往外抛
}else {
//拿到合格的bean
}
这个操作,放到bean刚被解析出来的时候做就行.
超越http协议本身的缓存控制模式http协议本身缓存控制有哪些局限:
.setCacheMode(CacheMode.FIRST_CACHE_THEN_REQUEST)
//缓存策略,分类参考:https://github.com/jeasonlzy/okhttp-OkGo
//不使用缓存,该模式下,cacheKey,cacheMaxAge 参数均无效
public static final int NO_CACHE = 1;
//完全按照HTTP协议的默认缓存规则,例如有304响应头时缓存。
public static final int DEFAULT = 2;
//先请求网络,如果请求网络失败,则读取缓存,如果读取缓存失败,本次请求失败。成功或失败的回调只有一次
public static final int REQUEST_FAILED_READ_CACHE = 3;
//优先使用缓存,如果缓存不存在才请求网络,成功或失败的回调只有一次
public static final int IF_NONE_CACHE_REQUEST = 4;
//先使用缓存,不管是否存在,仍然请求网络,可能导致两次成功的回调或一次失败的回调.
//成功回调里,有标识识别本次是缓存还是网络返回.
public static final int FIRST_CACHE_THEN_REQUEST = 5;
//只读取缓存,不请求网络
public static final int ONLY_CACHE = 6;
public static final int COOKIE_NONE = 1;
public static final int COOKIE_MEMORY = 2;
public static final int COOKIE_DISK = 3;
private int cookieMode = COOKIE_DISK;//默认是做持久化操作
/**
* 设置cookie管理策略
*/
public GlobalConfig setCookieMode(int cookieMode) {
this.cookieMode = cookieMode;
return this;
}
之前只有connecTimeout,read,write三个超时时间,现在看,已新增callTimeout,涵盖了okhttp层面的整个请求过程.
对于当初没有calltimeout的时代,单纯设置下面三个是不够的,因为dns解析过程并不能被这三者覆盖.
可以使用rxjava的timeout来控制整个流程的耗时.
如今依然优先使用rxjava来控制.因为okhttp的calltimeout无法覆盖自定义缓存读写的超时.
这种一般提供全局配置和单个请求配置
builder.retryOnConnectionFailure(boolean)
但只是tcp连接失败的重试.且只能重试一次
要不论什么错误都重试,且可指定重试次数,还是得靠rxjava的api. 这就不说了,直接用就行.
@Override
public Response intercept(Chain chain) throws IOException {
try {
Response response = chain.proceed(chain.request());
} catch (Throwable e) {
if (e instanceof IOException) {
throw e;
} else {
//降级,让okhttp框架能处理错误,而不是crash
throw new IOException(e);
}
}
}
这个一般在主工程做.框架内不参与.
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
report(e);
}
});
回调的onSuccess和onError是使用者实现的,如果也出现了崩溃怎么办?也给你兜住!onSuccess抛异常,降级给onError onError还抛异常,模仿rxjava,降级给全局错误处理
if(bean.success){
try {
onSuccess(callback,t);
}catch (Throwable throwable){
onError(callback,throwable);
}
}else {
onError(callback,bean.errorInfo);
}
private static <T> void onError(MyNetCallback<T> callback, Throwable e) {
try {
Tool.logd("-->http is onError: "+callback.getUrl() );
Tool.dismissLoadingDialog(callback.dialogConfig, callback.tagForCancel);
ErrorCallbackDispatcher.dispatchException(callback, e);
}catch (Throwable e2){
if(GlobalConfig.get().getErrorHandler() != null){
try {
GlobalConfig.get().getErrorHandler().accept(e2);
} catch (Exception exception) {
exception.printStackTrace();
}
}else {
if(!GlobalConfig.get().isDebug()){
e2.printStackTrace();
}
}
//测试环境,都崩溃,提醒一下
if(GlobalConfig.get().isDebug()){
throw e2;
}
}
}
网络嘛,debug主要形式还是抓包提供丰富多彩的看包的形式:
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="system"/>
<certificates src="user"/>
</trust-anchors>
</debug-overrides>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
或者直接网络框架在debug环境忽略证书
上报不麻烦,关键是统计分析怎么搞?有哪些现成的,自己搭又要怎么搭.在上面的拦截器里添加上报即可. 关键是上报到哪里 构建exception,上报到sentry. 或者自己搭一条flume+elk的分析系统. 或者猥琐一点,构建event上报到事件统计平台,蹭他们的流量.
基本上就是这几个问题什么是中间人攻击 如何防范中间人攻击 什么是单向证书校验,框架层如何实现 什么是双向证书校验,框架层如何实现 如何对抗证书校验? root手机+frida+okhttplogging的dex 参考: frida使用
final Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
final long size = buffer.size();
final byte[] bytes = new byte[(int) size];
buffer.readFully(bytes);
final byte[] bytesEncrypted = encrypt(bytes);
//加密成功/失败,最好在请求头加一个标识
return new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse(type);
}
@Override
public long contentLength() {
return bytesEncrypted.length;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.write(bytesEncrypted);
}
};
private RequestBody gzip(final RequestBody body, String type) {
return new RequestBody() {
@Override
public MediaType contentType() {
return body.contentType();
}
@Override
public long contentLength() {
return -1; // We don't know the compressed length in advance!
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
后端nginx上用lua脚本进行解压缩后再转发即可.
抄了些迅雷等下载软件的功能,用api的形式提供出来比如:
public static io.reactivex.Observable<ResponseBean<S3Info>> uploadImgs(String type, final List<String> filePaths){
final List<S3Info> infos = new ArrayList<>();
io.reactivex.Observable<ResponseBean<S3Info>> observable =
HttpUtil.requestAsJsonArray(getUploadTokenPath,S3Info.class)
.get()
.addParam("type", type)
.addParam("contentType", IMAGE_JPEG)
.addParam("cnt",filePaths.size())
.asObservable()
.flatMap(new Function<ResponseBean<List<S3Info>>, ObservableSource<ResponseBean<S3Info>>>() {
@Override
public ObservableSource<ResponseBean<S3Info>> apply(ResponseBean<List<S3Info>> bean) throws Exception {
infos.addAll(bean.bean);
List<io.reactivex.ObservableSource<ResponseBean<S3Info>>> observables = new ArrayList<>();
for(int i = 0; i< bean.bean.size(); i++){
S3Info info = bean.bean.get(i);
String filePath = filePaths.get(i);
io.reactivex.Observable<ResponseBean<S3Info>> observable =
HttpUtil.request(info.getUrl(),S3Info.class)
.uploadBinary(filePath)
.put()
.setExtraFromOut(info)
.responseAsString()
.treatEmptyDataAsSuccess()
.asObservable();
observables.add(observable);
}
return io.reactivex.Observable.merge(observables);
}
});
return observable;
}
后台微服务拆得太细,又不愿做聚合,只能客户端自己做. 在客户端,基于Rxjava实现通用的聚合接口请求. 每个接口可配置能否接受失败
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:
我在我的rails应用程序中安装了来自github.com的acts_as_versioned插件,但有一段代码我不完全理解,我希望有人能帮我解决这个问题class_eval我知道block内的方法(或任何它是什么)被定义为类内的实例方法,但我在插件的任何地方都找不到定义为常量的CLASS_METHODS,而且我也不确定是什么here,并且有问题的代码从lib/acts_as_versioned.rb的第199行开始。如果有人愿意告诉我这里的内幕,我将不胜感激。谢谢-C 最佳答案 这是一个异端。http://en.wikipedia
我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow