11.服务调用HTTP
1. HttpWebRequest
1.1 属性方法
1. HttpWebRequest
常用属性
Method
属性
获取或设置请求的方法, 默认值为 GET(可选: GET、HEAD、POST、PUT、DELETE、TRACE 或 OPTIONS)
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
req.Method = "POST";
Timeout
属性
获取或设置 GetResponse()
和 GetRequestStream()
方法的超时值,必须在调用方法之前设置,适用于整个请求和响应,默认值为100000毫秒 (100 秒),如果未在超时期限内返回资源,则该请求将引发异常,并将 WebException.Status
属性设置为 WebExceptionStatus.Timeout
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
req.Method = "POST";
req.Timeout = 20000; // 20秒
Accept
属性
获取或设置 Accept
HTTP 标头的值,默认值是 null
,设置 null
则清除 Accept
HttpWebRequest req =(HttpWebRequest)WebRequest.Create("url");
req.Accept="image/*";
AutomaticDecompression
属性
获取或设置所使用的解压缩类型
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
req.AutomaticDecompression = DecompressionMethods.GZip;
ContentLengt
h 属性
获取或设置 Content-length
HTTP 标头要发送到 Internet 资源的数据的字节数,默认值为 -1,该值指示尚未设置该属性,并且没有要发送的请求数据
ContentLength
将属性设置为某个值后,必须将该字节数写入到通过调用方法或同时调用方法返回的请求流中
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
req.Method = "POST";
string postData = "";
byte[] bs = Encoding.UTF8.GetBytes(postData);
req.ContentType = "application/json; charset=utf-8"; // 上传参数类型
req.ContentLength = bs.Length; // 上传数据的字节长度
req.GetRequestStream().Write(bs, 0, bs.Length); // 写入流中
ContentType
属性
获取或设置 Content-type
HTTP 标头的值,包含请求的媒体类型
req.ContentType = "application/json; charset=utf-8"; // 上传参数类型 json
req.ContentType = "application/x-www-form-urlencoded"; // from表单形式
CookieContainer
属性
获取或设置与此请求关联的 Cookie
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
req.CookieContainer.Add(new Cookie() { Name = "libai" });
DefaultCachePolicy
属性
获取或设置此请求的默认缓存策略
HaveResponse
属性
获取一个值,该值指示是否收到了来自 Internet 资源的响应,如果接收到了响应,则为 true
,否则为 false
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
req.Method = "POST";
string postData = "";
byte[] bs = Encoding.UTF8.GetBytes(postData);
req.ContentType = "application/json; charset=utf-8"; // 上传参数类型
req.ContentLength = bs.Length; // 上传数据的字节长度
req.GetRequestStream().Write(bs, 0, bs.Length); // 写入流中
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
if (req.HaveResponse)
{
}
Headers
属性
指定构成 HTTP 标头的名称/值对的集合
httpWebRequest.Headers.Add("Authorization", "Bearer asdgasdfasd");
2. HttpWebRequest
常用方法
GetResponse
方法
返回来自 Internet 资源的响应,返回 HttpWebResponse
对象
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
3. HttpWebResponse
常用属性
ContentType
属性
获取响应的内容类型(字符串),content-type
标头的值
ContentLength
属性
获取请求返回的内容的长度,由请求所返回的字节数
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Console.WriteLine(res.ContentLength);
Cookies
属性
获取或设置与此响应关联的 Cookie
using (var response = (HttpWebResponse) request.GetResponse())
{
foreach (Cookie cook in response.Cookies)
{
Console.WriteLine("Cookie:");
Console.WriteLine($"{cook.Name} = {cook.Value}");
Console.WriteLine($"Domain: {cook.Domain}");
Console.WriteLine($"Path: {cook.Path}");
Console.WriteLine($"Port: {cook.Port}");
Console.WriteLine($"Secure: {cook.Secure}");
Console.WriteLine($"When issued: {cook.TimeStamp}");
Console.WriteLine($"Expires: {cook.Expires} (expired? {cook.Expired})");
Console.WriteLine($"Don't save: {cook.Discard}");
Console.WriteLine($"Comment: {cook.Comment}");
Console.WriteLine($"Uri for comments: {cook.CommentUri}");
Console.WriteLine($"Version: RFC {(cook.Version == 1 ? 2109 : 2965)}");
Console.WriteLine($"String: {cook}");
}
}
Headers
属性
获取来自服务器的与此响应关联的标头
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
for(int i=0; i < res.Headers.Count; ++i)
{
Console.WriteLine("Name:{0}, Value:{1}",res.Headers.Keys[i],res.Headers[i]);
}
res.Close();
IsFromCache
属性
获取一个值,该值指示此响应是否为从缓存中获取的,如果此响应来自缓存,则为 true
;否则为 false
StatusCode
属性
获取响应的状态
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Console.WriteLine(res.StatusCode == HttpStatusCode.OK);
4. HttpWebResponse
常用方法
GetResponseStream
方法
获取流,该流用于读取来自服务器的响应的体,从请求的 Internet 资源返回数据流
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
HttpWebResponse res = (HttpWebResponse)req.GetResponse(); // 获取响应
Stream receiveStream = res.GetResponseStream(); // 获取响应流
Encoding encode = Encoding.GetEncoding("utf-8");
StreamReader readStream = new StreamReader(receiveStream, encode); // 以指定编码读取响应流
string responseContent = readStream.ReadToEnd(); // 读取
Console.WriteLine(responseContent);
Dispose
方法
释放由 WebResponse
对象使用的资源
Close
方法
将关闭响应流并释放与资源的连接,以供其他请求重用
1.2 创建请求
1. HTTP请求
从 WebRequest
派生的 HttpWebRequest
类,用以处理 HTTP 和 HTTPS 请求,需要将 WebRequest.Create
方法创建的 WebRequest
对象转换为 HttpWebRequest
类型,即可访问该请求特定于 HTTP 的属性
HttpWebResponse
对象可处理来自 HTTP 和 HTTPS 请求的响应,同样需要将 WebResponse
对象转换为 HttpWebResponse
类型
创建请求
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.contoso.com/");
2. FILE请求
提供 FileWebRequest
和 FileWebResponse
类来处理使用“file:”URI 方案的资源请求,提供文件系统实现,使用 WebRequest.Create
方法获取用于发出请求的对象,需要转换
3. FTP请求
提供的 FtpWebRequest
和 FtpWebResponse
类可处理使用“ftp:”方案的资源请求,实现文件传输协议 (FTP) 客户端,使用 WebRequest.Create
方法获取用于发出请求的对象,需要转换
1.3 请求步骤
1. 请求数据
// 1.创建请求实例
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.baidu.com");
// 2.设置请求对象中任何所需的属性值
req.Method = "GET";
req.Timeout = 20000;
req.Headers.Add("Authorization", "Bearer asdfagasd");
// 3.发送请求,返回响应
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
// 4.要获取包含由服务器发送的响应数据的流
Stream receiveStream = res.GetResponseStream();
Encoding encode = Encoding.GetEncoding("utf-8");
// 5.关闭响应对象
res.Close();
2. 发送数据
// 1.创建请求实例
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("url");
// 2.设置请求对象中任何所需的属性值
req.Timeout = 20000;
req.Headers.Add("Authorization", "Bearer asdfagasd");
// 3.指定允许使用请求发送数据的协议方法
req.Method = "POST";
// 4.将 ContentLength 属性设置为请求中包含的字节数
byte[] byteArray = new byte[0]; // 发送数据的字节
req.ContentLength = byteArray.Length;
// 5.将 ContentType 属性设置为适当的值
req.ContentType = "application/x-www-form-urlencoded"; // 表单
// 6.通过调用 GetRequestStream 方法获取包含请求数据的流,用于将发送数据写入到请求中
Stream dataStream = req.GetRequestStream();
// 7.将数据写入到请求流中
dataStream.Write(byteArray, 0, byteArray.Length);
// 8.关闭请求流
dataStream.Close();
// 9.发送请求,返回响应
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
// 10.获取包含由服务器发送的响应数据的流
Stream resultStream = res.GetResponseStream();
// 11. 转换为UTF8编码的数据
StreamReader readStream = new StreamReader(resultStream, Encoding.UTF8);
string responseContent = readStream.ReadToEnd();
// 关闭响应对象
res.Close();
1.4 处理错误
WebRequest
和 WebResponse
类都会引发系统异常(如 ArgumentException
)和特定于 Web 的异常(为 GetResponse
方法所引发的 WebException
)
每个 WebException
包括一个 Status
属性,它包含来自 WebExceptionStatus
枚举的一个值
枚举状态
当“状态”属性为“WebExceptionStatus.ProtocolError
”时,可以使用包含来自服务器的响应的 WebResponse
状态 | 描述 |
---|---|
ConnectFailure | 无法在传输级别联系远程服务 |
ConnectionClosed | 连接被提前关闭 |
KeepAliveFailure | 服务器已关闭与“保持的连接”标头设置建立的连接 |
NameResolutionFailure | 名称服务无法解析主机名 |
ProtocolError | 从服务器接收的响应是完整的,但指示在协议级别出现错误 |
ReceiveFailure | 无法从远程服务器接收完整的响应 |
RequestCanceled | 请求已被取消 |
SecureChannelFailure | 安全通道链接中出现错误 |
SendFailure | 无法向远程服务器发送完整的请求 |
ServerProtocolViolation | 服务器响应不是有效的 HTTP 响应 |
成功 | |
超时 | 在请求的超时设置内未接收到任何响应 |
TrustFailure | 无法验证服务器证书 |
MessageLengthLimitExceeded | 从服务器发送请求或接收响应时,接收到的消息超出指定限制 |
挂起 | 内部异步请求处于挂起状态 |
PipelineFailure | 此值支持 .NET Framework 基础结构,不能在代码中直接使用 |
ProxyNameResolutionFailure | 名称解析程序服务无法解析代理主机名 |
UnknownError | 出现未知类型的异常 |
简单示例
try
{
// 请求
}
catch (WebException webExcp)
{
// 异常信息
Console.WriteLine(webExcp.Message);
// 状态
WebExceptionStatus status = webExcp.Status;
if (status == WebExceptionStatus.ProtocolError)
{
HttpWebResponse httpResponse = (HttpWebResponse)webExcp.Response;
Console.WriteLine((int)httpResponse.StatusCode + " - "
+ httpResponse.StatusCode);
} else if(status == WebExceptionStatus.NameResolutionFailure)
{
Console.WriteLine("请求异常:名称服务无法解析主机名");
}
}
catch (Exception e)
{
Console.WriteLine("系统异常:" + e.Message);
}
1.5 常用示例
1. 请求内容
示例一:返回字符内容
using System;
using System.Net;
using System.IO;
using System.Text;
string GetHttpResponse(string url, int timeout)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Timeout = timeout;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8);
string resString = streamReader.ReadToEnd();
streamReader.Close();
responseStream.Close();
return resString;
}
示例二:返回字节数组,一般用于下载文件
public byte[] GetHttpResponseByte(string url, int timeout = 6000)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Timeout = timeout;
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
MemoryStream stmMemory = new MemoryStream();
byte[] arraryByte;
byte[] buffer = new byte[response.ContentLength];
int i;
while ((i = stream.Read(buffer, 0, buffer.Length)) > 0)
{
stmMemory.Write(buffer, 0, i);
}
arraryByte = stmMemory.ToArray();
stmMemory.Close();
stream.Close();
response.Close();
return arraryByte;
}
2. 表单提交
示例一
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("url");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
string requestForm = "userName=1693372175&userPassword=123456"; //拼接Form表单里的信息
byte[] postdatabyte = Encoding.UTF8.GetBytes(requestForm);
request.ContentLength = postdatabyte.Length;
Stream stream;
stream = request.GetRequestStream();
stream.Write(postdatabyte, 0, postdatabyte.Length); //设置请求主体的内容
stream.Close();
//接收响应
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream1 = response.GetResponseStream();
StreamReader sr = new StreamReader(stream1, Encoding.UTF8);
Console.WriteLine(sr.ReadToEnd());
示例二
using System;
using System.Net;
using System.Text;
using System.IO;
using System.Collections.Generic;
public string HttpPost(string url, string data = "",
IDictionary<string, string> parameters = null,
int timeout = 5000, CookieCollection cookies = null)
{
HttpWebRequest request = null;
request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.Timeout = timeout;
if (cookies != null)
{
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(cookies);
}
if (!string.IsNullOrWhiteSpace(data))
{
byte[] databytes = Encoding.UTF8.GetBytes(data);
using (Stream stream = request.GetRequestStream())
{
stream.Write(databytes, 0, databytes.Length);
}
}
else if (parameters != null && parameters.Count != 0)
{
StringBuilder buffer = new StringBuilder();
int i = 0;
foreach (string key in parameters.Keys)
{
if (i > 0)
{
buffer.AppendFormat("&{0}={1}", key, parameters[key]);
}
else
{
buffer.AppendFormat("{0}={1}", key, parameters[key]);
i++;
}
}
byte[] kvdata = Encoding.UTF8.GetBytes(buffer.ToString());
using (Stream stream = request.GetRequestStream())
{
stream.Write(kvdata, 0, kvdata.Length);
}
}
try
{
using (Stream stream = request.GetResponse().GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
using (Stream stream = ex.Response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
}
3. 上传文件
示例一:上传文件,无其他参数
// 初始化HttpWebRequest
HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create("http://localhost:7670/api/Update");
// 生成时间戳
string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = Encoding.ASCII.GetBytes(string.Format("\r\n--{0}--\r\n", strBoundary));
// 填报文类型
httpRequest.Method = "Post";
httpRequest.Timeout = 1000 * 120;
httpRequest.ContentType = "multipart/form-data; boundary=" + strBoundary;
// 封装HTTP报文头的流
StringBuilder sb = new StringBuilder();
sb.Append("--");
sb.Append(strBoundary);
sb.Append(Environment.NewLine);
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append("file");
sb.Append("\"; filename=\"");
sb.Append(@"E:\demo.xml");
sb.Append("\"");
sb.Append(Environment.NewLine);
sb.Append("Content-Type: ");
sb.Append("multipart/form-data;");
sb.Append(Environment.NewLine);
sb.Append(Environment.NewLine);
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(sb.ToString());
// 上传的文件
FileStream fileStream = new FileStream(@"D:\123.xml", FileMode.OpenOrCreate, FileAccess.Read);
// 计算报文长度
long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;
httpRequest.ContentLength = length;
// 将报文头写入流
Stream requestStream = httpRequest.GetRequestStream();
requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
// 将上传文件内容写入流 //每次上传4k 1024*4
byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
requestStream.Write(buffer, 0, bytesRead);
}
// 将报文尾部写入流
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
// 关闭流
requestStream.Close();
WebResponse webResponse = httpRequest.GetResponse();
Stream resultStream = webResponse.GetResponseStream();
StreamReader sr = new StreamReader(resultStream, Encoding.UTF8);
Console.WriteLine(sr.ReadToEnd());
示例二:上传文件+表单参数
Dictionary<string, object> nvc = new Dictionary<string, object>();
nvc.Add("id", "1000");
nvc.Add("name", "user1");
HttpUploadFile("http://localhost:7670/api/Update", @"D:\123.xml",
"file", "multipart/form-data", nvc);
public static void HttpUploadFile(string url, string filePath, string paramName,
string contentType, Dictionary<string, object> pairs)
{
string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
request.Credentials = CredentialCache.DefaultCredentials;
Stream requestStream = request.GetRequestStream();
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
foreach (string key in pairs.Keys)
{
requestStream.Write(boundarybytes, 0, boundarybytes.Length);
string formitem = string.Format(formdataTemplate, key, pairs[key]);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
requestStream.Write(formitembytes, 0, formitembytes.Length);
}
requestStream.Write(boundarybytes, 0, boundarybytes.Length);
string header = string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n", paramName, filePath, contentType);
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
requestStream.Write(headerbytes, 0, headerbytes.Length);
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
requestStream.Write(buffer, 0, bytesRead);
}
fileStream.Close();
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
requestStream.Write(trailer, 0, trailer.Length);
requestStream.Close();
WebResponse webResponse = null;
try
{
webResponse = request.GetResponse();
Stream responseStream = webResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);
string result = streamReader.ReadToEnd();
}
catch (Exception ex)
{
if (webResponse != null)
{
webResponse.Close();
webResponse = null;
}
}
}
示例三:文件+参数(一般情况下这里面的 Name、Filename 参数意义不大,完全取决于服务端如何使用)
using System;
using System.Text;
using System.Collections.Generic;
using System.Net;
using System.IO;
public class UploadFile
{
public UploadFile()
{
ContentType = "application/octet-stream";
}
public string Name { get; set; }
public string Filename { get; set; }
public string ContentType { get; set; }
public byte[] StreamContent { get; set; }
}
public string HttpPostFile(string url,
IEnumerable<UploadFile> files,
IDictionary<string, string> values)
{
var request = WebRequest.Create(url);
request.Method = "POST";
var boundary = "------------------" + DateTime.Now.ToString("yyyyMMddHHmmss");
request.ContentType = "multipart/form-data; boundary=" + boundary;
boundary = "--" + boundary;
using (var requestStream = request.GetRequestStream())
{
//遍历键值
foreach (string name in values.Keys)
{
var buffer = Encoding.UTF8.GetBytes(boundary + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
}
//遍历文件
foreach (var file in files)
{
var buffer = Encoding.UTF8.GetBytes(boundary + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", file.Name, file.Filename, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(string.Format("Content-Type: {0}{1}{1}", file.ContentType, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
requestStream.Write(file.StreamContent, 0, file.StreamContent.Length);
buffer = Encoding.UTF8.GetBytes(Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
}
var boundaryBuffer = Encoding.UTF8.GetBytes(boundary + "--");
requestStream.Write(boundaryBuffer, 0, boundaryBuffer.Length);
}
try
{
var stream = new MemoryStream();
request.GetResponse().GetResponseStream().CopyTo(stream);
return Encoding.UTF8.GetString(stream.ToArray());
}
catch (WebException ex)
{
var stream = new MemoryStream();
ex.Response.GetResponseStream().CopyTo(stream);
return Encoding.UTF8.GetString(stream.ToArray());
}
}
接受文件,参数在From中
[Route("api/Update")]
public class UpdateController : ApiController
{
public IHttpActionResult Test()
{
HttpRequest request = System.Web.HttpContext.Current.Request;
HttpFileCollection FileCollect = request.Files;
if (FileCollect.Count > 0) //如果集合的数量大于0
{
foreach (string str in FileCollect)
{
//用key获取单个文件对象HttpPostedFile
HttpPostedFile FileSave = FileCollect[str];
string imgName = DateTime.Now.ToString("yyyyMMddhhmmss");
string AbsolutePath = FileSave.FileName; // 请求头中的地址
FileSave.SaveAs(AbsolutePath); //将上传的东西保存
}
}
return Json("键值对数目:" + FileCollect.Count);
}
}
4. 下载文件
//建立一个WEB请求,返回HttpWebRequest对象
HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create("");
//设置接收对象的范围为0-10000000字节
hwr.AddRange(0, 10000000);
//流对象使用完后自动关闭
using (Stream stream = hwr.GetResponse().GetResponseStream())
{
//文件流,流信息读到文件流中,读完关闭
using (FileStream fs = File.Create(@"c:\f.xml"))
{
//建立字节组,并设置它的大小是多少字节
byte[] bytes = new byte[102400];
int n = 1;
while (n > 0)
{
//一次从流中读多少字节,并把值赋给N,当读完后,N为0,并退出循环
n = stream.Read(bytes, 0, 10240);
fs.Write(bytes, 0, n); //将指定字节的流信息写入文件流中
}
}
}
5. 通用类库
public static class RequestUtility
{
/// <summary>
/// 发送GET请求
/// </summary>
/// <param name="url">请求URL,如果需要传参,在URL末尾加上“?+参数名=参数值”即可</param>
/// <returns></returns>
public static string HttpGet(string url)
{
//创建
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
//设置请求方法
httpWebRequest.Method = "GET";
//请求超时时间
httpWebRequest.Timeout = 20000;
// 设置header信息,如果有
//发送请求
HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
//利用Stream流读取返回数据
StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.UTF8);
//获得最终数据,一般是json
string responseContent = streamReader.ReadToEnd();
streamReader.Close();
httpWebResponse.Close();
return responseContent;
}
/// <summary>
/// 发送POST请求
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="data">请求参数,json字符串</param>
/// <returns></returns>
public static string HttpPost(string url, string data, bool getToken = false)
{
string responseContent = "";
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
//字符串转换为字节码
byte[] bs = Encoding.UTF8.GetBytes(data);
//参数类型
httpWebRequest.ContentType = "application/json; charset=utf-8";
//设置header信息,如果有
//参数数据长度
httpWebRequest.ContentLength = bs.Length;
//设置请求类型
httpWebRequest.Method = "POST";
//设置超时时间
httpWebRequest.Timeout = 20000;
//将参数写入请求地址中
httpWebRequest.GetRequestStream().Write(bs, 0, bs.Length);
//发送请求
HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
//读取返回数据
StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.UTF8);
responseContent = streamReader.ReadToEnd();
streamReader.Close();
httpWebResponse.Close();
httpWebRequest.Abort();
}
catch (Exception ex)
{
responseContent = ex.Message;
}
return responseContent;
}
}
2. HttpClient
2.1 简单使用
简单使用
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://www.baidu.com");
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
使用客户端工厂
容器中注册服务
services.AddHttpClient();
在使用时通过注入工厂创建实例
[Route("api/[controller]")]
[ApiController]
public class HttpClientController : ControllerBase
{
IHttpClientFactory _httpClientFactory;
public HttpClientController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
[Route(nameof(Index))]
public async Task<IActionResult> Index()
{
var client = _httpClientFactory.CreateClient();
var result = await client.GetAsync("http://aspnetcore.online/api/resource");
return Ok(result);
}
}
2.2 集成Polly
Polly
是一个用来故障处理库,允许开发者在 Http
请求中添加“重试、熔断器、超时等”策略
安装引用
Microsoft.Extensions.Http.Polly
添加策略
var timeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10));
services.AddHttpClient("baidu")
.AddPolicyHandler(request => timeout)
.AddTransientHttpErrorPolicy(p=>p.RetryAsync(3));
2.5 最佳实践
创建尽可能少的
HttpClient
实例
using (var client = new HttpClient()) { /*todo*/ }
一般都是以这种方式使用,但这种情况下会出现一个严重的问题,在不停的调用情形下,tcp
连接数会被耗尽,虽然使用 using
方式调用 HttpClient
并在退出前调用 Dispose()
方法将 HttpClient
释放,但是 tcp
连接仍然处于保持状态,在240s后才会自动断开
正确的做法是创建尽可能少的实例,将针对某一类请求的 HttpClient
实例放入类的静态变量中,甚至放入静态工具类中,作为单例使用
虽然这样解决了"套接字资源耗尽异常",但是又带来了新的问题,熬不过DNS生存时间(TTL),当主机 DNS 更新时,又可能产生异常,提示无法解析主机名称,因为单例 HttpClient
不会随着主机DNS更新而更新
使用客户端工厂
在.NET Core 2.1后,微软引入 ·HttpClientFactory· 彻底解决这个问题,工厂模式的职责是负责创建对象,具体实现原理简述为:HttpClientFactory
内部管理着一个连接句柄池,对每一个 HttpClient
使用一个句柄进行跟踪管理,当该实例使用完毕后,句柄仍然控制资源释放,在短期大量处理时,可以将这部分句柄完成对不同实例的跟踪管理,使得句柄,也就是相应的套接字生命周期延长,对套接字完成了复用
//首先,在Startup中的ConfigureService注册我们的HttClient服务
service.AddHttpClien();
//在使用的地方通过依赖注入来请求IHttpClientFactory,并创建HttpClient实例
[Route("api/[controller]")]
[ApiController]
public class HttpClientController : ControllerBase
{
IHttpClientFactory _httpClientFactory;
public HttpClientController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
[Route(nameof(Index))]
public async Task<IActionResult> Index()
{
var client = _httpClientFactory.CreateClient();
var result = await client.GetAsync("http://aspnetcore.online/api/resource");
return Ok(result);
}
}
针对性地分配
HttpClient
实例
由于在DI容器中注册了唯一的 HttpClientFactory
,意味着通过 HttpClientFactory
创建的 HttpClient
可能是同一个配置和参数,如果需要不同配置的 HttpClient
,可以通过“命名”的方式注册不同的 HttpClient
// 注册一个baidu的实例
services.AddHttpClient("baidu", c =>
{
c.BaseAddress = new Uri("https://www.baidu.com");
c.DefaultRequestHeaders.Add("Accept", "application/json");
});
// 通过工厂,创建指定命名实例
var client = _clientFactory.CreateClient("baidu");