HttpWebRequest、HttpWebResponse、HttpClient、WebClient等http網絡訪問類的使用示例匯總


工作中長期需要用到通過HTTP調用API以及文件上傳下載,積累了不少經驗,現在將各種不同方式進行一個匯總。

首先是HttpWebRequest:

/// <summary>
/// 向服務器發送Request
/// </summary>
/// <param name="url">字符串</param>
/// <param name="method">枚舉類型的方法Get或者Post</param>
/// <param name="body">Post時必須傳值</param>
/// <param name="timeoutSeconds">超時時間,單位秒</param>
/// <returns></returns>
public static string Request(string url, MethodEnum method, string body = "", int timeoutSeconds = 15000)
{
    if (!IsConnectedInternet())
        return "網絡連接錯誤,請稍后再試。";

    try
    {
        GC.Collect();
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Timeout = timeoutSeconds;
        request.Method = method.ToString();
        21         //如果是Post的話,則設置body
        if (method == MethodEnum.POST)
        {
            request.ContentType = "application/json";
            request.KeepAlive = false;
            byte[] requestBody = Encoding.UTF8.GetBytes(body);
            request.ContentLength = requestBody.Length;

            Stream requestStream = request.GetRequestStream();
            requestStream.Write(requestBody, 0, requestBody.Length);
        }

        return Response(request);
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null)
            return ex.InnerException.Message;
        if (ex.Message.Contains("已取消一個任務"))
            return "連接服務器超時,請重試";
        if (ex.Message.Contains("404"))
            return "連接服務器404,請重試";
        return ex.Message;
    }
}

然后是HttpWebResponse:

/// <summary>
/// 返回Response數據
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private static string Response(HttpWebRequest request)
{
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    string jsonRead = "";

    if (response.StatusCode != HttpStatusCode.OK)
    {
        return response.StatusCode.ToString();
    }
    //接收過程
    if (response.GetResponseStream() != null)
    {
        StreamReader myStreamReader = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8);
        jsonRead = myStreamReader.ReadToEnd();
        myStreamReader.Close();
    }
    response.Close();
    request.Abort();

    return jsonRead;
}

 上面兩個方法需要配合使用,皆為同步方式。當然也可以將上面兩個方法合並到一個方法中,可以參見接下來這個方法。

另外是使用HttpWebRequest和HttpWebResponse進行文件上傳,采用同步方法異步回調方式:

public static void UploadFile(string url, string filePath, string fileName, Action<string> callback)
{
    // 時間戳,用做boundary
    string timeStamp = DateTime.Now.Ticks.ToString("x");

    //根據uri創建HttpWebRequest對象
    HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(url));
    httpReq.Method = "POST";
    httpReq.AllowWriteStreamBuffering = false; //對發送的數據不使用緩存
    httpReq.Timeout = 300000;  //設置獲得響應的超時時間(300秒)
    httpReq.ContentType = "multipart/form-data; boundary=" + timeStamp;

    //文件
    FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    BinaryReader binaryReader = new BinaryReader(fileStream);

    //頭信息
    string boundary = "--" + timeStamp;
    string dataFormat = boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";filename=\"{1}\"\r\nContent-Type:application/octet-stream\r\n\r\n";
    string header = string.Format(dataFormat, "file", Path.GetFileName(filePath));
    byte[] postHeaderBytes = Encoding.UTF8.GetBytes(header);

    //結束邊界
    byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + timeStamp + "--\r\n");

    long length = fileStream.Length + postHeaderBytes.Length + boundaryBytes.Length;

    httpReq.ContentLength = length;//請求內容長度

    try
    {
        //每次上傳4k
        int bufferLength = 4096;
        byte[] buffer = new byte[bufferLength];

        //已上傳的字節數
        long offset = 0;
        int size = binaryReader.Read(buffer, 0, bufferLength);
        Stream postStream = httpReq.GetRequestStream();

        //發送請求頭部消息
        postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

        while (size > 0)
        {
            postStream.Write(buffer, 0, size);
            offset += size;
            size = binaryReader.Read(buffer, 0, bufferLength);
        }

        //添加尾部邊界
        postStream.Write(boundaryBytes, 0, boundaryBytes.Length);
        postStream.Close();

        string returnValue = "";
        //獲取服務器端的響應
        using (HttpWebResponse response = (HttpWebResponse)httpReq.GetResponse())
        {
            Stream receiveStream = response.GetResponseStream();
            StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
            returnValue = readStream.ReadToEnd();
            response.Close();
            readStream.Close();
        }
        
        callback?.Invoke(returnValue);
    }
    catch (Exception)
    {
        callback?.Invoke("");
    }
    finally
    {
        fileStream.Close();
        binaryReader.Close();
    }
}

上面還用到一個enum叫MethodEnum,包含GET和POST兩個枚舉值。

還有一個比較特殊的POST方法:

public static string HttpPostFormData(string url, Dictionary<string, string> dic)
{
    try
    {
        GC.Collect();
        // 時間戳,用做boundary
        string timeStamp = DateTime.Now.Ticks.ToString("x");
        string boundary = "----" + timeStamp;

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = WebRequestMethods.Http.Post;
        request.ContentType = "multipart/form-data; boundary=" + boundary;
        request.KeepAlive = true;
        request.Timeout = 3000;

        var stream = new MemoryStream();

        //頭信息
        string dataFormat = "--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}\r\n";
        foreach (string key in dic.Keys)
        {
            string s = string.Format(dataFormat, key, dic[key]);
            byte[] data = Encoding.UTF8.GetBytes(s);
            stream.Write(data, 0, data.Length);
        }

        //結束邊界
        byte[] boundaryBytes = Encoding.ASCII.GetBytes("--" + boundary + "--");
        stream.Write(boundaryBytes, 0, boundaryBytes.Length);

        request.ContentLength = stream.Length;//請求內容長度

        Stream requestStream = request.GetRequestStream(); //寫入請求數據
        stream.Position = 0L;
        stream.CopyTo(requestStream);
        stream.Close();

        requestStream.Close();

        return Response(request);
    }
    catch (Exception e)
    {
        return e.Message;
    }
}

然后是HttpClient,這個類提供的都是異步方法,下面包含了POST、GET、PUT、DELETE四個方法,還有一個SEND方法稍加改動即可實現:

public static async void AsyncPost(string url, string body, Action<RequestResult> callback, int timeoutSeconds = 10)
{
    var requestResult = new RequestResult();
    if (!IsConnectedInternet())
    {
        requestResult.Message = "網絡連接錯誤,請稍后再試。";
        callback?.Invoke(requestResult);
        return;
    }

    try
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6");
            client.Timeout = new TimeSpan(0, 0, timeoutSeconds);
            //byte[] requestBody = Encoding.UTF8.GetBytes(body);
            HttpContent content = new StringContent(body);
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            var response = await client.PostAsync(url, content);
            //確保HTTP成功狀態值
            response.EnsureSuccessStatusCode();
            //await異步讀取最后的JSON
            await response.Content.ReadAsStringAsync().ContinueWith(t =>
            {
                if (t.IsCompleted)
                {
                    requestResult.IsSuccess = true;
                    requestResult.Result = t.Result;
                    callback?.Invoke(requestResult);
                }
            });
        }
    }
    catch (Exception e)
    {
        if (e.InnerException != null)
            requestResult.Message = e.InnerException.Message;
        else if (e.Message.Contains("已取消一個任務"))
            requestResult.Message = "連接服務器超時,請重試";
        else if (e.Message.Contains("404"))
            requestResult.Message = "連接服務器404,請重試";
        else
            requestResult.Message = e.Message;
        callback?.Invoke(requestResult);
    }
}

public static async void AsyncGet(string url, Action<RequestResult> callback, int timeoutSeconds = 10)
{
    var requestResult = new RequestResult();
    if (!IsConnectedInternet())
    {
        requestResult.Message = "網絡連接錯誤,請稍后再試。";
        callback?.Invoke(requestResult);
        return;
    }

    try
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6");
            client.Timeout = new TimeSpan(0, 0, timeoutSeconds);
            var response = await client.GetAsync(url);
            //確保HTTP成功狀態值
            response.EnsureSuccessStatusCode();
            //await異步讀取最后的JSON
            await response.Content.ReadAsStringAsync().ContinueWith(t =>
            {
                if (t.IsCompleted)
                {
                    requestResult.IsSuccess = true;
                    requestResult.Result = t.Result;
                    callback?.Invoke(requestResult);
                }
            });
        }
    }
    catch (Exception e)
    {
        if (e.InnerException != null)
            requestResult.Message = e.InnerException.Message;
        else if (e.Message.Contains("已取消一個任務"))
            requestResult.Message = "連接服務器超時,請重試";
        else if (e.Message.Contains("404"))
            requestResult.Message = "連接服務器404,請重試";
        else
            requestResult.Message = e.Message;
        callback?.Invoke(requestResult);
    }
}

public static async void AsyncPut(string url, string body, Action<RequestResult> callback, int timeoutSeconds = 10)
{
    var requestResult = new RequestResult();
    if (!IsConnectedInternet())
    {
        requestResult.Message = "網絡連接錯誤,請稍后再試。";
        callback?.Invoke(requestResult);
        return;
    }

    try
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6");
            client.Timeout = new TimeSpan(0, 0, timeoutSeconds);
            HttpContent content = new StringContent(body);
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            var response = await client.PutAsync(url, content);
            //確保HTTP成功狀態值
            response.EnsureSuccessStatusCode();
            //await異步讀取最后的JSON
            await response.Content.ReadAsStringAsync().ContinueWith(t =>
            {
                if (t.IsCompleted)
                {
                    requestResult.IsSuccess = true;
                    requestResult.Result = t.Result;
                    callback?.Invoke(requestResult);
                }
            });
        }
    }
    catch (Exception e)
    {
        if (e.InnerException != null)
            requestResult.Message = e.InnerException.Message;
        else if (e.Message.Contains("已取消一個任務"))
            requestResult.Message = "連接服務器超時,請重試";
        else if (e.Message.Contains("404"))
            requestResult.Message = "連接服務器404,請重試";
        else
            requestResult.Message = e.Message;
        callback?.Invoke(requestResult);
    }
}

public static async void AsyncDelete(string url, Action<RequestResult> callback, int timeoutSeconds = 10)
{
    var requestResult = new RequestResult();
    if (!IsConnectedInternet())
    {
        requestResult.Message = "網絡連接錯誤,請稍后再試。";
        callback?.Invoke(requestResult);
        return;
    }

    try
    {
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6");
            client.Timeout = new TimeSpan(0, 0, timeoutSeconds);
            var response = await client.DeleteAsync(url);
            //確保HTTP成功狀態值
            response.EnsureSuccessStatusCode();
            //await異步讀取最后的JSON
            await response.Content.ReadAsStringAsync().ContinueWith(t =>
            {
                if (t.IsCompleted)
                {
                    requestResult.IsSuccess = true;
                    requestResult.Result = t.Result;
                    callback?.Invoke(requestResult);
                }
            });
        }
    }
    catch (Exception e)
    {
        if (e.InnerException != null)
            requestResult.Message = e.InnerException.Message;
        else if (e.Message.Contains("已取消一個任務"))
            requestResult.Message = "連接服務器超時,請重試";
        else if (e.Message.Contains("404"))
            requestResult.Message = "連接服務器404,請重試";
        else
            requestResult.Message = e.Message;
        callback?.Invoke(requestResult);
    }
}

上面使用到的RequestResult類:

public class RequestResult : IDisposable
{
    public bool IsSuccess { get; set; }

    public string Result { get; set; }

    public string Message { get; set; }

    public RequestResult(bool isSuccess = false, string result = "", string message = "")
    {
        IsSuccess = isSuccess;
        Result = result;
        Message = message;
    }

    ~RequestResult()
    {
        Dispose();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);//不需要再調用本對象的Finalize方法
    }

    protected virtual void Dispose(Boolean disposing)
    {
        if (disposing)
        {
            //--- 清理托管資源 ---//
        }

        //--- 清理非托管資源 ---//
    }
}

還有一個判斷Windows系統網絡連接狀態的方法:

#region 網絡狀態測試

[DllImport("winInet.dll")]
private static extern bool InternetGetConnectedState(ref int dwFlag, int dwReserved);

/// <summary>
/// 用於檢查網絡是否可以連接互聯網,true表示連接成功,false表示連接失敗 
/// </summary>
/// <returns></returns>
private static bool IsConnectedInternet()
{
    int description = 0;
    return InternetGetConnectedState(ref description, 0);
}

#endregion

最后是使用WebClient進行文件下載,這里使用了Task異步方式,也可以改為普通方法或靜態方法:

/// <summary>
/// 下載文件
/// </summary>
/// <param name="fileUrl">文件地址</param>
/// <param name="filePath">文件的本地路徑</param>
/// <returns>文件在本地的存儲路徑</returns>
private Task GetFileLocalPath(string fileUrl, string filePath)
{
    return Task.Run(() =>
    {
        try
        {
            using (var mc = new WebClient())
            {
                mc.DownloadFile(new Uri(fileUrl), filePath);
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("下載文件時出現異常。", ex);
        }
    });
}

/// <summary>
/// 下載文件
/// </summary>
/// <param name="fileUrl">文件地址</param>
/// <param name="filePath">文件的本地路徑</param>
/// <returns>文件在本地的存儲路徑</returns>
private Task DownloadFile(string fileUrl, string filePath)
{
    return Task.Run(() =>
    {
        try
        {
            using (var webClient = new WebClient())
            {
                var netStream = webClient.OpenRead(fileUrl);
                if (netStream != null)
                {
                    FileStream fstr = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
                    byte[] readbyte = new byte[102400];
                    int realReadLen = netStream.Read(readbyte, 0, readbyte.Length);
                    while (realReadLen > 0)
                    {
                        fstr.Write(readbyte, 0, realReadLen);
                        realReadLen = netStream.Read(readbyte, 0, readbyte.Length);
                        Thread.Sleep(10);
                    }
                    netStream.Dispose();
                    fstr.Flush();
                    fstr.Close();
                }
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("下載文件時出現異常。", ex);
        }
    });
}

以上,有需要的可以拿去使用,通用性還是能保證的,有特殊用途改改就可以了,比如微信公眾平台上傳文件時form-data內的name是media。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM