1 重构Framework.Infrastructure.Middleware. CorsExceptionHandlerMiddleware. InvokeAsync
/// name="context">HTTP上下文实例。
///
/// 【异步调用】
///
/// 摘要:
/// 通过该方法向.Net(Core)框架内置管道中集成当前管道中间件,集中解决在由vue/uni-app前端项目跨域(Cors)访问当前后端项目时,浏览器或App中出现的异常:
/// 1、“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”。
/// 2、“has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.”。
/// 3、“has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.”。
///
///
public async Task InvokeAsync(HttpContext context)
{
//解决在由Hbuilder创建的前端Xuni-app项目(Cors)访问当前后端项目时,浏览器或App中会出现异常:
//“has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.”。
if (!context.Response.Headers.ContainsKey("Access-Control-Allow-Headers"))
{
context.Response.Headers.Add("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization");
}
if (!context.Response.Headers.ContainsKey("Access-Control-Allow-Methods"))
{
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS");
}
//解决在由Hbuilder创建的前端Xuni-app项目(Cors)访问当前后端项目时,浏览器或App中会出现异常:
//“has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.”。
if (!context.Response.Headers.ContainsKey("Access-Control-Allow-Origin"))
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
}
if (context.Request.Headers.ContainsKey(CorsConstants.Origin))
{
//解决在前端通过“axios.post”方式调用后端POST-API有,如果前端“axios.post”方法没有加载“headers”参数实例,下1行语句中的配置,否则“axios.post”方法,访问后端的POST-API,否则会出现:"HTTP:415"错误。
//context.Request.ContentType = "application/json";
//在使用“elmentUI”前端时“context.Request.ContentType”的实例值会为:null,所以必须包含:“context.Request.ContentType == null”。
if (context.Request.ContentType == null || !context.Request.ContentType.Contains("multipart/form-data"))
context.Request.ContentType = "application/json";
//解决在由Hbuilder创建的前端Xuni-app项目(Cors)访问当前后端项目时,浏览器或App中会出现异常:
//“' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.”。
if (context.Request.Method.Equals("OPTIONS"))
{
context.Response.StatusCode = StatusCodes.Status200OK;
return;
}
}
await _next(context);
}
2 重构WebApi.Controllers. CustomerController. PostAvatarStream
/// name="customerId">1个指定的长整型值。
/// name="formFile">1个指定的上传文件的实例。
///
/// 【上传单个文件--无需权限】
///
///
/// 摘要:
/// 把1个指定的上传文件从客户端上传到服务器端的指定目录中。
/// 说明(elmentUI Upload组件):
/// 1、如果使用头属性字典传递customerId参数实例,则不用使用“[FromForm]”标记,
/// URL为:this.actionRequestUrl = "https://localhost:7239/Customer/PostAvatarStream?customerId=" + this.formUser.id;
/// 2、如果使用“:data”传递customerId参数实例,则必须使用“[FromForm]”标记,否则customerId参数实例会一直为:0,
/// URL为:this.actionRequestUrl = "https://localhost:7239/Customer/PostAvatarStream"。
///
///
/// 返回:
/// 1个指定的上传文件上传操作后的状态信息。
///
[HttpPost]
public async Task<IActionResult> PostAvatarStream([FromForm] long customerId, /*IFormCollection collection [FromForm]*/ IFormFile formFile)
{
Customer _customer = await _customerService.GetCustomerByIdAsync(customerId);
if (_customer != null && formFile != null)
{
if(!string.IsNullOrEmpty(_customer.Avatar)&&!_nopFileProvider.GetFileName(_customer.Avatar).Equals("Default.jpg"))
{
//去除网络格式路径字符中的第一个字符“~/”。
_customer.Avatar = _customer.Avatar.Replace("~/", string.Empty).TrimStart('/');
//去除网络格式路径字符中的最后一个字符“/”。
var pathEnd = _customer.Avatar.EndsWith('/') ? Path.DirectorySeparatorChar.ToString() : string.Empty;
//通过拼接操作,拼接出与之相对应的1个本地格式的路径字符串。
string _avatarPath = _nopFileProvider.Combine(_nopFileProvider.WebRootPath ?? string.Empty, _customer.Avatar) + pathEnd;
_nopFileProvider.DeleteFile(_avatarPath);
}
string _filename = Guid.NewGuid().ToString() + _nopFileProvider.GetFileExtension(formFile.FileName);
string _path = _nopFileProvider.Combine(_nopFileProvider.WebRootPath, @"\images\Avatar\", _filename);
using FileStream fileStream = new FileStream(_path, FileMode.Create);
await formFile.CopyToAsync(fileStream);
_customer.Avatar = "/images/Avatar/" + _filename;
await _customerService.UpdateCustomerAsync(_customer);
return Created(WebUtility.UrlEncode(_customer.Avatar), _customer);
}
return BadRequest();
}
3 \src\components\Users\ EditUser.vue
#header>
编辑用户 style="margin: 0px; padding: 0px; ">
name="formFile" :before-upload="beforeUpload" :on-change="onUploadChange" :data="paramData">
#footer>
class="dialog-footer">
export default {
name: 'EditUser',
props: {
propParent: {},
},
data() {
return {
//用户编辑表单实例初始化。
formUser: {
id: 0,
name: '',
email: '',
phone: '',
avatar: '',
isSystemAccount: false,
isActive: true,
deleted: false,
//该实例用于指示,当前页面角色编辑子页面是否在父窗口中显示出来。
centerDialogVisible: false,
},
//文件上传--需要或已经被上传的文件。
fileList: [{
url: '',
}],
//文件上传--上传文件所需要调用的指定的后端控制器行为方法。
actionRequestUrl: '',
//文件上传--允许被上传文件的类型规则
fileType: ["bmp", "gif", "jpeg", "jpg", "jpe", "jfif", "pjpeg", "webp", "png", "svg", "tiff", "tif"],
//文件上传--允许被上传文件的最大大小值规则,单位:MB
fileSize: 10,
paramData: {
customerId: 0,
},
};
},
//监视父窗口传递的参数实例,使当前子页面中的表单始终显示父窗口最新传递的参数实例。
watch: {
propParent(val) {
//console.log(val);
this.formUser = val;
this.fileList[0].url = this.formUser.avatar;
//console.log(this.fileList[0].url);
},
},
methods: {
//上传文件之前,验证指定上传文件是否符合上传规则。
beforeUpload(file) {
if (file.type != "" || file.type != null || file.type != undefined) {
//清空已上传的文件列表
//this.$refs.upload.clearFiles();
//console.log(file);
//截取文件的后缀,判断文件类型
let fileExt = file.name.replace(/.+\./, "").toLowerCase();
//计算指定上传文件大小是否大于10MB。
let isMaxFileSize = file.size / 1024 / 1024 <= 10;
//如果大于i大于10MB
if (!isMaxFileSize) {
this.$message.error("上传文件大小不能超过 10MB!");
return false;
}
//如果文件类型不在允许上传的范围内
if (this.fileType.includes(fileExt)) {
return true;
} else {
this.$message.error("上传文件格式不正确!");
return false;
}
//this.fileList[0].url = file.url;
}
},
//头像图片文件发生变更时,实现头像图片文件的上传操作。
//注意:该方法必须包含“file”参数。
async onUploadChange(file,fileList) {
/* console.log("需要或已经被上传的文件列表:", fileList);
console.log("1个指定的需要被上传的文件:", file);
console.log("1个指定的需要被上传文件的大小:", file.size);
console.log("1个指定的需要被上传文件的文件名:", file.name);
console.log("1个指定的需要被上传文件的完整信息(作为传递参数的实例值):", file.raw);
console.log("1个指定的需要被上传文件的URL:", file.url);
console.log("上传操作后所返回的成功/失败的数据信息:", file.response);
console.log("1个指定用户的长整型编号值:", this.formUser.id); */
this.paramData.customerId = this.formUser.id;
this.actionRequestUrl = "";
//this.actionRequestUrl = "https://localhost:7239/Customer/PostAvatarStream?customerId=" + this.formUser.id;
this.actionRequestUrl = " https://localhost:7239/Customer/PostAvatarStream";
//需要或已经被上传的文件列表中只渲染显示最新的1个上传文件。
this.fileList = fileList.slice(-1);
//console.log("渲染显示的文件列表:", this.fileList);
},
},
async mounted() {
},
};
对以上功能更为具体实现和注释见:230406_015shopvue(elmentUI Upload组件通过IFormFile参数上传注意事项)。