在 Net Core 开发中如何解决 Cannot access a disposed object 这个问题
一、简介Net Core跨平台项目开发多了,总会遇到各种各样的问题,我就遇到了一个这样的问题,不能访问 Cannot access a disposed object 错误,经过自己多方努力,查阅资料,终于找到了解决办法,引发这个问题的原因大多数是多次读取请求Body流造成的,需要换一种获取请求Body流方法,不能使用StreamRreader方式,使用Body.CopyTo(ms)方法。
我使用的环境:Visual Studio 2022
开发语言:C#
开发框架:Asp.Net Core Mvc
DotNet版本:Net 6.0
遇到问题是好事,说明自己还有不足,那就解决它,时间长了,技术和知识也就积累了。其实解决方法不难,话不多,直接上解决方案。
二、解决方案的具体实现。
解决方法很简单,不需要做过多解释,直接找个配置和编码就可以了,我贴完整源码,是便于以后查阅,不喜勿喷。
总共三步:红色字体写好了操作步骤(说明一下,红色字体是要解决方法,其他不要关注,把整个代码贴出来,是为了以后查阅)
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using OpticalTrap.Framework.DataAccessors;
using OpticalTrap.Web.Facade.Extensions.Filters;
using OpticalTrap.Web.Facade.Utilities;
using OpticalTrap.Web.ServiceManager;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(option =>
{
option.Filters.Add(typeof(GlobalAuthorizationFilterAttribute));
option.Filters.Add(typeof(GlobalOperationLogFilterAttribute));
}).AddXmlSerializerFormatters();
#region <strong>第一步:配置可以同步请求读取流数据
builder.Services.Configure<KestrelServerOptions>(k => k.AllowSynchronousIO = true)
.Configure<IISServerOptions>(k => k.AllowSynchronousIO = true);
</strong>#endregion
#region 配置日志
builder.Logging.AddLog4Net("ConfigFiles/log4net.config");
#endregion
#region 配置 Session
builder.Services.AddSession();
#endregion
#region 配置数据库
builder.Services.AddTransientSqlSugar(builder.Configuration["ConnectionStrings:DefaultConnectionString"]);
#endregion
#region 配置区域
builder.Services.Configure<RazorViewEngineOptions>(option =>
{
option.AreaViewLocationFormats.Clear();
option.AreaViewLocationFormats.Add("/Areas/{2}/Views/{1}/{0}.cshtml");
option.AreaViewLocationFormats.Add("/Areas/{2}/Views/Shared/{0}.cshtml");
option.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
});
#endregion
#region 配置服务实例
builder.Services.AddBusinessServices();
builder.Services.AddUtilityServices();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton<SessionCacheObjectProvider>();
builder.Services.AddTransient<AuthorizedDataGridGeneratorWrapper>();
builder.Services.AddSingleton<PageNumberGenerator>();
#endregion
#region 认证设置
builder.Services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultForbidScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Validation/Index";
options.LogoutPath = "/Validation/Logout";
options.AccessDeniedPath = "/Validation/Index";
options.Cookie.HttpOnly = true;
options.ClaimsIssuer = "Cookie";
});
#endregion
var app = builder.Build();
//<strong>第二步:</strong><strong>启用倒带, 在发生异常时, 可以通过过滤器获取post参数</strong>
<strong>app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next(context);
});
</strong>if (app.Environment.IsProduction())
{
app.UseStatusCodePagesWithReExecute("/ErrorHandler/HttpStatusCode", "?statusCode={0}");
app.UseExceptionHandler("/ErrorHandler/ExceptionHandler");
}
else
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute("defaultAreaRoute", "{area:exists}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute("defaultRoute", "{controller=Home}/{action=Index}/{id?}");
app.Run();
我写了一个过滤器(实现日志功能),实现的是IActionFilter 过滤器,在过滤器中,获取Post请求Body的Context使用下面方式获取强调(强调,关注红色字体,其他无关,这是我写的日志功能,贴全部代码,便于以后查阅,不喜勿喷)
1 using Microsoft.AspNetCore.Mvc.Filters;
2 using Newtonsoft.Json;
3 using OpticalTrap.Framework.Loggings;
4 using OpticalTrap.Web.ConstProvider;
5 using OpticalTrap.Web.Contracts;
6 using OpticalTrap.Web.Facade.Controllers;
7 using OpticalTrap.Web.Models;
8 using System.Reflection;
9 using System.Security.Claims;
10 using System.Text;
11
12 namespace OpticalTrap.Web.Facade.Extensions.Filters
13 {
14 /// <summary>
15 /// 该类型定义了全局处理操作日志的过滤器,该类型是密封类型。
16 /// </summary>
17 public sealed class GlobalOperationLogFilterAttribute : Attribute, IActionFilter, IAsyncActionFilter
18 {
19 #region 实例字段
20
21 private readonly ILogger<GlobalOperationLogFilterAttribute> _logger;
22 private readonly IServiceProvider _serviceProvider;
23
24 #endregion
25
26 #region 构造函数
27
28 /// <summary>
29 /// 初始化该类型的新实例。
30 /// </summary>
31 /// <param name="logger">需要注入的日志服务实例。</param>
32 /// <param name="serviceProvider">需要注入的服务提供器。</param>
33 public GlobalOperationLogFilterAttribute(ILogger<GlobalOperationLogFilterAttribute> logger, IServiceProvider serviceProvider)
34 {
35 _logger = logger;
36 _serviceProvider = serviceProvider;
37 }
38
39 #endregion
40
41 #region 操作日志的同步方法
42
43 /// <summary>
44 /// 在标注方法执行之前执行该方法。
45 /// </summary>
46 /// <param name="context">方法执行前的上下文。</param>
47 public async void OnActionExecuting(ActionExecutingContext context)
48 {
49 if (context.Controller.GetType() != typeof(ErrorHandlerController))
50 {
51 if (context.ActionDescriptor.EndpointMetadata.Any(c => c.GetType() == typeof(RequiredLogAttribute)))
52 {
53 #region 核心处理
54
55 var controllerType = context.Controller.GetType();
56 var currentMethodName = context.ActionDescriptor.RouteValues["action"]!;
57
58 string? loginName = string.Empty;
59 var claimKeysProvider = _serviceProvider.GetService<ClaimKeysConstProvider>();
60 if (claimKeysProvider != null)
61 {
62 loginName = context.HttpContext.User.FindFirstValue(claimKeysProvider.ClaimStoreUserNameKey);
63 }
64
65 var currentDateTime = DateTime.Now;
66 var methodType = context.HttpContext.Request.Method;
67 string parameterResult = string.Empty;
68 if (string.Compare(methodType, "get", true) == 0)
69 {
70 if (!string.IsNullOrEmpty(context.HttpContext.Request.QueryString.Value) && !string.IsNullOrWhiteSpace(context.HttpContext.Request.QueryString.Value))
71 {
72 parameterResult = context.HttpContext.Request.QueryString.Value;
73 }
74 }
75 else
76 {
77 //<strong>第三步:在同步方法里的使用:</strong><strong>启用倒带, 读取request.body里的的参数, 还必须在在Program.cs里也启用倒带功能</strong>
78 <strong> context.HttpContext.Request.EnableBuffering();
</strong> 79 <strong>context.HttpContext.Request.Body.Position = 0;
</strong> 80 <strong>using (var memoryStream = new MemoryStream())
</strong> 81 <strong> {
</strong> 82 <strong> context.HttpContext.Request.Body.CopyTo(memoryStream);
</strong> 83 <strong>var streamBytes = memoryStream.ToArray();
</strong> 84 <strong>parameterResult = Encoding.UTF8.GetString(streamBytes);</strong> //把body赋值给bodyStr
85 <strong> }
</strong> 86 //using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))<br> //{<br> // var bodyRead = reader.ReadToEndAsync();<br> // bodyStr = bodyRead.Result; //把body赋值给bodyStr<br> // needKey = JsonConvert.DeserializeAnonymousType<br> // (bodyRead.Result, new Dictionary<string, object>()).ToString();<br> //} if (controllerType != typeof(ValidationController))
87 {
88 parameterResult = ProcessFormParameters(parameterResult);
89 }
90 else
91 {
92 parameterResult = ProcessLoginUserNameParameters(parameterResult, controllerType, nameof(ValidationController), currentMethodName, out loginName);
93 }
94 }
95
96 parameterResult = !string.IsNullOrEmpty(parameterResult) ? parameterResult : "没有传递任何参数";
97 loginName = !string.IsNullOrEmpty(loginName) ? loginName : "anonymous";
98 Guid userid = Guid.Empty;
99 if (string.IsNullOrEmpty(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)) || string.IsNullOrWhiteSpace(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)))
100 {
101 userid = Guid.Parse("a05897f9-0c86-4f5a-a581-e5da936d0e4c");
102 }
103 else
104 {
105 userid = Guid.Parse(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid));
106 }
107
108 OperationLog log = new OperationLog()
109 {
110 Id = Guid.NewGuid(),
111 Name = $"{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。",
112 LoginName = loginName,
113 Parameters = parameterResult,
114 ActionName = $"{controllerType.FullName}.{currentMethodName}",
115 ActionType = methodType,
116 Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作顺利完成。",
117 Remarks = "全局日志记录器记录的日志。",
118 CreateUserId = userid,
119 CreateDate = currentDateTime
120 };
121
122 try
123 {
124 MethodInfo? methodInfo;
125 if (controllerType.IsDefined(typeof(RequiredLogAttribute), false))
126 {
127 methodInfo = controllerType.GetMethod(currentMethodName);
128 if (methodInfo != null)
129 {
130 _logger.LogInformation(JsonConvert.SerializeObject(log));
131 }
132 }
133 else
134 {
135 methodInfo = controllerType.GetMethod(currentMethodName);
136 if (methodInfo != null)
137 {
138 if (methodInfo.IsDefined(typeof(RequiredLogAttribute), false))
139 {
140 _logger.LogInformation(JsonConvert.SerializeObject(log));
141 }
142 }
143 }
144 }
145 catch (Exception ex)
146 {
147 log.Name = $"异常操作日志:{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。";
148
149 log.Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作没有完成,系统发生了异常。<br/>异常详情:{ex.Message},<br/>异常堆栈:{ex.StackTrace}。";
150
151 _logger.LogInformation(JsonConvert.SerializeObject(log));
152 }
153
154 #endregion
155 }
156 }
157 }
158
159 /// <summary>
160 /// 在标注方法执行之后执行该方法。
161 /// </summary>
162 /// <param name="context">方法执行后的上下文。</param>
163 public void OnActionExecuted(ActionExecutedContext context) { }
164
165 #endregion
166
167 #region 操作日志的异步方法
168
169 /// <summary>
170 /// 全局日志记录器异步实现的操作日志的记录。
171 /// </summary>
172 /// <param name="context">方法执行前的上下文。</param>
173 /// <param name="next">方法执行的下一个环节代理。</param>
174 /// <returns></returns>
175 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
176 {
177 if (context.Controller.GetType() != typeof(ErrorHandlerController))
178 {
179 if (context.ActionDescriptor.EndpointMetadata.Any(c => c.GetType() == typeof(RequiredLogAttribute)))
180 {
181 #region 核心处理
182
183 var controllerType = context.Controller.GetType();
184 var currentMethodName = context.ActionDescriptor.RouteValues["action"]!;
185
186 string? loginName = string.Empty;
187 var claimKeysProvider = _serviceProvider.GetService<ClaimKeysConstProvider>();
188 if (claimKeysProvider != null)
189 {
190 loginName = context.HttpContext.User.FindFirstValue(claimKeysProvider.ClaimStoreUserNameKey);
191 }
192
193 var currentDateTime = DateTime.Now;
194 var methodType = context.HttpContext.Request.Method;
195 string parameterResult = string.Empty;
196 if (string.Compare(methodType, "get", true) == 0)
197 {
198 if (!string.IsNullOrEmpty(context.HttpContext.Request.QueryString.Value) && !string.IsNullOrWhiteSpace(context.HttpContext.Request.QueryString.Value))
199 {
200 parameterResult = context.HttpContext.Request.QueryString.Value;
201 }
202 }
203 else
204 {
205 //<strong>第三步:在异步步方法里的使用:</strong><strong>启用倒带, 读取request.body里的的参数, 还必须在在Program.cs里也启用倒带功能</strong>
206 <strong> context.HttpContext.Request.EnableBuffering();
</strong>207 <strong>context.HttpContext.Request.Body.Position = 0;
</strong>208 <strong>using (var memoryStream = new MemoryStream())
</strong>209 <strong> {
</strong>210 <strong> context.HttpContext.Request.Body.CopyTo(memoryStream);
</strong>211 <strong>var streamBytes = memoryStream.ToArray();
</strong>212 <strong> parameterResult = Encoding.UTF8.GetString(streamBytes);</strong> //把body赋值给bodyStr
213 <strong> }</strong><br> //using (var reader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8))<br> //{<br> // var bodyRead = reader.ReadToEndAsync();<br> // bodyStr = bodyRead.Result; //把body赋值给bodyStr<br> // needKey = JsonConvert.DeserializeAnonymousType<br> // (bodyRead.Result, new Dictionary<string, object>()).ToString();<br> //}
214 if (controllerType != typeof(ValidationController))
215 {
216 parameterResult = ProcessFormParameters(parameterResult);
217 }
218 else
219 {
220 parameterResult = ProcessLoginUserNameParameters(parameterResult, controllerType, nameof(ValidationController), currentMethodName, out loginName);
221 }
222 }
223
224 parameterResult = !string.IsNullOrEmpty(parameterResult) ? parameterResult : "没有传递任何参数";
225 loginName = !string.IsNullOrEmpty(loginName) ? loginName : "anonymous";
226 Guid userid = Guid.Empty;
227 if (string.IsNullOrEmpty(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)) || string.IsNullOrWhiteSpace(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid)))
228 {
229 userid = Guid.Parse("a05897f9-0c86-4f5a-a581-e5da936d0e4c");
230 }
231 else
232 {
233 userid = Guid.Parse(context.HttpContext.User.FindFirstValue(ClaimTypes.Sid));
234 }
235
236 OperationLog log = new OperationLog()
237 {
238 Id = Guid.NewGuid(),
239 Name = $"{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。",
240 LoginName = loginName,
241 Parameters = parameterResult,
242 ActionName = $"{controllerType.FullName}.{currentMethodName}",
243 ActionType = methodType,
244 Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作顺利完成。",
245 Remarks = "全局日志记录器记录的日志。",
246 CreateUserId = userid,
247 CreateDate = currentDateTime
248 };
249
250 try
251 {
252 MethodInfo? methodInfo;
253 if (controllerType.IsDefined(typeof(RequiredLogAttribute), false))
254 {
255 methodInfo = controllerType.GetMethod(currentMethodName);
256 if (methodInfo != null)
257 {
258 _logger.LogInformation(JsonConvert.SerializeObject(log));
259 }
260 }
261 else
262 {
263 methodInfo = controllerType.GetMethod(currentMethodName);
264 if (methodInfo != null)
265 {
266 if (methodInfo.IsDefined(typeof(RequiredLogAttribute), false))
267 {
268 _logger.LogInformation(JsonConvert.SerializeObject(log));
269 }
270 }
271 }
272 }
273 catch (Exception ex)
274 {
275 log.Name = $"异常操作日志:{loginName}在{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}执行了{currentMethodName}操作。";
276 log.Message = $"【{loginName}】用户在【{currentDateTime.ToString("yyyy-MM-dd HH:mm:ss")}】执行了控制器【{controllerType.FullName}】的【{currentMethodName}】方法,执行方法的类型:{methodType},执行方法所需的参数:【{parameterResult}】,操作没有完成,系统发生了异常。<br/>异常详情:{ex.Message},<br/>异常堆栈:{ex.StackTrace}。";
277
278 _logger.LogInformation(JsonConvert.SerializeObject(log));
279 }
280
281 #endregion
282 }
283 }
284
285 await next.Invoke();
286 }
287
288 /// <summary>
289 /// 处理参数。
290 /// </summary>
291 /// <param name="parameters">要处理的参数字符串。</param>
292 /// <param name="controllerType">控制器的类型。</param>
293 /// <param name="controllerFullName">控制器的全名。</param>
294 /// <param name="currentMethodName">当前调用的方法名称。</param>
295 /// <param name="loginName">登录系统的用户名称。</param>
296 /// <returns></returns>
297 private string ProcessLoginUserNameParameters(string parameters, Type controllerType, string controllerFullName, string currentMethodName, out string loginName)
298 {
299 loginName = string.Empty;
300 if (parameters.IndexOf("&__RequestVerificationToken") != -1)
301 {
302 parameters = parameters.Substring(0, parameters.LastIndexOf("&__RequestVerificationToken"));
303 if ((string.Compare(controllerType.FullName, controllerFullName, true) == 0) && (string.Compare(currentMethodName, "login", true) == 0))
304 {
305 if (parameters.IndexOf("userName=") != -1)
306 {
307 loginName = parameters.Substring("userName=".Length, parameters.IndexOf("&password") - "&password".Length);
308 parameters = $"{parameters.Substring(0, parameters.LastIndexOf("=") + 1)}*********";
309 }
310 }
311 }
312 else
313 {
314 if ((string.Compare(controllerType.FullName, controllerFullName, true) == 0) && (string.Compare(currentMethodName, "login", true) == 0))
315 {
316 if (parameters.IndexOf("userName=") != -1)
317 {
318 loginName = parameters.Substring("userName=".Length, parameters.IndexOf("&password") - "&password".Length);
319 parameters = $"{parameters.Substring(0, parameters.LastIndexOf("=") + 1)}*********";
320 }
321 }
322 }
323 return parameters;
324 }
325
326 /// <summary>
327 /// 返回经过处理的 Form 表单参数。
328 /// </summary>
329 /// <param name="originalFormParameters">未经处理的、原始的 Form 表单参数。</param>
330 /// <returns></returns>
331 private string ProcessFormParameters(string originalFormParameters)
332 {
333 string result = "没有 Form 表单参数。";
334 if (string.IsNullOrEmpty(originalFormParameters) || string.IsNullOrWhiteSpace(originalFormParameters))
335 {
336 return result;
337 }
338
339 if (originalFormParameters.IndexOf("=") != -1 && (originalFormParameters.IndexOf("=") != originalFormParameters.LastIndexOf("=")))
340 {
341 var formParameters = originalFormParameters.Split(new string[] { "-----------------------------", "Content-Disposition: form-data;" }, StringSplitOptions.RemoveEmptyEntries);
342 var filterParameter = new List<string>();
343
344 //获取参数数据,包含=等号的就是form表单的值
345 foreach (var parameter in formParameters)
346 {
347 if (parameter.IndexOf("=") != -1 && parameter.IndexOf("__RequestVerificationToken", StringComparison.CurrentCultureIgnoreCase) == -1)
348 {
349 filterParameter.Add(parameter);
350 }
351 }
352 //整理表单数据格式为:name='xxxx' value='yyyyyy'\r\nname='xxxx2' value='yyyyyy2'....
353 if (filterParameter.Count > 0)
354 {
355 for (int i = 0; i < filterParameter.Count; i++)
356 {
357 filterParameter = ProcessCore(filterParameter);
358 }
359 }
360
361 //凭借结果值,并返回。
362 if (filterParameter.Count > 0)
363 {
364 result = string.Join("\r\n", filterParameter);
365 }
366 }
367
368 return result;
369 }
370
371 /// <summary>
372 /// 递归的处理参数的格式,将格式转换为 name='xxxx' value='yyyyyy'。
373 /// </summary>
374 /// <param name="parameter">要处理的参数。</param>
375 /// <returns>返回处理好的参数。</returns>
376 private string ProcessCore(string parameter)
377 {
378 //过滤Form表单中的图片,只获取字段名和值,具体上传文件的数据不保留。
379 if (parameter.IndexOf("Content-Type: image/", StringComparison.CurrentCultureIgnoreCase) != -1)
380 {
381 parameter = parameter.Substring(0, parameter.IndexOf("Content-Type: image/"));
382 }
383 else if (parameter.IndexOf("\"") != -1)//替换数据中的斜杠和双引号为单引号
384 {
385 parameter = parameter.Replace("\"", "'");
386 }
387 else if (parameter.IndexOf("\r\n\r\n") != -1)
388 //替换数据中的两个换行符为value=',格式:“name=\"Details\"\r\n\r\n<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>\r\n”== “name=\"Details\" value='<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>'”
389 {
390 parameter = parameter.Replace("\r\n\r\n", " value='");
391 }
392 else if (parameter.EndsWith("\r\n"))
393 //替换数据尾部的换行符为单引号,格式:“name=\"Details\"\r\n\r\n<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>\r\n”== “name=\"Details\"\r\n\r\n<p>asdfsadfas</p><p>asdfsadf</p><p>fasdfsadfsadf</p>'”
394 {
395 parameter = parameter.Replace("\r\n", "'");
396 }
397 else if (parameter.IndexOf(";") != -1)
398 {
399 parameter = parameter.Replace(";", " ");
400 }
401 else if (parameter.IndexOf("''") != -1)
402 {
403 parameter = parameter.Replace("''", "'");
404 }
405 else
406 {
407 return parameter;
408 }
409 return ProcessCore(parameter);
410 }
411
412 #endregion
413 }
414 }
三、总结
好了,写完了,今天又解决了一个问题,知识和技能就是在出现问题和解决问题的过程中积累的,不忘初心,继续努力,老天不会亏待努力的人。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]