using Common.Extends;
using CustFramework.Constants;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
namespace Common.Helper
{
/// <summary>
/// 模拟请求帮助类
/// </summary>
public static class HttpHelper
{
/// <summary>
/// nlog util
/// </summary>
private static readonly NLogHelper NLogHelper = new NLogHelper();
/// <summary>
/// 获取 或 设置 跟踪code
/// </summary>
/// <param name="defaultValue"></param>
/// <returns></returns>
public static string GetOrSetTraceCode(string defaultValue = null)
{
// 为空情况(弃用,自定义HttpContext会导致异步报错):
// 1.异步编程:在 async/await 之后,执行可能跳转到另一个线程,该线程没有原始的 HttpContext。
// 2.Web API 自托管(Self - Host):不依赖于 IIS 和 System.Web。
// 3.后台线程:手动启动的 Task.Run 或 ThreadPool 线程。
// 4.OWIN 中间件:在某些 OWIN 管道阶段可能无法访问。
// 5.单元测试
if (HttpContext.Current?.Items == null)
{
//var mockContext = new HttpContext(
// new HttpRequest("Mock", "https://Mock.com", ""),
// new HttpResponse(new StringWriter())
//);
//var httpContextAccessorType = Type.GetType("System.Web.HttpContext, System.Web");
//var currentProperty = httpContextAccessorType?.GetProperty("Current");
//currentProperty?.SetValue(null, mockContext);
//HttpContext.Current = mockContext;
return defaultValue ?? string.Empty;
}
var traceCode = HttpContext.Current.Items[AppSettingConstant.HttpTraceCodeKey]?.ToString();
if (string.IsNullOrWhiteSpace(traceCode))
{
traceCode = string.IsNullOrWhiteSpace(defaultValue) ? Guid.NewGuid().ToString() : defaultValue;
HttpContext.Current.Items[AppSettingConstant.HttpTraceCodeKey] = traceCode;
}
return traceCode;
}
/// <summary>
/// Post 请求
/// </summary>
/// <typeparam name="T">请求参数 类型</typeparam>
/// <typeparam name="ReturnT">返回对象 类型</typeparam>
/// <param name="url">请求路径</param>
/// <param name="postData">氢气参数</param>
/// <returns></returns>
public static ReturnT Post<T, ReturnT>(string url, T postData)
{
var postDataStr = postData.ToJson();
var rspStr = Post(url, postDataStr);
return rspStr.IsNullOrWhiteSpace() ? default : rspStr.ToObject<ReturnT>();
}
/// <summary>
/// Post 请求
/// </summary>
/// <typeparam name="T">请求参数 类型</typeparam>
/// <typeparam name="ReturnT">返回对象 类型</typeparam>
/// <param name="url">请求路径</param>
/// <param name="postData">氢气参数</param>
/// <returns></returns>
public static ReturnT Post<ReturnT>(string url, string postDataStr)
{
var rspStr = Post(url, postDataStr);
return rspStr.IsNullOrWhiteSpace() ? default : rspStr.ToObject<ReturnT>();
}
/// <summary>
/// Post 请求
/// </summary>
/// <param name="url"></param>
/// <param name="postDataStr"></param>
/// <returns></returns>
public static string Post(string url, string postDataStr)
{
var rspTuple = SimulateRequest(HttpType.Post, url, postDataStr: postDataStr);
return rspTuple.Item1 ? rspTuple.Item2 : string.Empty;
}
/// <summary>
/// Post 请求
/// </summary>
/// <typeparam name="ReturnT">返回对象 类型</typeparam>
/// <param name="url">模拟请求路径</param>
/// <param name="headers">hander参字典</param>
/// <param name="isHttpSuccessStatusOnly200">http成功状态码,是否只判断200 ? 只有200才算请求成功 : 非200也需要处理响应(NTQS)</param>
/// <returns>item1:请求成功/失败;item2:请求响应数据;item3:错误信息</returns>
public static Tuple<bool, ReturnT, string> GetTupleT<ReturnT>(string url, Dictionary<string, string> headers = null, bool isHttpSuccessStatusOnly200 = true)
{
var rspTuple = GetTupleStr(url, headers, isHttpSuccessStatusOnly200: isHttpSuccessStatusOnly200);
ReturnT returnT = rspTuple.Item1 ? rspTuple.Item2.ToObject<ReturnT>() : default;
return new Tuple<bool, ReturnT, string>(rspTuple.Item1, returnT, rspTuple.Item3);
}
/// <summary>
/// Post 请求
/// </summary>
/// <param name="url">模拟请求路径</param>
/// <param name="headers">hander参字典</param>
/// <param name="isHttpSuccessStatusOnly200">http成功状态码,是否只判断200 ? 只有200才算请求成功 : 非200也需要处理响应(NTQS)</param>
/// <returns>item1:请求成功/失败;item2:请求响应数据;item3:错误信息</returns>
public static Tuple<bool, string, string> GetTupleStr(string url, Dictionary<string, string> headers = null, bool isHttpSuccessStatusOnly200 = true)
{
return SimulateRequest(HttpType.Get, url, headers: headers, isHttpSuccessStatusOnly200: isHttpSuccessStatusOnly200);
}
/// <summary>
/// 模拟请求
/// </summary>
/// <param name="httpType">模拟请求类型:GET / POST</param>
/// <param name="url">模拟请求路径</param>
/// <param name="postDataStr">Post请求参数</param>
/// <param name="headers">hander参字典</param>
/// <param name="isHttpSuccessStatusOnly200">http成功状态码,是否只判断200 ? 只有200才算请求成功 : 非200也需要处理响应(NTQS)</param>
/// <returns>item1:请求成功/失败;item2:请求响应数据;item3:错误信息</returns>
public static Tuple<bool, string, string> SimulateRequest(HttpType httpType, string url, string postDataStr = null, Dictionary<string, string> headers = null, bool isHttpSuccessStatusOnly200 = true)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var rspStr = string.Empty;
Stream reqStream = null;
HttpWebResponse response = null;
Stream rspStream = null;
StreamReader rspReader = null;
var httpTypeStr = httpType.ToString();
var traceCode = $"HttpHelper.{httpTypeStr}({url}) TraceCode:{GetOrSetTraceCode()} ";
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
// 设置模拟请求类型
request.Method = httpTypeStr;
//request.UserAgent = "Mozilla/5.0";
request.KeepAlive = false;
request.ReadWriteTimeout = 20 * 1000;
request.ContentType = "application/json;charset=utf-8";
// 添加请求头
if (headers != null)
{
foreach (var header in headers)
{
request.Headers[header.Key] = header.Value;
}
}
// post请求绑定body参数
if (httpType == HttpType.Post && !postDataStr.IsNullOrWhiteSpace())
{
traceCode += $"Param:{postDataStr} ";
var postData = Encoding.UTF8.GetBytes(postDataStr);
request.ContentLength = postData.Length;
reqStream = request.GetRequestStream();
reqStream.Write(postData, 0, postData.Length);
}
// 默认编码
var encoding = string.Empty;
try
{
response = (HttpWebResponse)request.GetResponse();
rspStream = response.GetResponseStream();
encoding = response.ContentEncoding;
}
catch (WebException webEx)
{
// 接口返回的状态码不正常,但是要求处理响应
if (!isHttpSuccessStatusOnly200 && webEx.Response is HttpWebResponse errorResponse && (int)errorResponse.StatusCode == 422)
{
rspStream = errorResponse.GetResponseStream();
encoding = errorResponse.ContentEncoding;
}
else
throw webEx;
}
if (encoding == null || encoding.Length < 1)
encoding = "UTF-8";
rspReader = new StreamReader(rspStream, Encoding.GetEncoding(encoding));
rspStr = rspReader.ReadToEnd();
}
catch (Exception ex)
{
var message = $"{traceCode} Error:{ex.Message}";
NLogHelper.Error(ex, message);
return new Tuple<bool, string, string>(false, string.Empty, $"Http[{httpTypeStr}]请求异常 {ex.Message}");
}
finally
{
if (rspReader != null) rspReader.Close();
if (rspStream != null) rspStream.Close();
if (response != null) response.Close();
if (reqStream != null) reqStream.Close();
stopwatch.Stop();
var actionDuration = new TimeSpan(stopwatch.ElapsedTicks).ToString();
NLogHelper.Debug($"{traceCode}Duration:{actionDuration}\r\nResponse:{rspStr}");
}
return new Tuple<bool, string, string>(true, rspStr, string.Empty);
}
}
/// <summary>
/// 模拟请求类型
/// </summary>
public enum HttpType
{
Get,
Post
}
}
// 默认编码
var encoding = string.Empty;
try
{
response = (HttpWebResponse)request.GetResponse();
rspStream = response.GetResponseStream();
encoding = response.ContentEncoding;
}
catch (WebException webEx)
{
// 接口返回的状态码不正常,但是要求处理响应
if (!isHttpSuccessStatusOnly200 && webEx.Response is HttpWebResponse errorResponse && (int)errorResponse.StatusCode == 422)
{
rspStream = errorResponse.GetResponseStream();
encoding = errorResponse.ContentEncoding;
}
else
throw webEx;
}
if (encoding == null || encoding.Length < 1)
encoding = "UTF-8";
rspReader = new StreamReader(rspStream, Encoding.GetEncoding(encoding));
rspStr = rspReader.ReadToEnd();