HTTP网络请求遇到的一些知识点和困难
名词解释
Accept:
发送端(客户端)希望接受的数据类型。
比如:
text/xml(application/json)代表客户端希望接受的数据类型是xml(json )类型
Content-Type:
发送端(客户端|服务器)发送的实体数据的数据类型。
比如:
text/html(application/json)代表发送端发送的数据格式是html(json)。
二者合起来,Accept:text/xml;Content-Type:text/html即代表希望接受的数据类型是xml格式,本次请求发送的数据的数据格式是html。
Conteny-Type:内容类型,即请求/响应的内容区数据的媒体类型
Accept:用来指定什么媒体类型的响应是可接受的,即告诉服务器我需要什么媒体类型的数据,此时服务器应该根据Accept请求头生产指定媒体类型的数据。
Content-Type(内容类型)
一般是指网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,这就是经常看到一些 PHP 网页点击的结果却是下载一个文件或一张图片的原因。
Content-Type 标头告诉客户端实际返回的内容的内容类型。
语法格式:
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something
HTTP协议传输的媒体类型及如何表示媒体类型
MediaType
即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。
类型格式:type/subtype(;parameter)? type
主类型,任意的字符串,如text,如果是号代表所有;
subtype 子类型,任意的字符串,如html,如果是号代表所有;
parameter 可选,一些参数,如Accept请求头的q参数, Content-Type的 charset参数。
例如:Content-Type: text/html;charset:utf-8;
常见的媒体格式类型如下:
常见的MediaType:
- text/html:HTML格式
- text/plain:纯文本格式
- text/xml: XML格式
- image/gif:gif图片格式
- image/jpeg:jpg图片格式
- image/png:png图片格式
- application/xhtml+xml:XHTML格式
- application/xml: XML数据格式
- application/atom+xml:Atom XML聚合格式
- application/json:JSON数据格式
- application/pdf:pdf格式
- application/msword:Word文档格式
- application/octet-stream: 二进制流数据(如常见的文件下载)
- application/x-www-form-urlencoded:中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
- multipart/form-data: 需要在表单中进行文件上传时,就需要使用该格式
一些示例
Conteny-Type:内容类型,即请求/响应的内容区数据的媒体类型
Accept:用来指定什么媒体类型的响应是可接受的,即告诉服务器我需要什么媒体类型的数据,此时服务器应该根据Accept请求头生产指定媒体类型的数据。
问题:
服务器端可以通过指定【headers = “Content-Type=application/json”】来声明可处理(可消费)的媒体类型,即只消费Content-Type指定的请求内容体数据;
客户端如何告诉服务器端它只消费什么媒体类型的数据呢?即客户端接受(需要)什么类型的数据呢?服务器应该生产什么类型的数据?此时我们可以请求的Accept请求头来实现这个功能。
@RequestMapping(value = "/response/ContentType", headers = "Accept=application/json")
public void response2(HttpServletResponse response) throws IOException {
//表示响应的内容区数据的媒体类型为json格式,且编码为utf-8(客户端应该以utf-8解码)
response.setContentType("application/json;charset=utf-8");
//写出响应体内容
String jsonData = "{\"username\":\"zhang\", \"password\":\"123\"}";
response.getWriter().write(jsonData);
}
@Controller
@RequestMapping(value = "/users", method = RequestMethod.POST, consumes="application/json", produces="application/json")
@ResponseBody
public List<User> addUser(@RequestBody User userl) {
// implementation omitted
return List<User> users;
}
上面两个例子都表示了request请求中Accept头中包含了"application/json"的请求,同时暗示了返回的内容类型为application/json:
- produces标识:produces=“application/json”
- headers =“Accept=application/json”
其中request Content-Type为“application/json”类型的请求.
当你有如下Accept头,将遵守如下规则进行应用:
-
Accept:text/html,application/xml,application/json
将按照如下顺序进行produces的匹配 ①text/html ②application/xml ③application/json -
Accept:application/xml;q=0.5,application/json;q=0.9,text/html
将按照如下顺序进行produces的匹配 ①text/html ②application/json ③application/xml
参数为媒体类型的质量因子,越大则优先权越高(从0到1) -
Accept:/,text/,text/html
将按照如下顺序进行produces的匹配 ①text/html ②text/ ③/
即匹配规则为:最明确的优先匹配。
formdata和json的区别
超链接中有图示了这两者的区别,比较完善
Spring MVC中关于关于Content-Type类型信息的使用
这个在https://www.jianshu.com/p/75e560d287f7这个链接中有,没有仔细看,可以之后再看。
http请求的代码编写
目前使用的代码
private void button4_Click(object sender, EventArgs e)
{
HttpPostFileRequestClient test1 = new HttpPostFileRequestClient();
string fileUrl = "truck_2.jpg";
FileStream fs = new FileStream("F:\\all_truck\\truck_3.jpg", FileMode.Open, FileAccess.Read);
test1.SetField("file", "truck_3.jpg", "multipart/form-data", fs);
HttpWebResponse response = test1.Post("http://127.0.0.1:5000/measure/");
Stream myResponseStream = response.GetResponseStream();
StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
string retString = myStreamReader.ReadToEnd();
MessageBox.Show(retString);
System.Diagnostics.Debug.WriteLine(retString);
//这个需要引入Newtonsoft.Json这个DLL并using
//传入我们的实体类还有需要解析的JSON字符串这样就OK了。然后就可以通过实体类使用数据了。
Root rt = JsonConvert.DeserializeObject<Root>(retString);
//这样就可以取出json数据里面的值
int truck_hgight = rt.truck.height;
MessageBox.Show("code=" + rt.code + "\r\n" + "costTime=" + rt.costTime+"GIGHT"+ truck_hgight);
//由于这个JSON字符串的 public List<DataItem> data 是一个集合,所以我们需要遍历集合里面的所有数据
}
/// <summary>
/// 用于以 POST 方式向目标地址提交表单数据, 仅适用于包含文件的请求
/// </summary>
public sealed class HttpPostFileRequestClient
{
#region - Private -
private string _boundary;
private List<byte[]> _postDatas;
#endregion
/// <summary>
/// 获取或设置数据字符编码, 默认使用<see cref="System.Text.Encoding.UTF8"/>
/// </summary>
public Encoding Encoding { get; set; } = Encoding.UTF8;
/// <summary>
/// 获取或设置 UserAgent
/// </summary>
public string UserAgent { get; set; } = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36";
/// <summary>
/// 获取或设置 Accept
/// </summary>
//public string Accept { get; set; } = "*/*";
public string Accept { get; set; } = "application/json";
/// <summary>
/// 获取或设置 Referer
/// </summary>
public string Referer { get; set; }
/// <summary>
/// 获取或设置 Cookie 容器
/// </summary>
public CookieContainer CookieContainer { get; set; } = new CookieContainer();
/// <summary>
/// 初始化一个用于以 POST 方式向目标地址提交表单数据的<see cref="HttpPostFileRequestClient"/>实例
/// </summary>
public HttpPostFileRequestClient()
{
this._boundary = DateTime.Now.Ticks.ToString("X");
this._postDatas = new List<byte[]>();
}
/// <summary>
/// 设置表单数据字段, 用于存放文本类型数据
/// </summary>
/// <param name="fieldName">指定的字段名称</param>
/// <param name="fieldValue">指定的字段值</param>
public void SetField(string fieldName, string fieldValue)
{
var field = $"--{this._boundary}\r\n" +
$"Content-Disposition: form-data;name=\"{fieldName}\"\r\n\r\n" +
$"{fieldValue}\r\n";
this._postDatas.Add(this.Encoding.GetBytes(field));
}
/// <summary>
/// 设置表单数据字段, 用于文件类型数据
/// </summary>
/// <param name="fieldName">字段名称</param> 应该是表格某一列的名称
/// <param name="fileName">文件名</param>
/// <param name="contentType">内容类型, 传入 null 将默认使用 application/octet-stream</param>
/// <param name="fs">文件流</param>
public void SetField(string fieldName, string fileName, string contentType, Stream fs)
{
var fileBytes = new byte[fs.Length];
using (fs)
{
fs.Read(fileBytes, 0, fileBytes.Length);
}
SetField(fieldName, fileName, contentType, fileBytes);
}
/// <summary>
/// 设置表单数据字段, 用于文件类型数据
/// </summary>
/// <param name="fieldName">字段名称</param>
/// <param name="fileName">文件名</param>
/// <param name="contentType">内容类型, 传入 null 将默认使用 application/octet-stream</param>
/// <param name="fileBytes">文件字节数组</param>
public void SetField(string fieldName, string fileName, string contentType, byte[] fileBytes)
{
var field = $"--{this._boundary}\r\n" +
$"Content-Disposition: form-data; name=\"{fieldName}\";filename=\"{fileName}\"\r\n" +
$"Content-Type:{contentType ?? "application/octet-stream"}\r\n\r\n";
this._postDatas.Add(this.Encoding.GetBytes(field));
this._postDatas.Add(fileBytes);
this._postDatas.Add(this.Encoding.GetBytes("\r\n"));
}
/// <summary>
/// 以POST方式向目标地址提交表单数据
/// </summary>
/// <param name="url">目标地址, http(s)://sample.com</param>
/// <returns>目标地址的响应</returns>
public HttpWebResponse Post(string url)
{
if (string.IsNullOrWhiteSpace(url))
throw new ArgumentNullException(nameof(url));
HttpWebRequest request = null;
if (url.ToLowerInvariant().StartsWith("https"))
{
request = WebRequest.Create(url) as HttpWebRequest;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((s, c, ch, ss) => { return true; });
request.ProtocolVersion = HttpVersion.Version11;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
request.KeepAlive = true;
ServicePointManager.CheckCertificateRevocationList = true; ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
}
else
{
request = WebRequest.Create(url) as HttpWebRequest;
}
request.Method = "POST";
request.ContentType = "multipart/form-data;boundary=" + _boundary;
request.UserAgent = this.UserAgent;
request.Accept = this.Accept;
request.Referer = this.Referer;
request.CookieContainer = this.CookieContainer;
var end = $"--{this._boundary}--\r\n";
Type end_Name = end.GetType();
Console.WriteLine("变量end的类型是{0}",
end_Name.ToString());
//不自动关闭控制台,等待输入
Console.ReadLine();
this._postDatas.Add(this.Encoding.GetBytes(end));
var requestStream = request.GetRequestStream();
//StreamReader myStreamReader = new StreamReader(requestStream, Encoding.UTF8);
//string retString = myStreamReader.ReadToEnd();
//MessageBox.Show(retString);
//Type requestStream_Name = requestStream.GetType();
//Console.WriteLine("变量end的类型是{0}",
// requestStream_Name.ToString());
foreach (var item in this._postDatas)
{
requestStream.Write(item, 0, item.Length);
}
return request.GetResponse() as HttpWebResponse;
}
/// <summary>
/// 以POST方式向目标地址提交表单数据
/// </summary>
/// <param name="url">目标地址, http(s)://sample.com</param>
/// <returns>目标地址的响应</returns>
public async Task<HttpWebResponse> PostAsync(string url)
{
if (string.IsNullOrWhiteSpace(url))
throw new ArgumentNullException(nameof(url));
HttpWebRequest request = null;
if (url.ToLowerInvariant().StartsWith("https"))
{
request = WebRequest.Create(url) as HttpWebRequest;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((s, c, ch, ss) => { return true; });
request.ProtocolVersion = HttpVersion.Version11;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
request.KeepAlive = true;
ServicePointManager.CheckCertificateRevocationList = true; ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
}
else
{
request = WebRequest.Create(url) as HttpWebRequest;
}
request.Method = "POST";
request.ContentType = "multipart/form-data;boundary=" + _boundary;
request.UserAgent = this.UserAgent;
request.Accept = this.Accept;
request.Referer = this.Referer;
request.CookieContainer = this.CookieContainer;
var end = $"--{this._boundary}--\r\n";
Type end_Name = end.GetType();
Console.WriteLine("变量end的类型是{0}",
end_Name.ToString());
//不自动关闭控制台,等待输入
Console.ReadLine();
this._postDatas.Add(this.Encoding.GetBytes(end));
var requestStream = await request.GetRequestStreamAsync();
Type requestStream_Name = requestStream.GetType();
Console.WriteLine("变量end的类型是{0}",
requestStream_Name.ToString());
foreach (var item in this._postDatas)
{
await requestStream.WriteAsync(item, 0, item.Length);
}
return await request.GetResponseAsync() as HttpWebResponse;
}
}
一些比较好的链接
Http请求中Accept、Content-Type讲解以及在Spring MVC中的应用
这个链接非常清楚明白,也有一些参考代码
C# 模拟 HTTP POST请求
这个链接中有两个代码
第一个:用于以 POST 方式向目标地址提交表达数据,application/x-www-form-urlencoded 编码方式:这个没使用
第二个:用于以 POST 方式向目标地址提交表单数据, 仅适用于包含文件的请求,multipart/form-data;
第二个代码我们经过测试可以使用,就是我上面贴出的代码。
C#中HttpClient使用注意:预热与长连接
这篇博文写出了如何让服务器和客服端保持持续连接,减少耗时。
C#网络编程之Http请求
这篇博客写出了客户端:application/json接收,multipart/form-data发送
服务器端接受的代码
C# 网络请求
写了接口,不写程序调用可以是使用postman 发包看看情况,简单写了HttpClient,HttpWebRequest的post和get请求方式,写的还是比较简洁。