在MVC控制器或WebApi控制器(实际上是从业务层膨胀)处理异常并将其传递到angular,并在“用户错误”弹出窗口中显示它是否存在最佳实践或最流畅的方法?人们是如何解决这个问题的?
public class ApiValidationResult
{
public List<ApiValidationError> Errors { get; set; }
public static ApiValidationResult Failure(string errorKey)
{
return new ApiValidationResult {Errors = new List<ApiValidationError> {new ApiValidationError(errorKey)}};
}
// You can add a bunch of utility methods here
}
public class ApiValidationError
{
public ApiValidationError()
{
}
public ApiValidationError(string errorKey)
{
ErrorKey = errorKey;
}
// More utility constructors here
public string PropertyPath { get; set; }
public string ErrorKey { get; set; }
public List<string> ErrorParameters { get; set; }
}
我通常会使用自己的基类来编写WebApi(和MVC)控制器,这样我就可以使用它们来添加方便的结果方法,例如:
public abstract class ExtendedApiController : ApiController
{
protected IHttpActionResult ValidationError(string error)
{
return new ValidationErrorResult(ApiValidationResult.Failure(error), this);
}
// More utility methods can go here
}
IHttpActionResult
:public class ValidationErrorResult : NegotiatedContentResult<ApiValidationResult>
{
public ValidationErrorResult(ApiValidationResult content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
: base(HttpStatusCode.BadRequest, content, contentNegotiator, request, formatters)
{
}
public ValidationErrorResult(ApiValidationResult content, ApiController controller)
: base(HttpStatusCode.BadRequest, content, controller)
{
}
}
[HttpPost]
public IHttpActionResult SomeAction(SomeInput input)
{
// Do whatever...
if (resultIsValid)
{
return Ok(outputObject);
}
return ValidationResult(errorMessage);
}
正如我所说的,我认为只有真正的未处理Exception
应该导致500(内部服务器错误)响应。
WebApi自动将这些未处理异常转换为500结果。 我需要做的唯一事情就是记录它们。 因此,我创建了一个实现IExceptionLogger
接口的实现,并像这样注册它:
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new UnhandledExceptionLogger());
AngularJS允许拦截从$http
服务发送的所有HTTP调用。我使用这个功能来集中所有消息弹窗。以下是我的拦截器代码:
appModule.factory("errorsHttpInterceptor", [
"$q", "$rootScope", "$injector",
($q: ng.IQService, $rootScope: IAppRootScopeService, $injector) => {
return {
'responseError': rejection => {
// Maybe put the error in $rootScope and show it in UI
// Maybe use a popup
// Maybe use a 'toast'
var toastr = $injector.get('toastr');
toastr.error(...);
return $q.reject(rejection);
}
};
}
]);
拦截器可以执行各种操作,例如记录调试消息或应用密钥以显示错误代码的翻译。您还可以区分500和400错误,并显示不同类型的错误消息。
我使用toastr库,它可以显示漂亮的用户界面,在API级别非常方便。
最后,我像这样注册拦截器:
appModule.config([
'$httpProvider',
($httpProvider: ng.IHttpProvider) => {
$httpProvider.interceptors.push('errorsHttpInterceptor');
}
]);
这段代码使用的是TypeScript语法,与JavaScript非常相似,我相信您可以理解它的含义。
通常情况下,我在 WEB API 中完成此类工作。对于此类工作,返回正确的状态码非常关键,而且它与你想要使用的许多前端框架完全无关。
public IHttpActionResult Get(DateTime? updatesAfter = null)
{
try
{
// Do something here.
return this.Ok(result);
}
catch (Exception ex) // Be more specific if you like...
{
return this.InternalServerError(ex);
throw;
}
}
this.BadRequest()
this.InternalServerError()
this.Ok()
this.Unauthorized()
this.StatusCode()
this.NotFound()
其中一些(例如InternalServerError)允许您将异常或消息(或仅仅是对象)作为参数传递。
通常情况下,与任何前端框架或库一样,当初始化对API方法的ajax调用时,您可以提供失败或错误回调函数,这将在返回错误状态代码的情况下被调用。
Install-Package elmah
同时,它只是工作,错误会被记录下来,用户会收到有所不同的提示等等...没有额外的工作需要做。
为了使我们的UI更加美观,我们采取了以下步骤,针对:
未处理的错误
MVC页面
只需让MVC前端做好它的工作,它会以一种友好的方式告诉用户出现了问题。
Angular网络调用
在.error函数中向用户发出警报,例如:
}).error(function (data, status, headers, config) {
$scope.addAlert('danger', 'Error deleting autosave details');
}
如果您不想因为错误被覆盖而失去错误,请让错误堆积。addAlert只是将内容写入与前端绑定的数组中。
已处理的错误 如果您正在处理它们,那么您已经控制了发生的情况,但是要记录这些错误
MVC
如果您只想记录它们,elmah
API有一个单独的调用可用于此。
ErrorSignal.FromCurrentContext().Raise(exception );
如果您正在使用错误属性,那么可以使用以下方法:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}
}
public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// Log only handled exceptions, because all other will be caught by ELMAH anyway.
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
}
}
return Content(HttpStatusCode.<statuscode>, "ResponseContent: " + "my_custom_error");
//If I'm getting output from another API/Layer then I pass it's output like this
var output = response.Content.ReadAsStringAsync().Result;
return Content(response.StatusCode, "ResponseContent: " + output);
如需了解更多关于HttpStatusCodes的详情,请参考HttpStatusCode枚举
在Angular代码中,我是这样读取它的:
$http({ method: methodType, url: endpoint })
.then(function (response) {
response.status; //gets you the HttpStatusCode to play with
response.data; //gets you the ReponseContent section
}, function (response) {
response.status; //gets you the HttpStatusCode
response.data; //gets you the ReponseContent section
});
responseType
未设置为'JSON'。因为此阶段API返回的数据不是JSON格式。HttpResponseMessage
。请参见下面的示例。希望这可以帮助你。 // Get all
[HttpGet]
public HttpResponseMessage Get()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, myObject);
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error Message");
}
}
Angular JS
// Get Request on Page Load
$http({
url: '/api/department/',
method: 'GET',
}).success(function (data) {
$scope.departments = data;
}).error(function (error) {
$scope.error = error;
});
HTML
<div class="alert alert-danger animate-show" role="alert" ng-show="error != null">{{ error }} </div>
<div class="alert alert-success animate-show" role="alert" ng-show="success != null">{{ success }}</div>