深入理解HTTP协议

2023-05-16

目标:

  • 掌握 http 原理,重点掌握 http Request & Response 格式
  • 掌握 http 中相关重点知识,如请求方法,属性,状态码等
  • 使用 java socket 编写简易版本 http server , 深刻理解原理
  • 掌握session和cookie

1、HTTP原理

理解为何要有应用层?

我们已经学过 TCP/IP , 已经知道目前数据能从客户端进程经过路径选择跨网络传送到服务器端进程[ IP+Port ],可是,仅仅把数据从A点传送到B点就完了吗?这就好比,在淘宝上买了一部手机,卖家[ 客户端 ]把手机通过顺丰[ 传送+路径选择 ] 送到买家 [ 服务器 ] 手里就完了吗?当然不是,买家还要使用这款产品,还要在使用之后,给卖家打分评论。所以,我们把数据从A端传送到B端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用,所以我们还需要一层协议,不关心通信细节,关心应用细节!
这层协议叫做应用层协议。而应用是有不同的场景的,所以应用层协议是有不同种类的,其中经典协议之一的HTTP就是其中的佼佼者。那么, Http 是解决什么应用场景呢?
早期用户,上网使用浏览器来进行上网,而用浏览器上网阅读信息,最常见的是查看各种网页【其实也是文件数据,不过是一系列的 html 文档,当然还有其他资源如图片, css , js 等】,而要把网页文件信息通过网络传送到客户端,或者把用户数据上传到服务器,就需要 Http 协议【当然,http作用不限于此】

我们用浏览器打开一个网页,基本都是基于HTTP 协议来传输的。
比如我们访问百度:

https://www.baidu.com/

访问网站使用的协议类型就是HTTPS(https是基于http 实现的,只不过在 http 基础上引入一个加密层)

http vs https

在这里 http 和 https 都是应用层协议,应用层协议很多时候都需要程序员来手动设定(自己指定协议),http 是大佬们已经定义好的现成的协议。
http 协议的优点:http 协议简单,支持的扩展能力很强,这样程序员就可以基于 http 进行自定制,节省开发成本。http 协议是基于 TCP 来实现的。

认识 url

平时我们俗称的 “网址” 其实就是说的 URL
在这里插入图片描述
注意服务器的地址一般是隐藏了端口号的,没有显示出来一般都是默认端口号,http(80)、https(443)

  • url 中对应的path(文件路径)不同的时候,获取到的页面也是不同的
  • url 中的服务器ip (域名)来确定一个服务器
  • url 中的服务器端口来确定这个主机上的哪个进程
  • url 中的path来确定这个进程中所管理的那个资源、文件
  • 最终http 请求得到的“网页” 本质上是一个文件
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=%E7%A1%AE%E5%AE%9E%E5%BC%BA%E5%A4%A7%E7%9A%84&oq=%25E5%25BE%2597%25E5%25BE%2597&rsv_pq=9726ade400008505&rsv_t=bb1bRKFfe31d0v1TWsZop7f4HUKpykwIGV0E4Ya%2B0UMwaZHQkU3rn9lKF1w&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=3&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&rsv_btype=t&inputT=3318&rsv_sug4=5016

我们查询字符串中,使用&符号把这些内容区分成若干个键值对,每个键值对的键和值之间使用 = 分割。
在这里键值对的具体含义外人是不清楚的,只有实现内部程序的程序员才清楚,当然不同的服务器上使用的查询字符串中的键值对内容也是不相同的。

urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.转义的规则如下:将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY
格式。
在这里插入图片描述
就如// 被转义了
urldecode就是urlencode的逆过程;

HTTP的方法

在这里插入图片描述

最初设计HTTP的时候是这样的,在现在很少按照这样来使用。
最常用的 HTTP 方法是GET和 POST

GET 和 POST 的区别(面试)

很多资料写的是GET 一般把数据放到 url 中,POST 一般把数据放到body 中。其实也不是很科学的,理论上完全可以把 POST 数据放在url 中,把 GET 的数据放在body 中。
1、GET 用于从服务器获取资源,POST用于给服务器提交数据(现在很少严格遵守这样的设计初衷,两者都可以用来获取资源或者来提交数据)
2、GET 传输的数据量上限较小(URL长度有限),POST传输数据的数据量较大(在20 年前这句话说法是对的,之前的 url 最长也就是1k-2k ,但是现在的url 可能会很长,长到甚至可能有几 M )
3、POST 比 GET 更加安全(POST 的安全其实是掩耳盗铃,只是把密码啥的放在body 中,这个密码只是不在 url 地址栏中,这样懂编程的人随便抓个包就可以看到)

HTTP的状态码

在这里插入图片描述
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

HTTP常见Header

  • Content-Type: 数据类型(text/html等)
  • Content-Length: Body的长度
  • Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
  • User-Agent: 声明用户的操作系统和浏览器版本信息;
  • referer: 当前页面是从哪个页面跳转过来的;(有些请求是没有 referer的,例如直接在浏览器地址栏里输入 url 或者点击收藏的网站)
  • location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
  • Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
cookie

HTTP 的特点是无状态(两次 HTTP 请求之间是没有任何关联的),那么从业务上建立起这样的关联联系,就需要使用到 cookie。它相当于保存在浏览器中的一个字符串,这个字符串是通过服务器返回响应中的 Set-Cookie 字段中来的,后续在访问该服务器,请求就会自己带上 Cookie字段。
以登录为例,一次登录过程中会涉及两次请求:验证用户名和密码,验证成功后,需要跳转到主页。
第一次交互:
请求:(没有Cookie 字段)
响应:通过服务器返回响应中的 Set-Cookie 字段
第二次交互:
请求:(重定向到首页)
在这里就是借助 Cookie 就可以把多个请求 关联到一起(Cookie 中的内容如果是一致,这几个请求就是有关联的,就好比识别用户的身份信息,你登录一次之后,此时在访问该网站的其他页面就不在需要重新登录,但是为了安全在设计的时候 Cookie是会隔一段时间会刷新的)

浏览器 是按照域名来区分 Cookie的,百度的 Cookie 和搜狗的 Cookie 相互独立,不冲突的。

HTTP协议格式

在这里插入图片描述

HTTP请求

在这里插入图片描述

  • 首行: [方法] + [url] + [版本]
  • 协议头(Header): 请求的属性, 冒号空格分割的键值对;每组属性之间使用\n分隔,此处的键值对可以是用户自己定义的,但大多数都是HTTP中已经有的,具有特定含义的内容;
  • 空行:遇到空行表示Header部分结束,
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;

HTTP响应

在这里插入图片描述

  • 首行: [版本号] + [状态码] + [状态码解释]
  • 协议头(Header): 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;
  • 空行:表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.

自己实现一个HTTP协议(V1版本)

主要做这几件事情:
1、把代码格式进行整理,让代码更加规范;
2、解析 URL 中包含的参数(键值对),能够方便的处理用户传过来的参数;
3、演示 Cookie 的工作流程~

HttpRequest:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

// 表示一个 HTTP 请求, 并负责解析.
public class HttpRequest {
    private String method;
    // /index.html?a=10&b=20
    private String url;
    private String version;
    private Map<String, String> headers = new HashMap<>();
    private Map<String, String> parameters = new HashMap<>();

    // 请求的构造逻辑, 也使用工厂模式来构造.
    // 此处的参数, 就是从 socket 中获取到的 InputStream 对象
    // 这个过程本质上就是在 "反序列化"
    public static HttpRequest build(InputStream inputStream) throws IOException {
        HttpRequest request = new HttpRequest();
        // 此处的逻辑中, 不能把 bufferedReader 写到 try ( ) 中.
        // 一旦写进去之后意味着 bufferReader 就会被关闭, 会影响到 clientSocket 的状态.
        // 等到最后整个请求处理完了, 再统一关闭
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        // 此处的 build 的过程就是解析请求的过程.
        // 1. 解析首行
        String firstLine = bufferedReader.readLine();
        String[] firstLineTokens = firstLine.split(" ");
        request.method = firstLineTokens[0];
        request.url = firstLineTokens[1];
        request.version = firstLineTokens[2];
        // 2. 解析 url 中的参数
        int pos = request.url.indexOf("?");
        if (pos != -1) {
            // 看看 url 中是否有 ? . 如果没有, 就说明不带参数, 也就不必解析了
            // 此处的 parameters 是希望包含整个 参数 部分的内容
            // pos 表示 ? 的下标
            // /index.html?a=10&b=20
            // parameters 的结果就相当于是 a=10&b=20
            String parameters = request.url.substring(pos + 1);
            // 切分的最终结果, key a, value 10; key b, value 20;
            parseKV(parameters, request.parameters);
        }
        // 3. 解析 header
        String line = "";
        while ((line = bufferedReader.readLine()) != null && line.length() != 0) {
            String[] headerTokens = line.split(": ");
            request.headers.put(headerTokens[0], headerTokens[1]);
        }
        // 4. 解析 body (暂时先不考虑)
        return request;
    }

    private static void parseKV(String input, Map<String, String> output) {
        // 1. 先按照 & 切分成若干组键值对
        String[] kvTokens = input.split("&");
        // 2. 针对切分结果再分别进行按照 = 切分, 就得到了键和值
        for (String kv : kvTokens) {
            String[] result = kv.split("=");
            output.put(result[0], result[1]);
        }
    }

    // 给这个类构造一些 getter 方法. (不要搞 setter).
    // 请求对象的内容应该是从网络上解析来的. 用户不应该修改.
    public String getMethod() {
        return method;
    }

    public String getUrl() {
        return url;
    }

    public String getVersion() {
        return version;
    }

    // 此处的 getter 手动写, 自动生成的版本是直接得到整个 hash 表.
    // 而我们需要的是根据 key 来获取值.
    public String getHeader(String key) {
        return headers.get(key);
    }

    public String getParameter(String key) {
        return parameters.get(key);
    }

    @Override
    public String toString() {
        return "HttpRequest{" +
                "method='" + method + '\'' +
                ", url='" + url + '\'' +
                ", version='" + version + '\'' +
                ", headers=" + headers +
                ", parameters=" + parameters +
                '}';
    }
}

HttpResponse:

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;

// 表示一个 HTTP 响应, 负责构造
// 进行序列化操作
public class HttpResponse {
    private String version = "HTTP/1.1";
    private int status;     // 状态码
    private String message; // 状态码的描述信息
    private Map<String, String> headers = new HashMap<>();
    private StringBuilder body = new StringBuilder(); // 方便一会进行拼接.
    // 当代码需要把响应写回给客户端的时候, 就往这个 OutputStream 中写就好了.
    private OutputStream outputStream = null;

    public static HttpResponse build(OutputStream outputStream) {
        HttpResponse response = new HttpResponse();
        response.outputStream = outputStream;
        // 除了 outputStream 之外, 其他的属性的内容, 暂时都无法确定. 要根据代码的具体业务逻辑
        // 来确定. (服务器的 "根据请求并计算响应" 阶段来进行设置的)
        return response;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setHeader(String key, String value) {
        headers.put(key, value);
    }

    public void writeBody(String content) {
        body.append(content);
    }

    // 以上的设置属性的操作都是在内存中倒腾.
    // 还需要一个专门的方法, 把这些属性 按照 HTTP 协议 都写到 socket 中.
    public void flush() throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write(version + " " + status + " " + message + "\n");
        headers.put("Content-Length", body.toString().getBytes().length + "");
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            bufferedWriter.write(entry.getKey() + ": " + entry.getValue() + "\n");
        }
        bufferedWriter.write("\n");
        bufferedWriter.write(body.toString());
        bufferedWriter.flush();
    }
}

HttpServer:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpServerV2 {
    private ServerSocket serverSocket = null;

    public HttpServerV2(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true) {
            Socket clientSocket = serverSocket.accept();
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    process(clientSocket);
                }
            });
        }
    }

    public void process(Socket clientSocket) {
        try {
            // 1. 读取并解析请求
            HttpRequest request = HttpRequest.build(clientSocket.getInputStream());
            System.out.println("request: " + request);
            HttpResponse response = HttpResponse.build(clientSocket.getOutputStream());
            response.setHeader("Content-Type", "text/html");
            // 2. 根据请求计算响应
            if (request.getUrl().startsWith("/hello")) {
                response.setStatus(200);
                response.setMessage("OK");
                response.writeBody("<h1>hello</h1>");
            } else if (request.getUrl().startsWith("/calc")) {
                // 这个逻辑要根据参数的内容进行计算
                // 先获取到 a 和 b 两个参数的值
                String aStr = request.getParameter("a");
                String bStr = request.getParameter("b");
                // System.out.println("a: " + aStr + ", b: " + bStr);
                int a = Integer.parseInt(aStr);
                int b = Integer.parseInt(bStr);
                int result = a + b;
                response.setStatus(200);
                response.setMessage("OK");
                response.writeBody("<h1> result = " + result + "</h1>");
            } else if (request.getUrl().startsWith("/cookieUser")) {
                response.setStatus(200);
                response.setMessage("OK");
                // HTTP 的 header 中允许有多个 Set-Cookie 字段. 但是
                // 此处 response 中使用 HashMap 来表示 header 的. 此时相同的 key 就覆盖
                response.setHeader("Set-Cookie", "user=tz");
                response.writeBody("<h1>set cookieUser</h1>");
            } else if (request.getUrl().startsWith("/cookieTime")) {
                response.setStatus(200);
                response.setMessage("OK");
                // HTTP 的 header 中允许有多个 Set-Cookie 字段. 但是
                // 此处 response 中使用 HashMap 来表示 header 的. 此时相同的 key 就覆盖
                response.setHeader("Set-Cookie", "time=" + (System.currentTimeMillis() / 1000));
                response.writeBody("<h1>set cookieTime</h1>");
            } else {
                response.setStatus(200);
                response.setMessage("OK");
                response.writeBody("<h1>default</h1>");
            }
            // 3. 把响应写回到客户端
            response.flush();
        } catch (IOException | NullPointerException e) {
            e.printStackTrace();
        } finally {
            try {
                // 这个操作会同时关闭 getInputStream 和 getOutputStream 对象
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        HttpServerV2 server = new HttpServerV2(9090);
        server.start();
    }
}

Cookie工作原理

在这里插入图片描述

Cookie演示

我们启动服务器,演示一下Cookie 的效果:
服务器:
在这里插入图片描述
客户端:(第一次设置 Cookie)
在这里插入图片描述
第二次就会自己加上Cookie信息,在这里存的就是用户的信息:
在这里插入图片描述
我们在看看服务器:
在这里插入图片描述
我们还可以通过Fiddler抓包看看我们构造的响应和请求:在这里插入图片描述
再次理解Cookie:Cookie是可以有多个的也不是一成不变的,比如我们演示一个多次访问cookieTime,每次都获取到不同的时间戳作为Cookie。
第一次访问:
在这里插入图片描述
第二次访问:
在这里插入图片描述
在这里只是修改了 time 这个cookie, user 还是登录用户的信息,是不收影响的。

如何使用cookie 完成一次登录过程,我们得看看下边这个代码是如何实现,不仅仅是浏览器,服务器也要做出相应的逻辑实现。

V3版本http 服务器

主要工作:
1、支持返回一个静态的 html 文件
2、解析处理 cookie(把 cookie处理成键值对结构)
3、解析处理 body (把body 中的数据处理成键值对结构)
4、实现完整的登录功能(session 的简单实现)
在这里我说一下主要代码:

private void doPost(HttpRequest request, HttpResponse response) {
        // 2. 实现 /login 的处理
        if (request.getUrl().startsWith("/login")) {
            // 读取用户提交的用户名和密码
            String userName = request.getParameter("username");
            String password = request.getParameter("password");
//            System.out.println("userName: " + userName);
//            System.out.println("password: " + password);
            // 登陆逻辑就需要验证用户名密码是否正确.
            // 此处为了简单, 咱们把用户名和密码在代码中写死了.
            // 更科学的处理方式, 应该是从数据库中读取用户名对应的密码, 校验密码是否一致.
            if ("fwh".equals(userName) && "123".equals(password)) {
                // 登陆成功
                response.setStatus(200);
                response.setMessage("OK");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                // 原来登陆成功, 是给浏览器写了一个 cookie, cookie 中保存的是用户的用户名.
                // response.setHeader("Set-Cookie", "userName=" + userName);

                // 现有的对于登陆成功的处理. 给这次登陆的用户分配了一个 session
                // (在 hash 中新增了一个键值对), key 是随机生成的. value 就是用户的身份信息
                // 身份信息保存在服务器中, 此时也就不再有泄露的问题了
                // 给浏览器返回的 Cookie 中只需要包含 sessionId 即可
                String sessionId = UUID.randomUUID().toString();
                User user = new User();
                user.userName = "fwh";
                user.age = 20;
                user.school = "邮电";
                sessions.put(sessionId, user);
                response.setHeader("Set-Cookie", "sessionId=" + sessionId);

                response.writeBody("<html>");
                response.writeBody("<div>欢迎您! " + userName + "</div>");
                response.writeBody("</html>");
            } else {
                // 登陆失败
                response.setStatus(403);
                response.setMessage("Forbidden");
                response.setHeader("Content-Type", "text/html; charset=utf-8");
                response.writeBody("<html>");
                response.writeBody("<div>登陆失败</div>");
                response.writeBody("</html>");
            }
        }
    }

session 工作原理

在这里插入图片描述

response.setHeader("Set-Cookie", "userName=" + userName);

如果我们用这行代码,借助 Cookie来实现登录保持功能,这样做是不太好的,用户信息在 Cookie中,每次数据传输都要把这个 Cookie 再次发给服务器,这就意味着 Cookie 中的信息很容易泄露,甚至可以伪造,绕开登录~~所以我们就需要session了!

在服务器登录成功时,把用户信息保存在一个 hash 表中(value),同时生成一个 key(这个key 是一个唯一的字符串),sessionId,最终把 sessionId 写会到 cookie 中就好了。后续访问页面的时候,Cookie 中的内容就是 sessionId,sessionId是一个没有规律的字符串,提高了安全性。服务器可以通过 sessionId 进一步找到用户的相关信息。

String sessionId = UUID.randomUUID().toString();

这行代码就会生成一个随机的字符串,每次1调用都会生成不同的。
刚开始是没有的,响应通过 set-Cookie 来保存:
在这里插入图片描述
接下来访问就可以获得:
在这里插入图片描述
通常session 是要搭配一个 过期机制,来记录线程何时创建,何时过期,如果过期也需要你重新登录,不同的网站它自己设定的过期时间也是不同的。

Cookie 和 session 的关系

  1. 都是为了实现客户端与服务端交互而产出
  2. Cookie是保存在客户端,缺点易伪造、不安全
  3. Session是保存在服务端,会消耗服务器资源
  4. Session实现有两种方式:Cookie和URL重写
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解HTTP协议 的相关文章

  • 如何在PHP中完成http响应并进行进一步处理?

    就我而言 我需要向客户端回显一个标志并发送一封电子邮件 现在客户端需要等待电子邮件发送 但我想把这两个步骤分开 该怎么做呢 你可以看一下异步运行 PHP 任务 https stackoverflow com questions 858883
  • Google Closure 编译器和 multipart/form-data 不起作用

    我正在向 google 闭包编译器 API 服务发出请求 content file get contents file js url http closure compiler appspot com compile post true p
  • 处理rails应用程序中的rack_throttle异常

    当超出速率限制时 如何处理由rack throttle gem 生成的错误 现在我只收到包含以下内容的回复 Internal Server Error undefined method each for 403 Forbidden Rate
  • 使用 Django 发出 HTTP 请求并反序列化输出

    因此 我对整个 Web 开发几乎完全陌生 但已经投入到一个使用 Django 从 Web 服务中提取和解析数据的副项目中 并且即使在查看 Django 时 我也很难准确地理解事情是如何工作的 文档 在 Django 中 我已经完成了所有设置
  • 如何在 ASP.NET MVC 中获取参数数组作为 GET / POST?

    如何最好地获取数组 item gt value 对作为 GET POST 参数 在 PHP 中 我可以这样做 网址 这得到的参数为 Array a gt Array one gt 100 two gt 200 有什么方法可以在 ASP NE
  • URL 哈希在重定向之间持续存在

    由于某种原因 当发送服务器端重定向 使用 Location 标头 时 非 IE 浏览器似乎会保留 URL 哈希 如果存在 例子 a simple redirect using Response Redirect http www yahoo
  • CXF 增加连接池大小而不更改 http.maxConnections

    最近我被要求将 CXF 配置为与我们旧的 XFire 服务相同的参数 这些参数之一是Keep Alive timeout 60 max 20 然而 我做了一些研究 看来 CXF 使用 JVMHttpURLConnection引擎盖下的对象
  • 在 Go 中读取请求负载?

    我正在使用文件上传器 需要请求负载中的详细信息来裁剪它 func Upload w http ResponseWriter r http Request reader err r MultipartReader if err nil htt
  • 如何使用 Node.js 和 Axios 将文件上传到 AWS 中的预签名 URL?

    我有下一个场景 通过 AWS SDK 生成用于文件上传的签名 URL 尝试使用axios 或request npm包上传本地文件 但每次我都会出错 Status 403状态文本 禁止
  • 如何在 Laravel 中禁用 JSON 响应的分块编码?

    我从 Laravel 中的控制器方法返回一个数组 Laravel 将此解释为我想发送 JSON 这很好 但它没有设置Content Length并改为使用Transfer Encoding chunked 我的回复很小 所以我不想把它们分块
  • 从 Django 基于类的视图的 form_valid 方法调用特殊(非 HTTP)URL

    如果你这样做的话 有一个 HTML 技巧 a href New SMS Message a 点击新短信打开手机的本机短信应用程序并预 先填写To包含所提供号码的字段 在本例中为 1 408 555 1212 以及body与提供的消息 Hel
  • 如何给所有HttpClient请求方法添加参数?

    我正在编写一些使用 Apache 的 Java 代码HttpClient版本4 2 2使用 RESTful 第三方 API 该 API 具有利用 HTTP 的方法GET POST PUT and DELETE 需要注意的是 我使用的是 4
  • 如何将 POST 请求内容保存为 .NET 中的文件 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有一个客户端应用程序POST请求a
  • Angular2 http.post 被执行两次

    我遇到一个奇怪的问题 Angular2 的 RC1 Http 服务执行 http post 调用两次 我已经调试了我的应用程序 并且我知道这不是点击事件问题 导致核心服务调用的所有调用 public create json Object p
  • 从express.js 中删除所有标头

    我正在创建一个页面 其中有一些数据可以由另一个设备解析 我曾经使用 php 执行此操作 但现在将其移至 Node js 我需要从页面中删除所有标题 这样我就只有我的输出 此输出是对 GET 请求的响应 此刻我有 HTTP 1 1 200 O
  • HTTP实时音频流服务器

    作为概念验证 我需要创建一个 HTTP 服务器 该服务器在 GET 请求时应启动连续的非编码 非压缩音频数据流 WAV PCM16 我们假设音频数据是 4096 个随机生成的单声道音频样本块 采样率为 44 1kHz 我应该在 HTTP 响
  • 如何在C++中使用Curl获取HTTP响应字符串

    我对 HTTP 命令和 libcurl 库非常陌生 我知道如何获取 HTTP 响应代码 但不知道如何获取 HTTP 响应字符串 以下是我为获取响应代码而编写的代码片段 任何有关如何获取响应字符串的帮助将不胜感激 curl easy seto
  • Angular4如何使用flatMap链接forkJoin

    我所处的情况是 我需要进行 5 个可以并行执行的 http 调用 在这五个调用之后需要执行另一个 http 调用 我在前 5 个中使用了 forkJoin 但我不知道如何链接 flatMap 或其他函数 forkJoin firstObse
  • 在 Heroku 上获取客户端的真实 IP 地址

    在任何 Heroku 堆栈上 我想获取客户端的 IP 我的第一次尝试可能是 request headers REMOTE ADDR 当然 这是行不通的 因为所有请求都是通过代理传递的 所以替代方法是使用 request headers X
  • Angular2 中 Http 的 Promise 与 Observable? [复制]

    这个问题在这里已经有答案了 本质上 正如标题所说 是否有任何理由使用可观察的承诺 https stackoverflow com questions 37364973 angular 2 promise vs observable为了进行

随机推荐

  • Go_常量、iota(枚举)的使用

    常量 常量是在程序运行过程中 xff0c 其值不可以发生改变的数据 xff0c 常量无法被获取地址 常量中的数据类型能是布尔型 数字型 xff08 整型 浮点型和复数型 xff09 字符串 常量的定义是通过const关键字完成的 xff0c
  • Go_反射的使用

    反射可以在运行时动态地检查变量和类型 xff0c 并且可以调用变量和类型的方法 获取和修改变量的值和类型等 使用反射可以使程序更加灵活 xff0c 但也需要谨慎使用 基于反射的代码是极其脆弱的 xff0c 反射中的类型错误会在真正运行的时候
  • 登录注册页怎么做

    app常见页面 xff1a 引导页 xff1a 概念 xff1a 第一次安装App或者更新App后第一次打开App时所展示的可以滑动的页面 作用 xff1a 主要是用于展示产品本身的一些核心功能点或者特色 启动页 xff1a 概念 xff1
  • win10安装appx工具_如何在Windows 10上安装.Appx或.AppxBundle软件

    win10安装appx工具 Microsoft s new Universal Windows Platform applications use the Appx or AppxBundle file format They re nor
  • 本地mysql数据库开启远程访问

    本地mysql数据库开启远程访问 1 开启远程访问端口 3306端口 依次点击控制面板 系统和安全 windows防火墙 高级设置 入站规则 xff1b 设置端口为3306 一直点下一步 xff1b PS xff1a 入站 xff1a 别人
  • Go_String常用操作

    字符串操作 xff1a len xff1a 统计字符串的长度 span class token keyword func span span class token function main span span class token p
  • Arrarys常用的方法

    int newArrary 61 Arrays copyOf int original int newarrary length 拷贝数组 xff0c 可定义要拷贝的长度 Array copyOf 有进行复制的功能 xff0c 而且底层是调
  • Unity2019 打包Android报错 Android NDK not found

    打包报错 xff1a UnityException Android NDK not found Android NDK not found or invalid NDK配置报错 xff1a Edit gt Preferences gt Ex
  • 安装zabbix proxy

    Centos7搭建zabbix proxy5 0 概述安装创建数据库导入数据下载包安装 编译过程中的报错 概述 Zabbix 代理可以代表 Zabbix 服务器收集性能和可用性数据 这样 xff0c 代理可以自己承担一些收集数据的负载并卸载
  • Mac localhost无法访问

    Mac localhost无法访问 localhost 8080和127 0 0 1 8080可以访问nginx的文件 xff0c 但输入localhost和127 0 0 1都打不开了
  • 生产者与消费者问题(线程同步经典案例)

    生产者 xff08 producer xff09 和消费者 xff08 consumer xff09 问题是并发处理中最常见的一类问题 xff0c 是一个多线程同步问题的经典案例 可以这样描述这个问题 xff0c 有一个或者多个生产者产生某
  • Git忽略提交规则 & .gitignore配置总结

    Git忽略提交规则 xff06 gitignore配置总结 在使用Git的过程中 xff0c 我们喜欢有的文件比如日志 xff0c 临时文件 xff0c 编译的中间文件等不要提交到代码仓库 xff0c 这时就要设置相应的忽略规则 xff0c
  • Spring之配置文件

    Spring简介 Spring是什么 Spring 自带 IoC xff08 Inverse of Control 控制反转 xff09 和 AOP Aspect Oriented Programming 面向切面编程 可以很方便地对数据库
  • Ubuntu开启SSH服务远程登录

    Ubuntu开启SSH服务远程登录 Ubuntu下开启ssh服务并能通过MobaXterm或者 Xshell进行远程登录 本人使用的是window10系统安装的MobaXterm window10系统安装MobaXterm可以参考 http
  • MongoDB

    一 MongoDB简介 1 集成简介 spring data mongodb提供了MongoTemplate与MongoRepository两种方式访问mongodb xff0c MongoRepository操作简单 xff0c Mong
  • 更改桌面壁纸_使用DeskSlide轻松更改桌面墙纸

    更改桌面壁纸 Looking to add some variety to your desktop instead of looking at the same wallpaper day in and day out Have fun
  • 科学素养题(2022年2月-2022年10月)

    二月科学素养 在我国山东省和山西省中间的 山 34 是 C A泰山 B吕梁山 C太行山 D沂蒙山 在一些寻宝游戏中 每个线索都会指向下一个线索的位置 玩家可以顺着这些线索一个一个找到所有的元素 这样的寻宝游戏的设计与 数据结构有着异曲同工之
  • Servlet综合练习:个人博客系统

    功能简介 1 注册新用户 2 xff09 登录已有用户 3 xff09 展示博客列表 xff08 包含文章标题以及作者信息 xff09 xff0c 点击标题就会跳转到文章详情页 4 xff09 文章详情页中 xff0c 显示文章标题 xff
  • Linux 环境搭建(如何获得一个免费云服务器)以及Linux基本指令

    搭建 Linux 环境 Linux 环境的搭建方式 主要有三种 直接安装在物理机上 但是由于 Linux 桌面使用起来非常不友好 不推荐 使用虚拟机软件 将 Linux 搭建在虚拟机上 但是由于当前的虚拟机软件 如 VMWare 之类的 存
  • 深入理解HTTP协议

    目标 xff1a 掌握 http 原理 xff0c 重点掌握 http Request amp Response 格式掌握 http 中相关重点知识 xff0c 如请求方法 xff0c 属性 xff0c 状态码等使用 java socket