Saya mulai menggunakan ASP.NET Inti saya baru SISA proyek API setelah menggunakan regular ASP.NET Web API selama bertahun-tahun. Saya don't melihat cara yang baik untuk menangani pengecualian di ASP.NET Inti Web API. Saya mencoba untuk menerapkan pengecualian penanganan filter/atribut:
public class ErrorHandlingFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
HandleExceptionAsync(context);
context.ExceptionHandled = true;
}
private static void HandleExceptionAsync(ExceptionContext context)
{
var exception = context.Exception;
if (exception is MyNotFoundException)
SetExceptionResult(context, exception, HttpStatusCode.NotFound);
else if (exception is MyUnauthorizedException)
SetExceptionResult(context, exception, HttpStatusCode.Unauthorized);
else if (exception is MyException)
SetExceptionResult(context, exception, HttpStatusCode.BadRequest);
else
SetExceptionResult(context, exception, HttpStatusCode.InternalServerError);
}
private static void SetExceptionResult(
ExceptionContext context,
Exception exception,
HttpStatusCode code)
{
context.Result = new JsonResult(new ApiResponse(exception))
{
StatusCode = (int)code
};
}
}
Dan berikut adalah Startup filter pendaftaran:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilter());
options.Filters.Add(new ErrorHandlingFilter());
});
Masalah yang saya alami adalah bahwa ketika terkecuali occurres saya AuthorizationFilter
it's tidak sedang ditangani oleh ErrorHandlingFilter
. Saya mengharapkan untuk dapat menangkap ada hanya seperti itu bekerja dengan tua ASP.NET Web API.
Jadi bagaimana saya bisa menangkap semua aplikasi pengecualian serta pengecualian dari Tindakan Filter?
Setelah banyak percobaan dengan berbagai pengecualian penanganan pendekatan akhirnya saya menggunakan middleware. Itu berhasil keluar yang terbaik bagi saya ASP.NET Inti API Web aplikasi. Menangani pengecualian aplikasi serta pengecualian dari tindakan filter dan saya memiliki kontrol penuh atas penanganan eksepsi dan respon HTTP. Berikut adalah pengecualian penanganan middleware:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError; // 500 if unexpected
if (ex is MyNotFoundException) code = HttpStatusCode.NotFound;
else if (ex is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
else if (ex is MyException) code = HttpStatusCode.BadRequest;
var result = JsonConvert.SerializeObject(new { error = ex.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
Mendaftar sebelum MVC di Startup
kelas:
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();
Anda dapat menambahkan stack trace, terkecuali ketik nama, kode kesalahan atau apa pun yang anda inginkan untuk itu. Sangat fleksibel. Berikut adalah contoh dari pengecualian respon:
{ "error": "Authentication token is not valid." }
Mempertimbangkan menyuntikkan IOptions<MvcJsonOptions>
untuk Memanggil
metode untuk kemudian menggunakannya ketika anda membuat respon objek untuk memanfaatkan ASP.NET MVC's serialisasi pengaturan di JsonConvert.SerializeObject(errorObj, memilih.Nilai.SerializerSettings)
untuk lebih baik serialisasi konsistensi di semua endpoint.
Ada lagi non-jelas API yang disebut UseExceptionHandler
yang bekerja "ok" untuk sederhana scenarious:
app.UseExceptionHandler(a => a.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = feature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));
Hal ini tidak sangat jelas, tetapi cara mudah untuk mengatur penanganan eksepsi. Namun saya masih lebih suka middleware pendekatan yang lebih dari itu karena saya mendapatkan lebih banyak kontrol dengan kemampuan untuk menyuntikkan dependensi yang diperlukan.
Terbaru Asp.Net Core
(setidaknya dari 2.2, mungkin sebelumnya) memiliki built-in middleware yang membuatnya sedikit lebih mudah dibandingkan dengan pelaksanaan di jawaban yang diterima:
app.UseExceptionHandler(a => a.Run(async context =>
{
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = exceptionHandlerPathFeature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));
Itu harus melakukan cukup banyak hal yang sama, hanya sedikit kode untuk menulis. Ingat untuk menambahkan sebelum UseMvc
sebagai urutan penting.
Anda bertaruh terbaik adalah untuk menggunakan Middleware untuk mencapai penebangan anda're looking for. Anda ingin menempatkan anda terkecuali logging di salah satu middleware dan kemudian menangani kesalahan halaman yang ditampilkan untuk pengguna yang berbeda middleware. Yang memungkinkan pemisahan logika dan mengikuti desain Microsoft telah ditata dengan 2 middleware componenets. Berikut ini's link yang bagus untuk Microsoft's dokumentasi: Kesalahan Penanganan dalam ASP.Net Inti
Untuk contoh, anda mungkin ingin menggunakan salah satu ekstensi di StatusCodePage middleware atau roll sendiri seperti ini.
Anda dapat menemukan contoh di sini untuk login pengecualian: ExceptionHandlerMiddleware.cs
public void Configure(IApplicationBuilder app)
{
// app.UseErrorPage(ErrorPageOptions.ShowAll);
// app.UseStatusCodePages();
// app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
// app.UseStatusCodePages("text/plain", "Response, status code: {0}");
// app.UseStatusCodePagesWithRedirects("~/errors/{0}");
// app.UseStatusCodePagesWithRedirects("/base/errors/{0}");
// app.UseStatusCodePages(builder => builder.UseWelcomePage());
app.UseStatusCodePagesWithReExecute("/Errors/{0}"); // I use this version
// Exception handling logging below
app.UseExceptionHandler();
}
Jika anda don't seperti yang spesifik implementasi, maka anda juga dapat menggunakan ELM Middleware, dan berikut adalah beberapa contoh: Elm Terkecuali Middleware
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePagesWithReExecute("/Errors/{0}");
// Exception handling logging below
app.UseElmCapture();
app.UseElmPage();
}
Jika itu doesn't bekerja untuk kebutuhan anda, anda selalu dapat memutar sendiri Middleware komponen, dengan melihat implementasi dari ExceptionHandlerMiddleware dan ElmMiddleware untuk memahami konsep-konsep untuk membangun anda sendiri.
It's penting untuk menambahkan pengecualian penanganan middleware di bawah StatusCodePages middleware tetapi di atas semua yang lain komponen middleware. Dengan cara itu anda Terkecuali middleware akan menangkap pengecualian, log ini, maka memungkinkan permintaan untuk melanjutkan ke StatusCodePage middleware yang akan menampilkan ramah halaman kesalahan kepada pengguna.
Untuk Mengkonfigurasi pengecualian penanganan perilaku per jenis pengecualian anda dapat menggunakan Middleware dari paket NuGet:
ASP.NET Core 2.0
ASP.NET Inti 2.1+
. Contoh kode:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddExceptionHandlingPolicies(options =>
{
options.For<InitializationException>().Rethrow();
options.For<SomeTransientException>().Retry(ro => ro.MaxRetryCount = 2).NextPolicy();
options.For<SomeBadRequestException>()
.Response(e => 400)
.Headers((h, e) => h["X-MyCustomHeader"] = e.Message)
.WithBody((req,sw, exception) =>
{
byte[] array = Encoding.UTF8.GetBytes(exception.ToString());
return sw.WriteAsync(array, 0, array.Length);
})
.NextPolicy();
// Ensure that all exception types are handled by adding handler for generic exception at the end.
options.For<Exception>()
.Log(lo =>
{
lo.EventIdFactory = (c, e) => new EventId(123, "UnhandlerException");
lo.Category = (context, exception) => "MyCategory";
})
.Response(null, ResponseAlreadyStartedBehaviour.GoToNextHandler)
.ClearCacheHeaders()
.WithObjectResult((r, e) => new { msg = e.Message, path = r.Path })
.Handled();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandlingPolicies();
app.UseMvc();
}
Nah jawaban yang diterima banyak membantu saya, tapi saya ingin lulus HttpStatusCode saya middleware untuk mengelola kesalahan kode status pada saat runtime.
Menurut link ini aku punya beberapa ide untuk melakukan hal yang sama. Jadi saya bergabung Andrei Menjawab dengan hal ini. Jadi saya akhir kode di bawah ini:
1. Kelas dasar
public class ErrorDetails
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
public class HttpStatusCodeException : Pengecualian { publik HttpStatusCode StatusCode { get; set; } public string ContentType { get; set; } = @"text/plain";
publik HttpStatusCodeException(HttpStatusCode statusCode) { ini.StatusCode = statusCode; }
publik HttpStatusCodeException(HttpStatusCode statusCode, string pesan) : base(pesan) { ini.StatusCode = statusCode; }
publik HttpStatusCodeException(HttpStatusCode statusCode, Terkecuali batin) : ini(statusCode, batin.ToString()) { }
publik HttpStatusCodeException(HttpStatusCode statusCode, JObject errorObject) : ini(statusCode, errorObject.ToString()) { ini.ContentType = @"application/json"; }
}
3. Pengecualian Kustom Middleware
public class CustomExceptionMiddleware
{
private readonly RequestDelegate next;
public CustomExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (HttpStatusCodeException ex)
{
await HandleExceptionAsync(context, ex);
}
catch (Exception exceptionObj)
{
await HandleExceptionAsync(context, exceptionObj);
}
}
private Task HandleExceptionAsync(HttpContext context, HttpStatusCodeException exception)
{
string result = null;
context.Response.ContentType = "application/json";
if (exception is HttpStatusCodeException)
{
result = new ErrorDetails() { Message = exception.Message, StatusCode = (int)exception.StatusCode }.ToString();
context.Response.StatusCode = (int)exception.StatusCode;
}
else
{
result = new ErrorDetails() { Message = "Runtime Error", StatusCode = (int)HttpStatusCode.BadRequest }.ToString();
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
return context.Response.WriteAsync(result);
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
string result = new ErrorDetails() { Message = exception.Message, StatusCode = (int)HttpStatusCode.InternalServerError }.ToString();
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return context.Response.WriteAsync(result);
}
}
4. Metode Penyuluhan
public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app)
{
app.UseMiddleware<CustomExceptionMiddleware>();
}
app.ConfigureCustomExceptionMiddleware();
app.UseMvc();
Sekarang saya metode login di Akun controller :
try
{
IRepository<UserMaster> obj = new Repository<UserMaster>(_objHeaderCapture, Constants.Tables.UserMaster);
var Result = obj.Get().AsQueryable().Where(sb => sb.EmailId.ToLower() == objData.UserName.ToLower() && sb.Password == objData.Password.ToEncrypt() && sb.Status == (int)StatusType.Active).FirstOrDefault();
if (Result != null)//User Found
return Result;
else// Not Found
throw new HttpStatusCodeException(HttpStatusCode.NotFound, "Please check username or password");
}
catch (Exception ex)
{
throw ex;
}
Di atas anda dapat melihat jika saya tidak menemukan pengguna kemudian menaikkan HttpStatusCodeException di mana saya telah lulus HttpStatusCode.NotFound status dan pesan khusus
Di middleware
menangkap (HttpStatusCodeException ex)
diblokir akan disebut yang akan melewati kontrol untuk
private Tugas HandleExceptionAsync(HttpContext konteks, HttpStatusCodeException pengecualian) metode
.
bagaimana jika aku punya runtime error sebelumnya? Untuk itu saya telah menggunakan try catch blok yang melempar pengecualian dan akan ditangkap di catch (Exception exceptionObj) blok dan akan melewati kontrol untuk
Tugas HandleExceptionAsync(HttpContext konteks, Pengecualian pengecualian)
metode.
saya telah menggunakan satu ErrorDetails kelas untuk keseragaman.
Pertama, terima kasih kepada Andrei seperti yang saya've berbasis solusi saya di teladan-nya.
I'm termasuk saya sebagai it's yang lebih lengkap sampel dan mungkin menyimpan pembaca beberapa waktu.
Keterbatasan Andrei's pendekatan yang doesn't menangani penebangan, menangkap berpotensi berguna variabel permintaan dan negosiasi konten (itu akan selalu kembali JSON tidak peduli apa yang klien telah meminta - XML / teks dll).
Pendekatan saya adalah dengan menggunakan ObjectResult yang memungkinkan kita untuk menggunakan fungsi-fungsi yang dipanggang menjadi MVC.
Kode ini juga mencegah caching respon.
Kesalahan respon yang telah didekorasi sedemikian rupa sehingga dapat menjadi serial dengan XML serializer.
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate next;
private readonly IActionResultExecutor<ObjectResult> executor;
private readonly ILogger logger;
private static readonly ActionDescriptor EmptyActionDescriptor = new ActionDescriptor();
public ExceptionHandlerMiddleware(RequestDelegate next, IActionResultExecutor<ObjectResult> executor, ILoggerFactory loggerFactory)
{
this.next = next;
this.executor = executor;
logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
logger.LogError(ex, $"An unhandled exception has occurred while executing the request. Url: {context.Request.GetDisplayUrl()}. Request Data: " + GetRequestData(context));
if (context.Response.HasStarted)
{
throw;
}
var routeData = context.GetRouteData() ?? new RouteData();
ClearCacheHeaders(context.Response);
var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);
var result = new ObjectResult(new ErrorResponse("Error processing request. Server error."))
{
StatusCode = (int) HttpStatusCode.InternalServerError,
};
await executor.ExecuteAsync(actionContext, result);
}
}
private static string GetRequestData(HttpContext context)
{
var sb = new StringBuilder();
if (context.Request.HasFormContentType && context.Request.Form.Any())
{
sb.Append("Form variables:");
foreach (var x in context.Request.Form)
{
sb.AppendFormat("Key={0}, Value={1}<br/>", x.Key, x.Value);
}
}
sb.AppendLine("Method: " + context.Request.Method);
return sb.ToString();
}
private static void ClearCacheHeaders(HttpResponse response)
{
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "-1";
response.Headers.Remove(HeaderNames.ETag);
}
[DataContract(Name= "ErrorResponse")]
public class ErrorResponse
{
[DataMember(Name = "Message")]
public string Message { get; set; }
public ErrorResponse(string message)
{
Message = message;
}
}
}
Pertama, mengkonfigurasi ASP.NET Core 2 Startup
untuk kembali mengeksekusi ke halaman kesalahan untuk kesalahan dari server web dan setiap pengecualian tidak tertangani.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) {
// Debug config here...
} else {
app.UseStatusCodePagesWithReExecute("/Error");
app.UseExceptionHandler("/Error");
}
// More config...
}
Selanjutnya, menentukan pengecualian tipe yang akan membiarkan anda melempar kesalahan dengan kode status HTTP.
public class HttpException : Exception
{
public HttpException(HttpStatusCode statusCode) { StatusCode = statusCode; }
public HttpStatusCode StatusCode { get; private set; }
}
Akhirnya, pada controller anda untuk kesalahan halaman, menyesuaikan respon didasarkan pada alasan untuk kesalahan dan apakah respon yang akan dilihat langsung oleh pengguna akhir. Kode ini mengasumsikan semua API Url yang dimulai dengan /api/
.
[AllowAnonymous]
public IActionResult Error()
{
// Gets the status code from the exception or web server.
var statusCode = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error is HttpException httpEx ?
httpEx.StatusCode : (HttpStatusCode)Response.StatusCode;
// For API errors, responds with just the status code (no page).
if (HttpContext.Features.Get<IHttpRequestFeature>().RawTarget.StartsWith("/api/", StringComparison.Ordinal))
return StatusCode((int)statusCode);
// Creates a view model for a user-friendly error page.
string text = null;
switch (statusCode) {
case HttpStatusCode.NotFound: text = "Page not found."; break;
// Add more as desired.
}
return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, ErrorText = text });
}
ASP.NET Inti akan log kesalahan detail untuk anda untuk men-debug, jadi kode status dapat menjadi semua yang anda ingin berikan kepada (berpotensi terpercaya) pemohon. Jika anda ingin menampilkan info lebih lanjut, anda dapat meningkatkan HttpException
untuk memberikan itu. Untuk API kesalahan, anda dapat menempatkan JSON-dikodekan kesalahan info di tubuh pesan dengan mengganti kembali StatusCode...
dengan return Json...
.
menggunakan middleware atau IExceptionHandlerPathFeature baik-baik saja. ada cara lain dalam eshop
membuat exceptionfilter dan mendaftar
public class HttpGlobalExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext konteks) {...} }
jasa.AddMvc(pilihan => { pilihan.Filter.Tambahkan(typeof(HttpGlobalExceptionFilter)); })