在现代的.net应用程序开发中,与外部服务进行http通信是一项常见需求。httpclient作为.net框架中处理http请求的核心组件,为我们提供了强大而灵活的api。然而,直接使用原生的httpclient可能会导致代码重复、错误处理不完善等问题。为了提高代码的可维护性和可测试性,我们通常会对httpclient进行封装。本文将介绍一个完整的httprequest类封装实现,并深入探讨http请求处理的最佳实践。
一、完整的httprequest类实现
首先,让我们来看一下完整的httprequest类实现代码:
using system;
using system.collections.generic;
using system.net;
using system.net.http;
using system.net.http.headers;
using system.text;
using system.text.json;
using system.threading.tasks;
public class response
{
public bool success { get; set; }
public string message { get; set; }
public object data { get; set; }
public httpstatuscode statuscode { get; set; }
}
public static class jsonconverterextensions
{
public static readonly jsonserializeroptions serializersettings = new jsonserializeroptions
{
propertynamingpolicy = jsonnamingpolicy.camelcase,
ignorenullvalues = true,
writeindented = false
};
}
public class httprequest : idisposable
{
private readonly httpclient client;
private bool disposed = false;
public httprequest(httpclient client)
{
this.client = client ?? throw new argumentnullexception(nameof(client));
}
public void dispose()
{
dispose(true);
gc.suppressfinalize(this);
}
protected virtual void dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
client?.dispose();
}
disposed = true;
}
}
public async task<response> getasync(string resource)
{
try
{
var response = await client.getasync(resource);
return await processresponseasync(response);
}
catch (httprequestexception ex)
{
return handleexception(ex);
}
catch (exception ex)
{
return handleunexpectedexception(ex);
}
}
public async task<response> postasync(string resource, object body)
{
try
{
var content = createjsoncontent(body);
var response = await client.postasync(resource, content);
return await processresponseasync(response);
}
catch (httprequestexception ex)
{
return handleexception(ex);
}
catch (exception ex)
{
return handleunexpectedexception(ex);
}
}
public async task<response> putasync(string resource, object body)
{
try
{
var content = createjsoncontent(body);
var response = await client.putasync(resource, content);
return await processresponseasync(response);
}
catch (httprequestexception ex)
{
return handleexception(ex);
}
catch (exception ex)
{
return handleunexpectedexception(ex);
}
}
public async task<response> deleteasync(string resource)
{
try
{
var response = await client.deleteasync(resource);
return await processresponseasync(response);
}
catch (httprequestexception ex)
{
return handleexception(ex);
}
catch (exception ex)
{
return handleunexpectedexception(ex);
}
}
public httprequest withbaseaddress(string baseaddress)
{
if (!string.isnullorempty(baseaddress))
{
client.baseaddress = new uri(baseaddress);
}
return this;
}
public httprequest withtimeout(timespan timeout)
{
client.timeout = timeout;
return this;
}
public httprequest withheader(string name, string value)
{
if (!client.defaultrequestheaders.contains(name))
{
client.defaultrequestheaders.add(name, value);
}
return this;
}
public httprequest withheaders(idictionary<string, string> headers)
{
if (headers != null)
{
foreach (var header in headers)
{
withheader(header.key, header.value);
}
}
return this;
}
public httprequest withauthorization(string scheme, string parameter)
{
client.defaultrequestheaders.authorization = new authenticationheadervalue(scheme, parameter);
return this;
}
public httprequest withbearertoken(string token)
{
return withauthorization("bearer", token);
}
private stringcontent createjsoncontent(object body)
{
if (body == null)
{
return new stringcontent("{}", encoding.utf8, "application/json");
}
var json = jsonserializer.serialize(body, jsonconverterextensions.serializersettings);
return new stringcontent(json, encoding.utf8, "application/json");
}
private async task<response> processresponseasync(httpresponsemessage response)
{
var responsecontent = await response.content.readasstringasync();
try
{
// 尝试解析json响应
var responseobject = jsonserializer.deserialize<response>(responsecontent, jsonconverterextensions.serializersettings);
if (responseobject != null)
{
responseobject.statuscode = response.statuscode;
return responseobject;
}
}
catch (jsonexception)
{
// 如果json解析失败,创建一个基于http状态码的响应
}
// 对于非json响应或解析失败的情况
return new response
{
success = response.issuccessstatuscode,
message = response.reasonphrase,
statuscode = response.statuscode,
data = responsecontent
};
}
private response handleexception(httprequestexception ex)
{
return new response
{
success = false,
message = $"http请求错误: {ex.message}",
statuscode = ex.statuscode ?? httpstatuscode.internalservererror,
data = ex
};
}
private response handleunexpectedexception(exception ex)
{
return new response
{
success = false,
message = $"处理请求时发生意外错误: {ex.message}",
statuscode = httpstatuscode.internalservererror,
data = ex
};
}
}
二、设计思路与实现要点
1. 依赖注入与生命周期管理
这个封装类采用了依赖注入模式,通过构造函数接收一个httpclient实例。这样做有几个重要好处:
遵循单一职责原则,httprequest类专注于http请求处理
便于单元测试,可以轻松注入模拟的httpclient
利用.net的ihttpclientfactory进行正确的httpclient生命周期管理,避免资源泄漏
同时,类实现了idisposable接口,确保在不再需要时正确释放httpclient资源。
2. 流畅接口设计
为了提供更友好的api体验,封装类实现了流畅接口模式:
var response = await new httprequest(httpclient)
.withbaseaddress("https://api.example.com")
.withbearertoken("your-token-here")
.withheader("x-custom-header", "value")
.postasync("/resource", new { key = "value" });
这种链式调用方式使代码更加简洁易读,同时保持了良好的可扩展性。
3. 统一的错误处理
在每个http方法中,我们都实现了统一的异常处理机制:
- 捕获httprequestexception处理http特定错误
- 捕获其他异常处理意外错误
- 将所有错误转换为统一的response对象
- 保留原始异常信息以便调试
这种统一的错误处理方式使上层调用代码更加简洁,无需重复处理各种异常情况。
4. 灵活的响应处理
processresponseasync方法负责处理http响应,它尝试将响应内容解析为json格式的response对象:
- 如果解析成功,返回包含完整信息的response对象
- 如果解析失败,创建一个基于http状态码的response对象
- 始终保留原始响应内容和状态码信息
这种设计使封装类能够处理各种类型的http响应,同时提供一致的返回格式。
三、实际使用示例
下面是一个使用这个封装类的完整示例:
using system;
using system.net.http;
using system.threading.tasks;
public class program
{
public static async task main()
{
try
{
// 创建httpclient实例(实际应用中建议使用ihttpclientfactory)
using var httpclient = new httpclient();
// 创建请求实例并配置
var request = new httprequest(httpclient)
.withbaseaddress("https://api.example.com")
.withbearertoken("your-auth-token");
// 发送get请求
var getresponse = await request.getasync("/api/users");
console.writeline($"get请求结果: {getresponse.success}, 状态码: {getresponse.statuscode}");
// 发送post请求
var postdata = new { name = "john doe", email = "john@example.com" };
var postresponse = await request.postasync("/api/users", postdata);
console.writeline($"post请求结果: {postresponse.success}, 状态码: {postresponse.statuscode}");
// 发送put请求
var putdata = new { id = 1, name = "jane doe" };
var putresponse = await request.putasync("/api/users/1", putdata);
console.writeline($"put请求结果: {putresponse.success}, 状态码: {putresponse.statuscode}");
// 发送delete请求
var deleteresponse = await request.deleteasync("/api/users/1");
console.writeline($"delete请求结果: {deleteresponse.success}, 状态码: {deleteresponse.statuscode}");
}
catch (exception ex)
{
console.writeline($"发生未处理的异常: {ex.message}");
}
}
}
四、httpclient使用最佳实践
在使用httpclient和这个封装类时,还需要注意以下最佳实践:
- 使用ihttpclientfactory:在asp.net core应用中,始终使用ihttpclientfactory创建httpclient实例,避免直接实例化httpclient。
- 设置合理的超时时间:默认情况下,httpclient的超时时间是100秒,根据实际需求调整这个值,防止长时间阻塞。
- 处理取消请求:考虑实现请求取消机制,通过cancellationtoken参数传递取消令牌。
- 处理重试逻辑:对于临时性网络错误,考虑实现重试机制。可以使用polly等库来简化重试策略的实现。
- 监控http请求性能:记录http请求的执行时间、成功率等指标,便于性能分析和问题排查。
通过这个完整的httprequest类封装,我们可以更加高效、安全地处理http通信,同时保持代码的整洁和可维护性。希望这篇文章对你理解c#中的http请求处理有所帮助。
这个实现提供了完整的http请求功能,包括get、post、put、delete方法,以及灵活的请求配置和统一的响应处理。
到此这篇关于c#封装httpclient实现http请求处理的文章就介绍到这了,更多相关c#封装httpclient内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论