- A+
所属分类:.NET技术
.NET中间件以及VUE拦截器联合使用
工作中遇见的问题,边学边弄,记录一下
Vue的UI库使用的是antvue 3.2.9版本的。
-
业务逻辑
-
特性
//特性 public class ModelEsignNameAttribute : Attribute { public ModelEsignNameAttribute(string nameProp, string id, string reversion = "", ModelESignType eSignType = ModelESignType.Modeling, string middleModelId = "") { } }
- 接口加上特性
/// <summary> /// 添加或者修改方法 /// </summary> /// <param name="input"></param> /// <returns></returns> //特性上添加参数的地址 [ModelEsignName("Bolg.BolgBaseEditDto.BolgName", "Document.Id", "Bolg.BolgRevision")] public async Task<Output> CreateOrUpdate(CreateOrUpdateBolgInput input) { var doc = await _XXXXManager.FindRdoById(input.Bolg.Id.Value); // 文档id为空,新增 if (doc == null || !input.Bolg.BolgBaseId.HasValue) { return await this.Create(input.Bolg); } // 更新 return await this.Update(input.Bolg); }
- 中间件代码
namespace GCT.MedPro.Middleware { public class ModelESignCheckMiddleware : IMiddleware { #region 依赖注入等内容 .... #endregion public async Task InvokeAsync(HttpContext context, RequestDelegate next) { if (await ShouldCheckESign(context)) { // 不需要电子签名 await next(context); } } /// <summary> /// 是否需要拦截 /// </summary> /// <param name="actionContext"></param> /// <returns></returns> private async Task<bool> ShouldCheckESign(HttpContext actionContext) { var whetherSignature = true; var request = actionContext.Request;//获取请求值 var currentUser = actionContext.User.Identity.Name; var serviceAction = actionContext .GetEndpoint()? .Metadata .GetMetadata<ControllerActionDescriptor>(); if (serviceAction == null) { return whetherSignature; } //通过接口特性来筛选是否需要进行拦截 var attrObj = serviceAction.MethodInfo.CustomAttributes .FirstOrDefault(x => x.AttributeType == typeof(ModelEsignNameAttribute)); if (attrObj == null) { return whetherSignature; } string inputbody = default; actionContext.Request.EnableBuffering(); //Post请求获取请求参数,转换JSON if (request.Method.ToLower().Equals("post")) { var requestReader = new StreamReader(actionContext.Request.Body); var body = await requestReader.ReadToEndAsync(); inputbody = UpperFirst(body); //首字母大写 全局搜索可得下方有 } else //GET请求以及其他方式获取 { var reqString = request.QueryString.Value.Remove(0, 1); string[] parts = reqString.Split("&"); JObject json = new JObject(); foreach (string part in parts) { String[] keyVal = part.Split("="); json.Add(keyVal[0], keyVal[1]); } inputbody = JsonConvert.SerializeObject(json); inputbody = UpperFirst(inputbody); } var inputObj = JObject.Parse(inputbody);//转换JObject #region 获取特性传入的参数,,总五位参数 var actionName = serviceAction.ActionName; var namePath = attrObj.ConstructorArguments[0].Value.ToString(); var idPath = attrObj.ConstructorArguments[1].Value.ToString(); var revsionPath = attrObj.ConstructorArguments[2].Value.ToString(); var typePath = (ModelESignType)attrObj.ConstructorArguments[3].Value; var middlePath = attrObj.ConstructorArguments[4].Value.ToString(); #endregion var middleModelId = GetValueName(inputObj, middlePath);//通过JObject获取对应值 //接口控制器名称 var typeName = serviceAction.ControllerTypeInfo.FullName; //重置请求Body指针 actionContext.Request.Body.Position = 0; //验证方法,自己写个,自已业务的处理验证 var output = await CheckSign(middleModelId); if (!output.SignStatus) { actionContext.Request.EnableBuffering(); Stream originalBody = actionContext.Response.Body; try { using (var ms = new MemoryStream()) { //修改响应状态麻420 actionContext.Response.Body = ms; actionContext.Response.StatusCode = 420; ms.Position = 0; //写入数据 var responseBody = TextJosn.JsonSerializer.Serialize(output); var memoryStream = new MemoryStream(); var sw = new StreamWriter(memoryStream); //自己编辑的实体写入响应体 sw.Write(responseBody); sw.Flush(); //重置响应指针 memoryStream.Position = 0; //复制到原body上 await memoryStream.CopyToAsync(originalBody); } } finally { actionContext.Response.Body = originalBody; actionContext.Request.Body.Position = 0; } whetherSignature = false; } else { if (!string.IsNullOrWhiteSpace(output.ErrorMessage)) { var serializerSettings = new JsonSerializerSettings { // 设置为驼峰命名 ContractResolver = new Newtonsoft.Json.Serialization .CamelCasePropertyNamesContractResolver() }; //错误友好提示,适配中间件中抛出错误,修改响应体 var exception = new UserFriendlyException(output.ErrorMessage); actionContext.Response.StatusCode = 500; actionContext.Response.ContentType = "application/json; charset=utf-8"; //写入 await actionContext.Response.WriteAsync( JsonConvert.SerializeObject( new AjaxResponse( _errorInfoBuilder.BuildForException(exception), true ), serializerSettings ) ); whetherSignature = false; } } return whetherSignature; } //取出json的Name值 private string GetValueName(JObject inputObj, string path) { string result = null; if (!string.IsNullOrWhiteSpace(path)) { result = inputObj.SelectToken(path).ToObject<string>(); } return result; } /// <summary> /// Json字符串首字母转大写 /// </summary> /// <param name="strJsonData">json字符串</param> /// <returns></returns> public static string UpperFirst(string strJsonData) { MatchCollection matchCollection = Regex.Matches(strJsonData, "\"[a-zA-Z0-9]+\"\s*:"); foreach (Match item in matchCollection) { string res = Regex.Replace(item.Value, @"b[a-z]w+", delegate (Match match) { string val = match.ToString(); return char.ToUpper(val[0]) + val.Substring(1); }); strJsonData = strJsonData.Replace(item.Value, res); } return strJsonData; } } }
- Vue拦截器,拦截失败的响应,状态码为420的,中间件修改的响应的状态码
import { AppConsts } from '/@/abpPro/AppConsts'; import { abpService } from '/@/shared/abp'; import { Modal } from 'ant-design-vue'; import axios, { AxiosResponse } from 'axios'; import abpHttpConfiguration from './abp-http-configuration.service'; const apiHttpClient = axios.create({ baseURL: AppConsts.remoteServiceBaseUrl, timeout: 300000, }); // 请求拦截器 apiHttpClient.interceptors.request.use( (config: any) => { // .... return config; }, (error: any) => { return Promise.reject(error); }, ); // 响应拦截器 apiHttpClient.interceptors.response.use( (response: AxiosResponse) => { // 响应成功拦截器 if (response.data.__abp) { response.data = response.data.result; } return response; }, (error: any) => { // 响应失败拦截器 //方法里存在异步,使用一个Promise包裹起来 return new Promise((resolve, reject) => { // 关闭所有模态框 Modal.destroyAll(); const errorResponse = error.response; const ajaxResponse = abpHttpConfiguration.getAbpAjaxResponseOrNull(error.response); if (ajaxResponse != null) { abpHttpConfiguration.handleAbpResponse(errorResponse, ajaxResponse); reject(error); } else { if (errorResponse.status == 420) { //abpHttpConfiguration中自己写的一个模态框弹窗,把响应数据传入其中 abpHttpConfiguration.needIntercept(errorResponse.data) .toPromise()//Observable转Promise .then((value) => { if (value) { // resolve 原先的请求地址,重发请求 resolve(apiHttpClient(errorResponse.config)); } else { reject(error); } }); } else { abpHttpConfiguration.handleNonAbpErrorResponse(errorResponse); reject(error); } } }); }, ); export default apiHttpClient;
- 模态框弹窗,返回的bool类型
//是否验证需求通过弹窗 needIntercept(error): Observable<Boolean> { return new Observable<Boolean>((obs) => { if (error != undefined && error.SignStatus != null && !error.SignStatus) { //弹出模态框 this.modalCreate(error).subscribe( (b) => { obs.next(b); obs.complete(); }, (error) => console.log(error, 123), () => { obs.next(false); obs.complete(); }, ); } else { obs.next(false); obs.complete(); } }); } //电子签名弹窗 modalCreate(responseBody: any): Observable<Boolean> { let sub; if (!responseBody.IsAccountSign) { //弹出模态框,指定的组件GESignNameComponent ,传入参数 sub = modalHelper.create( GESignNameComponent, { relationId: responseBody.ModelSignNameId, listEsignRequirementId: responseBody.ListSignRequirementId, }, ); } else { //弹出模态框,GESignNameAccountComponent ,传入参数 sub = modalHelper.create( GESignNameAccountComponent, { relationId: responseBody.ModelSignNameId, listEsignRequirementId: responseBody.ListSignRequirementId, }, ); } return sub; }