本篇文章給大家分享的是有關(guān)如何在Asp.Net Core中使用Refit,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
建網(wǎng)站原本是網(wǎng)站策劃師、網(wǎng)絡(luò)程序員、網(wǎng)頁設(shè)計師等,應(yīng)用各種網(wǎng)絡(luò)程序開發(fā)技術(shù)和網(wǎng)頁設(shè)計技術(shù)配合操作的協(xié)同工作。成都創(chuàng)新互聯(lián)專業(yè)提供網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計,網(wǎng)頁設(shè)計,網(wǎng)站制作(企業(yè)站、響應(yīng)式網(wǎng)站建設(shè)、電商門戶網(wǎng)站)等服務(wù),從網(wǎng)站深度策劃、搜索引擎友好度優(yōu)化到用戶體驗的提升,我們力求做到極致!ASP.NET 是開源,跨平臺,高性能,輕量級的 Web 應(yīng)用構(gòu)建框架,常用于通過 HTML、CSS、JavaScript 以及服務(wù)器腳本來構(gòu)建網(wǎng)頁和網(wǎng)站。
在常規(guī)的方式中我們一般使用IHttpClientFactory來創(chuàng)建HttpClient對象,然后使用這個對象來發(fā)送和接收消息,至于為什么要使用這個接口來創(chuàng)建HttpClient對象而不是使用using new HttpClient的原因請點擊這里了解更多的信息,我們先來看下面的這個例子。
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using System.Web; using Abp.Domain.Services; using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace Sunlight.Dms.Parts.Domain.Web { /// <summary> /// HttpClient的幫助類 /// </summary> public class DcsPartClientService : DomainService { private readonly HttpClient _httpClient; private readonly ILogger<DcsPartClientService> _loggerHelper; public DcsPartClientService(IHttpClientFactory httpClientFactory, ILogger<DcsPartClientService> loggerHelper) { _loggerHelper = loggerHelper; _httpClient = httpClientFactory.CreateClient(PartsConsts.DcsPartClientName); if (_httpClient.BaseAddress == null) { throw new ArgumentNullException(nameof(httpClientFactory), $"沒有配置名稱為 {PartsConsts.DcsPartClientName} 的HttpClient,或者接口服務(wù)的地址為空"); } } /// <summary> /// Post請求返回實體 /// </summary> /// <param name="relativeUrl">請求相對路徑</param> /// <param name="postObj">請求數(shù)據(jù)</param> /// <returns>實體T</returns> public async Task<List<T>> PostResponse<T>(string relativeUrl, object postObj) where T : class { var postData = JsonConvert.SerializeObject(postObj); _httpClient.DefaultRequestHeaders.Add("user-agent", "Dcs-Parts"); _httpClient.CancelPendingRequests(); _httpClient.DefaultRequestHeaders.Clear(); HttpContent httpContent = new StringContent(postData); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); var result = default(List<T>); var response = await _httpClient.PostAsync(_httpClient.BaseAddress + relativeUrl, httpContent); if (response.StatusCode == HttpStatusCode.NotFound) { throw new ValidationException("找不到對應(yīng)的DcsParts服務(wù)"); } var responseContent = await response.Content.ReadAsAsync<ReceiveResponseBody<List<T>>>(); if (response.IsSuccessStatusCode) { result = responseContent?.Payload; } else { if (!string.IsNullOrWhiteSpace(responseContent?.Message)) { throw new ValidationException(responseContent.Message); } _loggerHelper.LogDebug($"請求返回結(jié)果:{0} 請求內(nèi)容:{1}", response.StatusCode, postData); } return await Task.FromResult(result); } public async Task<List<T>> GetResponse<T>(string relativeUrl, object queryObj) where T : class { var queryData = ModelToUriQueryParam(queryObj); _httpClient.DefaultRequestHeaders.Add("user-agent", "Dcs-Parts"); _httpClient.CancelPendingRequests(); _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Add("accept", "application/json"); var result = default(List<T>); var response = await _httpClient.GetAsync(_httpClient.BaseAddress + relativeUrl + queryData); if (response.StatusCode == HttpStatusCode.NotFound) { throw new ValidationException("找不到對應(yīng)的DcsParts服務(wù)"); } var responseContent = await response.Content.ReadAsAsync<ReceiveResponseBody<List<T>>>(); if (response.IsSuccessStatusCode) { result = responseContent?.Payload; } else { if (!string.IsNullOrWhiteSpace(responseContent?.Message)) { throw new ValidationException(responseContent.Message); } } return await Task.FromResult(result); } private string ModelToUriQueryParam<T>(T t, string url = "") { var properties = t.GetType().GetProperties(); var sb = new StringBuilder(); sb.Append(url); sb.Append("?"); foreach (var p in properties) { var v = p.GetValue(t, null); if (v == null) continue; sb.Append(p.Name); sb.Append("="); sb.Append(HttpUtility.UrlEncode(v.ToString())); sb.Append("&"); } sb.Remove(sb.Length - 1, 1); return sb.ToString(); } } public class ReceiveResponseBody<T> where T : class { public string Message { get; set; } public T Payload { get; set; } } public class ReceiveResponseBody { public string Message { get; set; } } }
在這個過程中我們通過構(gòu)造函數(shù)來注入IHttpClientFactory接口,然后用這個接口的CreateClient方法來創(chuàng)建一個的HttpClient對象,在這里我們一般都會同步注入ILogger接口來記錄日志信息從而便于我們排查線上問題,這里我們在CreateClient方法中傳入了一個字符串類型的參數(shù)用于標記自己創(chuàng)建的HttpClient對象的性。這里我們可以看到在構(gòu)造函數(shù)中我們會去判斷當前創(chuàng)建的HttpClient的BaseAddress,如果沒有這個基地址那么程序會直接拋出錯誤提示,那么問題來了我們的HttpClient的BaseAddress到底在哪里配置呢?熟悉Asp.Net Core機制的朋友肯定一下子就會想到在Startup類中配置,那么我們來看看需要怎么配置。
public IServiceProvider ConfigureServices(IServiceCollection services) { //dcs.part服務(wù) services.AddHttpClient(PartsConsts.DcsPartClientName, config => { config.BaseAddress = new Uri(_appConfiguration["DependencyServices:DcsParts"]); config.Timeout = TimeSpan.FromSeconds(60); }); }
這里我只是簡要截取了一小段內(nèi)容,這里我們看到AddHttpClient的第一個參數(shù)也是一個字符串常量,這個常量應(yīng)該是和IHttpClientFactory的CreateClient的方法中的那個常量保持絕對的一致,只有這樣我們才能夠標識的標識一個HttpClient對象,創(chuàng)建完了之后我們就能夠在這個里面去配置這個HttpClient的各種參數(shù)了,另外在上面的這段代碼中_appConfiguration這個對象是通過Startup的構(gòu)造函數(shù)注入的,具體的代碼請參考下面。
public Startup(IHostingEnvironment env) { _appConfiguration = env.GetAppConfiguration(); Clock.Provider = ClockProviders.Local; Environment = env; Console.OutputEncoding = System.Text.Encoding.UTF8; }
另外我們還需要配置一些HttpClient所必須的屬性包括基地址、超時時間......等等,當然這個基地址我們是配置在appsetting.json中的,具體的配置如下所示。
"DependencyServices": { "BlobStorage": "http://blob-storage/", "DcsParts": "http://dcs-parts/", "DmsAfterSales": "http://dms-after-sales/" }
有了這些我們就能夠具備創(chuàng)建一個HttpClient對象的條件了,后面我們來看看我們怎么使用這個HttpClient進行發(fā)送和接收數(shù)據(jù)。
/// <summary> /// Post請求返回實體 /// </summary> /// <param name="relativeUrl">請求相對路徑</param> /// <param name="postObj">請求數(shù)據(jù)</param> /// <returns>實體T</returns> public async Task<List<T>> PostResponse<T>(string relativeUrl, object postObj) where T : class { var postData = JsonConvert.SerializeObject(postObj); _httpClient.DefaultRequestHeaders.Add("user-agent", "Dcs-Parts"); _httpClient.CancelPendingRequests(); _httpClient.DefaultRequestHeaders.Clear(); HttpContent httpContent = new StringContent(postData); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); var result = default(List<T>); var response = await _httpClient.PostAsync(_httpClient.BaseAddress + relativeUrl, httpContent); if (response.StatusCode == HttpStatusCode.NotFound) { throw new ValidationException("找不到對應(yīng)的DcsParts服務(wù)"); } var responseContent = await response.Content.ReadAsAsync<ReceiveResponseBody<List<T>>>(); if (response.IsSuccessStatusCode) { result = responseContent?.Payload; } else { if (!string.IsNullOrWhiteSpace(responseContent?.Message)) { throw new ValidationException(responseContent.Message); } _loggerHelper.LogDebug($"請求返回結(jié)果:{0} 請求內(nèi)容:{1}", response.StatusCode, postData); } return await Task.FromResult(result); }
在上面的代碼中我們模擬了一個Post請求,請求完成以后我們再使用ReadAsAsync的方法來異步接收另外一個域中的數(shù)據(jù),然后我們根據(jù)返回的StatusCode來拋出不同的錯誤提示,并記錄相關(guān)的日志信息并返回最終Post請求的結(jié)果,進而完成整個過程,在這個中間我們發(fā)送請求的時候需要注意一下內(nèi)容:1 最終的完整版地址=BaseAddress+RelativeAddress,基地址是在appsetting.json中進行配置的,RelativeAddress是我們請求不同域的時候的相對地址,這個需要我們根據(jù)實際的業(yè)務(wù)來進行配置。2 請求的對象是我們將數(shù)據(jù)對象序列化成json后的結(jié)果,這兩點需要特別注意。
通過上面的講述我們知道了如何完整的創(chuàng)建HttpClient以及通過創(chuàng)建的HttpClient如何收發(fā)數(shù)據(jù),但同時我們也發(fā)現(xiàn)了通過上面的方式我們的缺點:如果一個業(yè)務(wù)中有大量的這種跨域請求整個代碼顯得非常臃腫并且由于不同開發(fā)人員的認知不同最終導致很容易出問題,那么我們是否有辦法能夠去解決上面的問題呢?Refit庫的出現(xiàn)正好解決了這個問題,Refit通過這種申明式的方式能夠很大程度上讓代碼更加簡練明了而且提供了更加豐富的功能。
在我們的項目中我們可以通過 <PackageReference Include="Refit" Version="XXX" />來快速引用Refit包,引用的方式這里便不再贅述。
我們將我們業(yè)務(wù)中涉及到的方法定義在一個接口中,就像下面這樣。
public interface IDmsAfterSalesApi { [Headers("User-Agent: Dms-Parts")] [Post("/internal/api/v1/customerAccounts/update")] Task<ResponseBody> UpdateCustomerAmount([Body]PartRetailSettlementModel input); [Headers("User-Agent: Dms-Parts")] [Post("/internal/api/v1/repairShortagePart/checkCustomerAccount")] Task<RepairShortagePartResponseBody> RepairShortagePartCheckCustomerAccount([Body]RepairShortagePartModel input); [Headers("User-Agent: Dms-Parts")] [Post("/internal/api/v1/vehiclesAndMemberCode/forCoupons")] Task<GetMemberCodeBrandCodeForVehicleBody> GetMemberCodeBrandCodeForVehicle(Guid vehicleId); }
public class DmsAfterSalesClientService : DomainService { private readonly IDmsAfterSalesApi _api; private readonly ILogger<DcsPartClientService> _logger; private const string From = "Dms After Sales"; public DmsAfterSalesClientService(IDmsAfterSalesApi api, ILogger<DcsPartClientService> logger) { _api = api; _logger = logger; } private async Task<Exception> WrapException(ApiException exception) { if (exception.StatusCode == System.Net.HttpStatusCode.BadRequest) { var receivedBody = await exception.GetContentAsAsync<ResponseBody>(); return new ValidationException($"業(yè)務(wù)校驗失敗,{receivedBody.Message} ({From})", exception); } else { _logger.LogWarning(exception, "Call Dms After Sales API failed"); return new ApplicationException($"內(nèi)部調(diào)用失敗,{exception.Message} ({exception.StatusCode}) ({From})", exception); } } private Exception WrapException(HttpRequestException exception) { _logger.LogWarning(exception, "Call Dms After Sales API failed"); return new ApplicationException($"內(nèi)部調(diào)用失敗,{exception.Message} ({From})", exception); } public async Task UpdateCustomerAmount([Body] PartRetailSettlementModel input) { try { await _api.UpdateCustomerAmount(input); } catch (ApiException ex) { throw await WrapException(ex); } catch (HttpRequestException ex) { throw WrapException(ex); } } public async Task<decimal> RepairShortagePartCheckCustomerAccount([Body] RepairShortagePartModel input) { try { var result = await _api.RepairShortagePartCheckCustomerAccount(input); return result.Payload.BalanceAmount; } catch (ApiException ex) { throw await WrapException(ex); } catch (HttpRequestException ex) { throw WrapException(ex); } } public async Task<GetMemberCodeBrandCodeForVehicleOutput> GetMemberCodeBrandCodeForVehicle([Body]Guid vehicleId) { try { var result = await _api.GetMemberCodeBrandCodeForVehicle(vehicleId); return result.Payload; } catch (ApiException ex) { throw await WrapException(ex); } catch (HttpRequestException ex) { throw WrapException(ex); } } }
在上面接口中定義好這個方法以后我們就可以直接在我們的領(lǐng)域類中引入這個接口IDmsAfterSalesApi ,然后就直接使用這個接口中的方法,講到這里便有疑問,這個接口的實現(xiàn)到底在哪里?這里當我們定義好接口然后點擊里面的方法轉(zhuǎn)到實現(xiàn)的時候我們發(fā)現(xiàn)里面會轉(zhuǎn)到一個叫做RefitStubs.g.cs的類中,然后自動的生成下面的方法。
/// <inheritdoc /> [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [global::System.Diagnostics.DebuggerNonUserCode] [Preserve] [global::System.Reflection.Obfuscation(Exclude=true)] partial class AutoGeneratedIDmsAfterSalesApi : IDmsAfterSalesApi { /// <inheritdoc /> public HttpClient Client { get; protected set; } readonly IRequestBuilder requestBuilder; /// <inheritdoc /> public AutoGeneratedIDmsAfterSalesApi(HttpClient client, IRequestBuilder requestBuilder) { Client = client; this.requestBuilder = requestBuilder; } /// <inheritdoc /> Task<ResponseBody> IDmsAfterSalesApi.UpdateCustomerAmount(PartRetailSettlementModel input) { var arguments = new object[] { input }; var func = requestBuilder.BuildRestResultFuncForMethod("UpdateCustomerAmount", new Type[] { typeof(PartRetailSettlementModel) }); return (Task<ResponseBody>)func(Client, arguments); } /// <inheritdoc /> Task<RepairShortagePartResponseBody> IDmsAfterSalesApi.RepairShortagePartCheckCustomerAccount(RepairShortagePartModel input) { var arguments = new object[] { input }; var func = requestBuilder.BuildRestResultFuncForMethod("RepairShortagePartCheckCustomerAccount", new Type[] { typeof(RepairShortagePartModel) }); return (Task<RepairShortagePartResponseBody>)func(Client, arguments); } /// <inheritdoc /> Task<GetMemberCodeBrandCodeForVehicleBody> IDmsAfterSalesApi.GetMemberCodeBrandCodeForVehicle(Guid vehicleId) { var arguments = new object[] { vehicleId }; var func = requestBuilder.BuildRestResultFuncForMethod("GetMemberCodeBrandCodeForVehicle", new Type[] { typeof(Guid) }); return (Task<GetMemberCodeBrandCodeForVehicleBody>)func(Client, arguments); } }
這里面的核心是調(diào)用一個BuildRestResultFuncForMethod的方法,后面我們再來分析這里面到底是怎么實現(xiàn)的,這里我們首先把這整個使用流程說完,之前我們說過Refit的很多配置都是通過標簽的方式來注入進去的,這里包括請求類型、相對請求地址,那么我們的默認超時時間和BaseAddress到底是怎樣來配置的呢?下面我們就來重點講述。
public IServiceProvider ConfigureServices(IServiceCollection services) { //refit dms after sales服務(wù) services.AddRefitClient<IDmsAfterSalesApi>() .ConfigureHttpClient(c => { c.BaseAddress = new Uri(_appConfiguration["DependencyServices:DmsAfterSales"]); c.Timeout = TimeSpan.FromMilliseconds(_appConfiguration.GetValue<int>("AppSettings:ServiceTimeOutMs")); }); }
這里我們看到通過一個AddRefitClient方法我們就能夠去配置我們的基礎(chǔ)信息,講到這里我們是不是對整個過程都有一個清楚的認識呢?通過上下兩種方式的對比,相信你對整個Refit的使用都有自己的理解。
由于我們的Headers經(jīng)常需要我們?nèi)ヅ渲靡唤M數(shù)據(jù),那么我們應(yīng)該怎么配置多個項呢?
[Headers("User-Agent: Dms-Parts", "Content-Type: application/json")]
通過上面的方式我們能夠配置一組Headers,另外在很多的時候如果Headers里面沒有配置Content-Type那么很有可能會返回StatusCode=415 Unsupport Media Type這個類型的錯誤信息,這個在使用的時候需要注意。
以上就是如何在Asp.Net Core中使用Refit,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
名稱欄目:如何在Asp.NetCore中使用Refit-創(chuàng)新互聯(lián)
標題網(wǎng)址:http://m.rwnh.cn/article48/djighp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、企業(yè)網(wǎng)站制作、服務(wù)器托管、網(wǎng)站維護、響應(yīng)式網(wǎng)站、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容