4.2.1 网络请求之HTTP

HTTP请求&响应:(常用的只有Post与Get,还有Head/put/delete/connect/options/trace)

Get&Post建议用post规范参数传递方式,并没有什么更优秀,只是大家都这样社会更和谐。

网络请求中我们常用键值对来传输参数(少部分API用json来传递,毕竟不是主流)。

通过上面的介绍,可以看出虽然Post与Get本意一个是表单提交一个是请求页面,但本质并没有什么区别。

  • Get方式:在url中填写参数:  http://xxxx.xx.com/xx.php?params1=value1&params2=value2
  • Post方式:参数是经过编码放在请求体中的。编码包括x-www-form-urlencoded 与 form-data。

因为url是存在于请求行中的,所以Get与Post区别本质就是参数是放在请求行中还是放在请求体

当然无论用哪种都能放在请求头中。一般在请求头中放一些发送端的常量。

表单提交中get post方式的区别有4点:

1) get是从服务器上获取数据,post是向服务器传送数据。

2) get是把参数数据队列加到提交表单的 ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTPpost机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。所以,get安全性非常低,post安全性较高。

3) get,服务器端用 Request.QueryString获取变量的值;post,服务器端用Request.Form获取提交的数据。

4) get 传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。

常见问题:

1. Get是明文,Post隐藏:  错,不用https全都是明文。

2. Get传递数据上限XXX有限制的是url长度,不是Http。Http服务器部分有限制的设置一下即可。

3. Get中文需要编码:      是真的...要注意:URLEncoder.encode(params, "gbk");

  请求是键值对,但返回数据我们常用Json。对于内存中的结构数据,肯定要用数据描述语言将对象序列化成文本,再用Http传递,接收端并从文本还原成结构数据。对象(服务器)<-->文本(Http传输)<-->对象(移动端) 。

  服务器返回的数据大部分都是复杂的结构数据,所以Json最适合。另:要求传输性能的话用FlatBuffers。

HttpURLConnection( HttpClient 被废弃了)

1. 入门级

public class NetUtils {
        public static String post(String url, String content) {
            HttpURLConnection conn = null;
            try {
                URL mURL = new URL(url);                       // 创建一个URL对象
                conn = (HttpURLConnection) mURL.openConnection(); .// 获取HttpURLConnection对象
                conn.setRequestMethod("POST");                   // 设置请求方法为post
                conn.setReadTimeout(5000);                       // 设置读取超时为5秒
                conn.setConnectTimeout(10000);                   .// 设置连接网络超时为10秒
                conn.setDoOutput(true);                          .// 设置此方法,允许向服务器输出内容
                String data = content;                            .//  post请求的参数
                OutputStream out = conn.getOutputStream();        // 获得一个输出流,向服务器写数据
                out.write(data.getBytes());                         // GET方式不需要
                out.flush();  out.close();
                int responseCode = conn.getResponseCode();        // 调用此方法就不必再使用.connect()方法
                if (responseCode == 200) {
                    InputStream is = conn.getInputStream();
                    String response = getStringFromInputStream(is);
                    return response;
                } else
                    throw new NetworkErrorException("response status is "+responseCode);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null)  conn.disconnect();               // 关闭连接
            }
            return null;
        }
        public static String get(String url) {
            HttpURLConnection conn = null;
            try {
                URL mURL = new URL(url); // 利用string url构建URL对象
                conn = (HttpURLConnection) mURL.openConnection();
                conn.setRequestMethod("GET");
                conn.setReadTimeout(5000);
                conn.setConnectTimeout(10000);
                int responseCode = conn.getResponseCode();
                if (responseCode == 200) {
                    InputStream is = conn.getInputStream();
                    String response = getStringFromInputStream(is);
                    return response;
                } else
                    throw new NetworkErrorException("response status is "+responseCode);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null)  conn.disconnect();
            }
            return null;
        }
    // 模板代码 必须熟练
        private static String getStringFromInputStream(InputStream is) throws IOException {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = is.read(buffer)) != -1)
                os.write(buffer, 0, len);
            is.close();
            String state = os.toString();  // 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
            os.close();
            return state;
        }
    }

  注意网络权限:  <uses-permission android:name="android.permission.INTERNET"/>

2. 初级

同步&异步

这2个概念仅存在于多线程编程中。

android中默认只有一个主线程,也叫UI线程。因为View绘制只能在这个线程内进行。所以如果你阻塞了(某些操作使这个线程在此处运行了N秒)这个线程,这期间View绘制将不能进行,UI就会卡。所以要极力避免在UI线程进行耗时操作。网络请求是一个典型耗时操作

通过上面的Utils类进行网络请求只有一行代码:NetUtils.get("http://www.baidu.com");    //这行代码将执行几百毫秒。

如果你这样写:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String response = Utils.get("http://www.baidu.com");
    }

就会死!!!

这就是同步方式。直接耗时操作阻塞线程直到数据接收完毕然后返回。Android不允许的。

异步方式:(在子线程进行耗时操作,完成后通过Handler将更新UI的操作发送到主线程执行。)

//在主线程new的Handler,就会在主线程进行后续处理。
    private Handler handler = new Handler();
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        new Thread(new Runnable() {
            @Override
            public void run() {
                final String response = NetUtils.get("http://www.baidu.com");  //从网络获取数据
                handler.post(new Runnable() {                          //向Handler发送处理操作
                    @Override
                    public void run() {
                        textView.setText(response);   //在UI线程更新UI
                    }
                });
            }
        }).start();
    }

但这样写好难看。异步通常伴随者他的好基友回调

这是通过回调封装的AsynNetUtils类

public class AsynNetUtils {
        public interface Callback{
            void onResponse(String response);
        }
        public static void get(final String url, final Callback callback){
            final Handler handler = new Handler();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    final String response = NetUtils.get(url);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onResponse(response);
                        }
                    });
                }
            }).start();
        }
        public static void post(final String url, final String content, final Callback callback){
            final Handler handler = new Handler();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    final String response = NetUtils.post(url,content);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onResponse(response);
                        }
                    });
                }
            }).start();
        }
    }

然后使用方法:

private TextView textView;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.webview);
        AsynNetUtils.get("http://www.baidu.com", new AsynNetUtils.Callback() {
            @Override
            public void onResponse(String response) {
                textView.setText(response);
            }
        });

但是,愚蠢的地方有很多:

  1. 每次都new Thread,new Handler消耗过大
  2. 没有异常处理机制
  3. 没有缓存机制
  4. 没有完善的API(请求头,参数,编码,拦截器等)与调试模式
  5. 没有Https

3. 高级 OKHttp

加入HTTP缓存机制

缓存对于移动端是非常重要的存在。

  • 减少请求次数,减小服务器压力.
  • 本地数据读取速度更快,让页面不会空白几百毫秒。
  • 在无网络的情况下提供数据。
  1. 1.      高级 OKHttp

加入HTTP缓存机制

缓存对于移动端是非常重要的存在。

  • 减少请求次数,减小服务器压力.
  • 本地数据读取速度更快,让页面不会空白几百毫秒。
  • 在无网络的情况下提供数据。

缓存一般由服务器控制(通过某些方式可以本地控制缓存,比如向过滤器添加缓存控制信息)。通过在请求头添加几个字段,正式使用时按需求也许只包含其中部分字段

客户端要根据这些信息储存这次请求信息。然后在客户端发起请求的时候要检查缓存。遵循下面步骤:

注意 服务器返回304意思是数据没有变动滚去读缓存信息。

  现在Android网络方面的第三方库很多,volley,Retrofit,OKHttp等,各有各自的特点。不过再怎么封装Volley在功能拓展性上始终无法与OkHttp相比。Volley停止了更新,而OkHttp得到了官方的认可,并在不断优化。

OkHttp是一个高效的HTTP:

  • 支持 SPDY ,共享同一个 Socket 来处理同一个服务器的所有请求
  • 如果 SPDY 不可用,则通过连接池来减少请求延时
  • 无缝的支持GZIP来减少数据流量
  • 缓存响应数据来减少重复的网络请求

  SPDY(读作“SPeeDY”)是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快64%。

  OKHttp(com.squareup.okhttp)是Android版Http客户端,非常高效,会自动处理常见的网络问题,像二次连接、SSL的握手问题。如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求。OkHttp支持Android 2.3及其以上版本。对于Java, JDK1.7以上。Android4.4开始HttpURLConnection的底层实现采用的是okHttp

  在OKHttp,每次网络请求就是一个Request,我们在Request里填写我们需要的url,header等其他参数,再通过Request构造出Call,Call内部去请求参数,得到回复,并将结果告诉调用者。

详细使用步骤如下:

  • 同步请求 excute()

HTTP GET

OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder() .url(url) .build();
Response response = client.newCall(request).execute();
if (response.isSuccessful())
  return response.body().string();
else
  throw new IOException("Unexpected code " + response);
}  // Request是OkHttp中访问的请求,Builder是辅助类。Response即OkHttp中的响应。

HTTP POST

1) POST提交Json数据

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
     RequestBody body = RequestBody.create(JSON, json);
     Request request = new Request.Builder() .url(url) .post(body) .build();   // 对比GET,放入post数据
     Response response = client.newCall(request).execute();
     if (response.isSuccessful())
        return response.body().string();
    else
        throw new IOException("Unexpected code " + response);
}  //使用Request的post方法来提交请求体RequestBody

2) POST提交键值对

OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
     RequestBody formBody = new FormEncodingBuilder()
                                  .add("platform", "android")
                                  .add("name", "bug")
                                  .build();
    Request request = new Request.Builder() .url(url) .post(formBody) .build();
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else
        throw new IOException("Unexpected code " + response);
}

  OkHttp官方文档并不建议我们创建多个OkHttpClient。如果有需要,可以使用clone方法,再进行自定义。

  • 异步请求 enqueue()

  我们通过Request.Builder传入url,然后直接execute执行得到Response,通过Response可以得到code,message等信息。这是通过同步的方式去操作网络请求,而android是不允许在UI线程做网络请求操作的,因此我们需要自己开启一个线程。当然,OKHttp也支持异步线程并且有回调返回,有了上面同步的基础,异步只要稍加改动即可

private void enqueue(){
        Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt") .build();
        client.newCall(request).enqueue(new Callback() {   //就是在同步的基础上讲execute改成enqueue
            public void onFailure(Request request, IOException e) {       }
            public void onResponse(Response response) throws IOException {
                //NOT UI Thread
                if(response.isSuccessful()){
                    System.out.println(response.code());
                    System.out.println(response.body().string());
                }
            }
        });
}// execute改成enqueue,接口回调的代码是在非UI线程的,有更新UI的操作要用Handler或者其他方式。

响应缓存

  为了缓存响应,你需要一个你可以读写的缓存目录,和缓存大小的限制。这个缓存目录应该是私有的,不信任的程序应不能读取缓存内容。

  一个缓存目录同时拥有多个缓存访问是错误的。大多数程序只需要调用一次new OkHttp(),在第一次调用时配置好缓存,然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。

private final OkHttpClient client;
public CacheResponse(File cacheDirectory) throws Exception {
    int cacheSize = 10 * 1024 * 1024;   // 10 MiB
    Cache cache = new Cache(cacheDirectory, cacheSize);
    client = new OkHttpClient();
    client.setCache(cache);
}
public void run() throws Exception {
    Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
    Response response1 = client.newCall(request).execute();
    if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
    String response1Body = response1.body().string();
    System.out.println("Response 1 response:          " + response1);
    System.out.println("Response 1 cache response:    " + response1.cacheResponse());
    System.out.println("Response 1 network response:  " + response1.networkResponse());

    Response response2 = client.newCall(request).execute();
    if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
    String response2Body = response2.body().string();
    System.out.println("Response 2 response:         " + response2);
    System.out.println("Response 2 cache response:    " + response2.cacheResponse());
    System.out.println("Response 2 network response:  " + response2.networkResponse());
    System.out.println("Response 2 equals Response 1 ? " + response1Body.equals(response2Body));
}

  response1 的结果在networkresponse,代表是从网络请求加载过来的;response2的networkresponse 就为null,而cacheresponse有数据。因为设置了缓存因此第二次请求时发现缓存里有就不再去走网络请求了。

  但有时候,即使在有缓存的情况下我们依然需要去后台请求最新的资源(比如资源更新了)这个时候可以使用强制走网络来要求必须请求网络数据

public void execute() throws Exception {
    Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt").build();
        Response response1 = client.newCall(request).execute();
        if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
        String response1Body = response1.body().string();
        System.out.println("Response 1 response:          "  +  response1);
        System.out.println("Response 1 cache response:     "  +  response1.cacheResponse());
        System.out.println("Response 1 network response:   "  +  response1.networkResponse());

  request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build();
        Response response2 = client.newCall(request).execute();
        if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
        String response2Body = response2.body().string();
        System.out.println("Response 2 response:          "  +  response2);
        System.out.println("Response 2 cache response:     "  +  response2.cacheResponse());
        System.out.println("Response 2 network response:   "  +  response2.networkResponse());
        System.out.println("Response 2 equals Response 1?  "  +  response1Body.equals(response2Body));
}
// response2的cache response为null,network response依然有数据。

  同样的我们可以使用 FORCE_CACHE 强制只要使用缓存的数据,但如果请求必须从网络获取才有数据,但又使用了FORCE_CACHE 策略就会返回504错误。

HTTPHTTPS

  1. HTTP是一个属于应用层的面向对象的协议,使用80端口。HTTP协议的主要特点可概括如下:

  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得服务器的程序规模小,因而通信速度很快。
  • 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  • 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • 无状态:HTTP协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

  2. HTTPS(基于SSL的HTTP协议)使用了HTTP协议,但使用不同于HTTP协议的默认端口,使用443端口,以及一个加密、身份验证层(HTTP与TCP之间),即HTTP下加入SSL层

使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示:

  1. 客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
  2. Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
  3. 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
  4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
  5. Web服务器利用自己的私钥解密出会话密钥。
  6. Web服务器利用会话密钥加密与客户端之间的通信。

SSL介绍:

  安全套接字(Secure Socket Layer,SSL)协议是Web浏览器与Web服务器之间安全交换信息的协议,提供两个基本的安全服务:鉴别与保密。

  SSL介于应用层和TCP层之间。应用层数据不再直接传递给传输层,而是传递给SSL层,SSL层对从应用层收到的数据进行加密,并增加自己的SSL头。

  SSL协议的三个特性

  ① 保密:在握手协议中定义了会话密钥后,所有的消息都被加密。

  ② 鉴别:可选的客户端认证,和强制的服务器端认证。

  ③ 完整性:传送的消息包括消息完整性检查(使用MAC)  

时间: 2024-10-18 19:43:27

4.2.1 网络请求之HTTP的相关文章

ios编程之网络请求

网络请求有GET请求和POST请求,get和post实现的时候可以选择同步或者异步实现.看一个请求是GET还是POST就看网址后面有没有携带请求体. GET与POST 区别 1.get请求 请求的网址全部明文显示 安全性不高 2.get请求 请求的网址 有字符数的限制 大概255个 3.post请求 请求的网址 不光是有一个请求的网址 还可以携带请求体 这个请求体 是以NSData形式存在 安全性较高 4.post请求没有字符数的限制 GET同步和GET异步 同步请求是在请求数据的时候不能做其他

Swift网络请求(Moya篇)

在使用Alamofire进行网络请求的时候,相信大部分的同学都会封装一个抽象的NetworkLayer,如"APIManager" 或者 "NetworkModel"等等.但是位置业务功能增加,会渐渐混合各种请求,不够清晰,而Moya能很好地解决这类问题.Moya在Alamofire基础上进行封装,是一个允许高度自定义的网络层,可以根据具体的需求进行接口的设置.具体的介绍可以参考Moya的官方链接,结构图如下: 接下来就介绍一下Moya的一些常见的用法: (一)根据

微信小程序 网络请求之re.request 和那些坑

微信小程序有四种网络请求类型,下面只详细介绍普通HTTPS请求(wx.request) 普通HTTPS请求(wx.request) 上传文件(wx.uploadFile) 下载文件(wx.downloadFile) WebSocket通信(wx.connectSocket) 首先,先确认是否设置了合法域名,或者在开发环境下不校验合法域名.关于设置合法域名请看→微信小程序 网络请求之设置合法域名   以下是wx.request的详细说明,截图于微信小程序开发文档 在微信index.js 发起一个普

Android网络请求框架AsyncHttpClient实例详解(配合JSON解析调用接口)

最近做项目要求使用到网络,想来想去选择了AsyncHttpClient框架开进行APP开发.在这里把我工作期间遇到的问题以及对AsyncHttpClient的使用经验做出相应总结,希望能对您的学习有所帮助. 首先按照惯例先来简单了解一些AsyncHttpClient网络框架的一些知识. 1.简介 Android中网络请求一般使用Apache HTTP Client或者采用HttpURLConnect,但是直接使用这两个类库需要写大量的代码才能完成网络post和get请求,而使用android-a

Go实战--实现一个自己的网络请求日志httplogger(The way to go)

生命不止,继续go go go~~~ 之前我们简要介绍了go语言中的log package 和 net/http package,那么我们今天就干点实事儿,将二者结合,实现我们自己的日志记录网络请求. 另外,我们还没有跟你介绍time package,但是也可以看懂的. 首先,我默认你了解go语言的组织结构. 导入需要的package 我们需要 log net/http time三个包 package httplogger import ( "log" "net/http&q

Android实际开发之网络请求组件的封装(OkHttp为核心)

趁周末时间撸了两天代码,将OkHttp网络请求框架进行了一次简单封装,对于实际开发非常有用.. 此次封装主要针对我们经常使用的网络请求的步骤进行封装,在已有框架OkHttp的基础上进行实际开发的封装 发送一个网络请求,有以下三个功能模块: 一:request处理 二:OkHttp核心处理 三:callback处理 我们进行网络请求组件的封装也是根据这三大模块进行封装的,下面规划一下这次封装的一个思维导图: 根据以上思维导图,我们第一步,先进行request的封装: 以下是封装的一个CommonR

【Swift】Alamofile网络请求数据更新TableView的坑

写这篇BLOG前,有些话不得不提一下,就仅当发发恼骚吧... 今天下午为了一个Alamofire取得数据而更新TableView的问题,查了一下午的百度(360也是见鬼的一样),竟然没有一个简单明了的回答, 而唯一几个比较接近答案的,说要 self.tableView.reloadData(),也没有贴上代码,说要放在哪个函数内, 都犹抱琵琶半遮面,让初学者自己采坑,于是郁闷了一下午,刚刚回到家,试想想,要不试试英文网,毕竟Swift就是人家老外的, 说不定老外会告诉你,怎么取得数据并绑定Tab

网络请求三方库——OkHttp

我们知道在Android开发中是可以直接使用现成的API进行网络请求的,就是使用 HttpClient 和 HttpURLConnention ,而Android 4.4 之后 HttpClient 已经被废弃,由于此前一直很流行的三方库 android-async-http 是基于 HttpClient 的,所以作者已经放弃了维护 android-async-http 库,我们在项目中也尽量不要使用这个库. OkHttp是Squaur公司开源的一个高性能Http请求库,它的职责同 HttpUR

Volley源码(2):执行网络请求的流程

上一篇(http://blog.csdn.net/szxgg/article/details/51345859)讲述了当我们调用Volley.newRequest()时,Volley内部这个类做了什么,其实Volley这个类就做了一件事情,就是实例化了RequesQueue,这也符合设计模式中的单一职责,其实主要的处理都在其他类中,有三个类最重要,HttpStack/Network/RequestQueue,之后会讲解这些类的关系及作用,那首先还是结合我们使用Volley时的情形来看看源码内部执

iOS进阶(网络请求)

1.网络请求方式 GET:通过网址字符串  网址字符串最多255字节  所有传输给服务器的数据都显示在网址里,直接可见,不安全 POST:通过data  容量很大 数据被转换成二进制数 安全 2.连接方式 同步连接:程序容易出现卡死现象 异步连接:等待数据返回 (有代理和block两种方式)创建请求对象时,采用NSMutableURLRequest对象并设置请求方式和body主体 POSTBlock异步block //创建一个NSURLSession对象 NSURLSession *sessio