{"id":1108,"date":"2025-05-27T14:46:16","date_gmt":"2025-05-27T06:46:16","guid":{"rendered":"https:\/\/www.hyy.net\/?p=1108"},"modified":"2025-05-27T14:46:16","modified_gmt":"2025-05-27T06:46:16","slug":"ultimate-asp-net-core-web-api-5-global-error-handling","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=1108","title":{"rendered":"Ultimate ASP.NET Core Web API  5 GLOBAL ERROR HANDLING"},"content":{"rendered":"<p>5 GLOBAL ERROR HANDLING<\/p>\n<p>5 \u5168\u5c40\u9519\u8bef\u5904\u7406<\/p>\n<p>Exception handling helps us deal with the unexpected behavior of our system. To handle exceptions, we use the try-catch block in our code as well as the finally keyword to clean up our resources afterward.\u200c<br \/>\n\u5f02\u5e38\u5904\u7406\u6709\u52a9\u4e8e\u6211\u4eec\u5904\u7406\u7cfb\u7edf\u7684\u610f\u5916\u884c\u4e3a\u3002\u4e3a\u4e86\u5904\u7406\u5f02\u5e38\uff0c\u6211\u4eec\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528 try-catch \u5757\uff0c\u7136\u540e\u4f7f\u7528 finally \u5173\u952e\u5b57\u6765\u6e05\u7406\u6211\u4eec\u7684\u8d44\u6e90\u3002<\/p>\n<p>Even though there is nothing wrong with the try-catch blocks in our Actions and methods in the Web API project, we can extract all the exception handling logic into a single centralized place. By doing that, we make our actions cleaner, more readable, and the error handling process more maintainable.<br \/>\n\u5373\u4f7f Web API \u9879\u76ee\u4e2d\u7684 Actions \u548c methods \u4e2d\u7684 try-catch \u5757\u6ca1\u6709\u95ee\u9898\uff0c\u6211\u4eec\u4e5f\u53ef\u4ee5\u5c06\u6240\u6709\u5f02\u5e38\u5904\u7406\u903b\u8f91\u63d0\u53d6\u5230\u4e00\u4e2a\u96c6\u4e2d\u7684\u4f4d\u7f6e\u3002\u901a\u8fc7\u8fd9\u6837\u505a\uff0c\u6211\u4eec\u4f7f\u6211\u4eec\u7684\u4f5c\u66f4\u6e05\u6670\u3001\u66f4\u5177\u53ef\u8bfb\u6027\uff0c\u5e76\u4e14\u9519\u8bef\u5904\u7406\u8fc7\u7a0b\u66f4\u6613\u4e8e\u7ef4\u62a4\u3002<\/p>\n<p>In this chapter, we are going to refactor our code to use the built-in middleware for global error handling to demonstrate the benefits of this approach. Since we already talked about the middleware in ASP.NET Core (in section 1.8), this section should be easier to understand.<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u91cd\u6784\u6211\u4eec\u7684\u4ee3\u7801\uff0c\u4ee5\u4f7f\u7528\u5185\u7f6e\u4e2d\u95f4\u4ef6\u8fdb\u884c\u5168\u5c40\u9519\u8bef\u5904\u7406\uff0c\u4ee5\u6f14\u793a\u8fd9\u79cd\u65b9\u6cd5\u7684\u597d\u5904\u3002\u7531\u4e8e\u6211\u4eec\u5df2\u7ecf\u5728 ASP.NET Core \u4e2d\u8ba8\u8bba\u4e86\u4e2d\u95f4\u4ef6\uff08\u5728 1.8 \u8282\u4e2d\uff09\uff0c\u56e0\u6b64\u672c\u8282\u5e94\u8be5\u66f4\u5bb9\u6613\u7406\u89e3\u3002<\/p>\n<h2>5.1 Handling Errors Globally with the Built-In Middleware<\/h2>\n<p>5.1 \u4f7f\u7528\u5185\u7f6e\u4e2d\u95f4\u4ef6\u5168\u5c40\u5904\u7406\u9519\u8bef<\/p>\n<p>The UseExceptionHandler middleware is a built-in middleware that we can use to handle exceptions. So, let\u2019s dive into the code to see this middleware in action.\u200c<br \/>\nUseExceptionHandler \u4e2d\u95f4\u4ef6\u662f\u4e00\u4e2a\u5185\u7f6e\u4e2d\u95f4\u4ef6\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u5b83\u6765\u5904\u7406\u5f02\u5e38\u3002\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u6df1\u5165\u7814\u7a76\u4ee3\u7801\uff0c\u770b\u770b\u8fd9\u4e2a\u4e2d\u95f4\u4ef6\u7684\u5b9e\u9645\u6548\u679c\u3002<\/p>\n<p>We are going to create a new ErrorModel folder in the Entities project, and add the new class ErrorDetails in that folder:<br \/>\n\u6211\u4eec\u5c06\u5728 Entities \u9879\u76ee\u4e2d\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 ErrorModel \u6587\u4ef6\u5939\uff0c\u5e76\u5728\u8be5\u6587\u4ef6\u5939\u4e2d\u6dfb\u52a0\u65b0\u7684\u7c7b ErrorDetails\uff1a<\/p>\n<pre><code>using System.Text.Json;\n\nnamespace Entities.ErrorModel\n{\n    public class ErrorDetails\n    {\n        public int StatusCode { get; set; }\n        public string? Message { get; set; }\n        public override string ToString() =&gt; JsonSerializer.Serialize(this);\n    }\n}<\/code><\/pre>\n<p>We are going to use this class for the details of our error message.<br \/>\n\u6211\u4eec\u5c06\u4f7f\u7528\u8fd9\u4e2a\u7c7b\u6765\u83b7\u53d6\u9519\u8bef\u6d88\u606f\u7684\u8be6\u7ec6\u4fe1\u606f\u3002<\/p>\n<p>To continue, in the Extensions folder in the main project, we are going to add a new static class: ExceptionMiddlewareExtensions.cs.<br \/>\n\u8981\u7ee7\u7eed\uff0c\u5728\u4e3b\u9879\u76ee\u7684 Extensions \u6587\u4ef6\u5939\u4e2d\uff0c\u6211\u4eec\u5c06\u6dfb\u52a0\u65b0\u7684\u9759\u6001\u7c7b\uff1aExceptionMiddlewareExtensions.cs\u3002<\/p>\n<p>Now, we need to modify it:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u9700\u8981\u4fee\u6539\u5b83\uff1a<\/p>\n<pre><code>using Contracts;\nusing Entities.ErrorModel;\nusing Microsoft.AspNetCore.Diagnostics;\nusing System.Net;\n\nnamespace CompanyEmployees.Extensions\n{\n    public static class ExceptionMiddlewareExtensions\n    {\n        public static void ConfigureExceptionHandler(this WebApplication app, ILoggerManager logger)\n        {\n            app.UseExceptionHandler(appError =&gt;\n            {\n                appError.Run(async context =&gt;\n                {\n                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;\n                    context.Response.ContentType = &quot;application\/json&quot;;\n                    var contextFeature = context.Features.Get&lt;IExceptionHandlerFeature&gt;();\n                    if (contextFeature != null)\n                    {\n                        logger.LogError($&quot;Something went wrong: {contextFeature.Error}&quot;);\n                        await context.Response.WriteAsync(new ErrorDetails()\n                        {\n                            StatusCode = context.Response.StatusCode,\n                            Message = &quot;Internal Server Error.&quot;,\n                        }.ToString());\n                    }\n                });\n            });\n        }\n    }\n}<\/code><\/pre>\n<p>In the code above, we create an extension method, on top of the WebApplication type, and we call the UseExceptionHandler method. That method adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline.<br \/>\n\u5728\u4e0a\u9762\u7684\u4ee3\u7801\u4e2d\uff0c\u6211\u4eec\u5728 WebApplication \u7c7b\u578b\u4e4b\u4e0a\u521b\u5efa\u4e86\u4e00\u4e2a\u6269\u5c55\u65b9\u6cd5\uff0c\u5e76\u8c03\u7528 UseExceptionHandler \u65b9\u6cd5\u3002\u8be5\u65b9\u6cd5\u5c06\u4e2d\u95f4\u4ef6\u6dfb\u52a0\u5230\u7ba1\u9053\u4e2d\uff0c\u8be5\u4e2d\u95f4\u4ef6\u5c06\u6355\u83b7\u5f02\u5e38\u3001\u8bb0\u5f55\u5f02\u5e38\uff0c\u5e76\u5728\u5907\u7528\u7ba1\u9053\u4e2d\u91cd\u65b0\u6267\u884c\u8bf7\u6c42\u3002<\/p>\n<p>Inside the UseExceptionHandler method, we use the appError variable of the IApplicationBuilder type. With that variable, we call the Run method, which adds a terminal middleware delegate to the application\u2019s pipeline. This is something we already know from section 1.8.<br \/>\n\u5728 UseExceptionHandler \u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528 IApplicationBuilder \u7c7b\u578b\u7684 appError \u53d8\u91cf\u3002\u4f7f\u7528\u8be5\u53d8\u91cf\uff0c\u6211\u4eec\u8c03\u7528 Run \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5c06\u7ec8\u7aef\u4e2d\u95f4\u4ef6\u59d4\u6258\u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u7684\u7ba1\u9053\u4e2d\u3002\u8fd9\u662f\u6211\u4eec\u4ece 1.8 \u8282\u4e2d\u5df2\u7ecf\u77e5\u9053\u7684\u3002<\/p>\n<p>Then, we populate the status code and the content type of our response, log the error message and finally return the response with the custom-created object. Later on, we are going to modify this middleware even more to support our business logic in a service layer.<br \/>\n\u7136\u540e\uff0c\u6211\u4eec\u586b\u5145\u54cd\u5e94\u7684\u72b6\u6001\u4ee3\u7801\u548c\u5185\u5bb9\u7c7b\u578b\uff0c\u8bb0\u5f55\u9519\u8bef\u6d88\u606f\uff0c\u6700\u540e\u8fd4\u56de\u5305\u542b\u81ea\u5b9a\u4e49\u521b\u5efa\u5bf9\u8c61\u7684\u54cd\u5e94\u3002\u7a0d\u540e\uff0c\u6211\u4eec\u5c06\u8fdb\u4e00\u6b65\u4fee\u6539\u8fd9\u4e2a\u4e2d\u95f4\u4ef6\uff0c\u4ee5\u652f\u6301\u6211\u4eec\u5728\u670d\u52a1\u5c42\u4e2d\u7684\u4e1a\u52a1\u903b\u8f91\u3002<\/p>\n<p>Of course, there are several namespaces we should add to make this work:<br \/>\n\u5f53\u7136\uff0c\u6211\u4eec\u5e94\u8be5\u6dfb\u52a0\u51e0\u4e2a\u547d\u540d\u7a7a\u95f4\u624d\u80fd\u5b9e\u73b0\u6b64\u76ee\u7684\uff1a<\/p>\n<pre><code>using Contracts;\nusing Entities.ErrorModel;\nusing Microsoft.AspNetCore.Diagnostics;\nusing System.Net;<\/code><\/pre>\n<h2>5.2 Program Class Modification<\/h2>\n<p>5.2 Program\u7c7b\u4fee\u6539<\/p>\n<p>To be able to use this extension method, let\u2019s modify the Program class:\u200c<br \/>\n\u4e3a\u4e86\u80fd\u591f\u4f7f\u7528\u8fd9\u4e2a\u6269\u5c55\u65b9\u6cd5\uff0c\u8ba9\u6211\u4eec\u4fee\u6539 Program \u7c7b\uff1a<\/p>\n<pre><code>using CompanyEmployees.Extensions;\nusing Contracts;\nusing Microsoft.AspNetCore.HttpOverrides;\nusing NLog;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\/\/ Add services to the container.\nLogManager.LoadConfiguration(\n    string.Concat(Directory.GetCurrentDirectory(),\n    &quot;\/nlog.config&quot;));\nbuilder.Services.ConfigureLoggerService();\n\nbuilder.Services.ConfigureCors();\nbuilder.Services.ConfigureIISIntegration();\nbuilder.Services.ConfigureRepositoryManager();\nbuilder.Services.ConfigureServiceManager();\nbuilder.Services.ConfigureSqlContext(builder.Configuration);\nbuilder.Services.AddAutoMapper(typeof(Program));\nbuilder.Services.AddControllers().AddApplicationPart(typeof(CompanyEmployees.Presentation.AssemblyReference).Assembly);\n\nvar app = builder.Build();\n\n\/\/ Configure the HTTP request pipeline.\nvar logger = app.Services.GetRequiredService&lt;ILoggerManager&gt;();\napp.ConfigureExceptionHandler(logger);\n\napp.UseHttpsRedirection();\n\napp.UseForwardedHeaders(new ForwardedHeadersOptions\n{\n    ForwardedHeaders = ForwardedHeaders.All\n});\n\napp.UseCors(&quot;CorsPolicy&quot;);\napp.UseAuthorization();\n\napp.MapControllers();\n\napp.Run();<\/code><\/pre>\n<p>Here, we first extract the ILoggerManager service inside the logger variable. Then, we just call the ConfigureExceptionHandler method and pass that logger service. It is important to know that we have to extract the ILoggerManager service after the var app = builder.Build() code line because the Build method builds the WebApplication and registers all the services added with IOC.<br \/>\n\u5728\u8fd9\u91cc\uff0c\u6211\u4eec\u9996\u5148\u63d0\u53d6 logger \u53d8\u91cf\u4e2d\u7684 ILoggerManager \u670d\u52a1\u3002\u7136\u540e\uff0c\u6211\u4eec\u53ea\u9700\u8c03\u7528 ConfigureExceptionHandler \u65b9\u6cd5\u5e76\u4f20\u9012\u8be5 Logger \u670d\u52a1\u3002\u8bf7\u52a1\u5fc5\u77e5\u9053\uff0c\u6211\u4eec\u5fc5\u987b\u5728 var app = builder.Build() \u4e4b\u540e\u63d0\u53d6 ILoggerManager \u670d\u52a1\uff0c\u56e0\u4e3a Build \u65b9\u6cd5\u6784\u5efa WebApplication \u5e76\u6ce8\u518c\u4f7f\u7528 IOC \u6dfb\u52a0\u7684\u6240\u6709\u670d\u52a1\u3002<\/p>\n<p>Additionally, we remove the call to the UseDeveloperExceptionPage method in the development environment since we don\u2019t need it now and it also interferes with our error handler middleware.<br \/>\n\u6b64\u5916\uff0c\u6211\u4eec\u5728\u5f00\u53d1\u73af\u5883\u4e2d\u5220\u9664\u4e86\u5bf9 UseDeveloperExceptionPage \u65b9\u6cd5\u7684\u8c03\u7528\uff0c\u56e0\u4e3a\u6211\u4eec\u73b0\u5728\u4e0d\u9700\u8981\u5b83\uff0c\u5b83\u8fd8\u4f1a\u5e72\u6270\u6211\u4eec\u7684\u9519\u8bef\u5904\u7406\u7a0b\u5e8f\u4e2d\u95f4\u4ef6\u3002<\/p>\n<p>Finally, let\u2019s remove the try-catch block from the GetAllCompanies service method:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u4ece GetAllCompanies \u670d\u52a1\u65b9\u6cd5\u4e2d\u5220\u9664 try-catch \u5757\uff1a<\/p>\n<pre><code>using AutoMapper;\nusing Contracts;\nusing Entities.Models;\nusing Service.Contracts;\nusing Shared;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Service\n{\n    internal sealed class CompanyService : ICompanyService\n    {\n        private readonly IRepositoryManager _repository;\n        private readonly ILoggerManager _logger;\n        private readonly IMapper _mapper;\n        public CompanyService(IRepositoryManager repository, ILoggerManager logger, IMapper mapper)\n        {\n            _repository = repository;\n            _logger = logger;\n            _mapper = mapper;\n        }\n        \/\/public IEnumerable&lt;CompanyDto&gt; GetAllCompanies(bool trackChanges)\n        \/\/{\n        \/\/    try\n        \/\/    {\n        \/\/        var companies = _repository.Company.GetAllCompanies(trackChanges);\n        \/\/        var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies);\n        \/\/        return companiesDto;\n        \/\/    }\n        \/\/    catch (Exception ex)\n        \/\/    {\n        \/\/        _logger.LogError($&quot;Something went wrong in the {nameof(GetAllCompanies)} service method {ex}&quot;);\n        \/\/        throw;\n        \/\/    }\n        \/\/}\n\n        public IEnumerable&lt;CompanyDto&gt; GetAllCompanies(bool trackChanges)\n        {\n            var companies = _repository.Company.GetAllCompanies(trackChanges);\n            var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies);\n            return companiesDto;\n        }\n\n    }\n}<\/code><\/pre>\n<p>And from our GetCompanies action:<br \/>\n\u4ece\u6211\u4eec\u7684 GetCompanies action\u4e2d\uff1a<\/p>\n<pre><code>using Microsoft.AspNetCore.Mvc;\nusing Service.Contracts;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace CompanyEmployees.Presentation.Controllers\n{\n    [Route(&quot;api\/[controller]&quot;)]\n    [ApiController]\n    public class CompaniesController : ControllerBase\n    {\n        private readonly IServiceManager _service;\n        public CompaniesController(IServiceManager service) =&gt; _service = service;\n\n        \/\/[HttpGet]\n        \/\/public IActionResult GetCompanies()\n        \/\/{\n        \/\/    try\n        \/\/    {\n        \/\/        var companies = _service.CompanyService.GetAllCompanies(trackChanges: false);\n        \/\/        return Ok(companies);\n        \/\/    }\n        \/\/    catch\n        \/\/    {\n        \/\/        return StatusCode(500, &quot;Internal server error&quot;);\n        \/\/    }\n        \/\/}\n\n        [HttpGet]\n        public IActionResult GetCompanies()\n        {\n            var companies = _service.CompanyService.GetAllCompanies(trackChanges: false);\n            return Ok(companies);\n        }\n\n    }\n}<\/code><\/pre>\n<p>And there we go. Our methods are much cleaner now. More importantly, we can reuse this functionality to write more readable methods and actions in the future.<br \/>\n\u597d\u4e86\u3002\u6211\u4eec\u73b0\u5728\u7684\u65b9\u6cd5\u8981\u5e72\u51c0\u5f97\u591a\u3002\u66f4\u91cd\u8981\u7684\u662f\uff0c\u6211\u4eec\u53ef\u4ee5\u5728\u672a\u6765\u91cd\u7528\u6b64\u529f\u80fd\u6765\u7f16\u5199\u66f4\u591a\u53ef\u8bfb\u7684\u65b9\u6cd5\u548c\u4f5c\u3002<\/p>\n<h2>5.3 Testing the Result<\/h2>\n<p>5.3 \u6d4b\u8bd5\u7ed3\u679c<\/p>\n<p>To inspect this functionality, let\u2019s add the following line to the\u200c GetCompanies action, just to simulate an error:<br \/>\n\u4e3a\u4e86\u68c0\u67e5\u6b64\u529f\u80fd\uff0c\u8ba9\u6211\u4eec\u5c06\u4ee5\u4e0b\u884c\u6dfb\u52a0\u5230 GetCompanies\u4f5c\u4e2d\uff0c\u4ee5\u6a21\u62df\u9519\u8bef\uff1a<\/p>\n<pre><code>        [HttpGet]\n        public IActionResult GetCompanies()\n        {\n            throw new Exception(&quot;Exception&quot;);\n            var companies = _service.CompanyService.GetAllCompanies(trackChanges: false);\n            return Ok(companies);\n        }<\/code><\/pre>\n<p>NOTE: Once you send the request, Visual Studio will stop the execution inside the GetCompanies action on the line where we throw an exception. This is normal behavior and all you have to do is to click the continue button to finish the request flow. Additionally, you can start your app with CTRL+F5, which will prevent Visual Studio from stopping the execution. Also, if you want to start your app with F5 but still to avoid VS execution stoppages, you can open the Tools-&gt;Options-&gt;Debugging-&gt;General option and uncheck the Enable Just My Code checkbox.<br \/>\n\u6ce8\u610f\uff1a\u53d1\u9001\u8bf7\u6c42\u540e\uff0cVisual Studio \u5c06\u5728\u5f15\u53d1\u5f02\u5e38\u7684\u884c\u4e0a\u7684 GetCompanies\u4f5c\u4e2d\u505c\u6b62\u6267\u884c\u3002\u8fd9\u662f\u6b63\u5e38\u884c\u4e3a\uff0c\u60a8\u6240\u8981\u505a\u7684\u5c31\u662f\u5355\u51fb continue \uff08\u7ee7\u7eed\uff09 \u6309\u94ae\u4ee5\u5b8c\u6210\u8bf7\u6c42\u6d41\u3002\u6b64\u5916\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 Ctrl+F5 \u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\uff0c\u8fd9\u5c06\u963b\u6b62 Visual Studio \u505c\u6b62\u6267\u884c\u3002\u6b64\u5916\uff0c\u5982\u679c\u60a8\u60f3\u4f7f\u7528 F5 \u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u4ecd\u8981\u907f\u514d VS \u6267\u884c\u505c\u6b62\uff0c\u5219\u53ef\u4ee5\u6253\u5f00 Tools-&gt;Options-&gt;Debugging-&gt;General \u9009\u9879\uff0c\u5e76\u53d6\u6d88\u9009\u4e2d Enable Just My Code \u590d\u9009\u6846\u3002<\/p>\n<p>And send a request from Postman:<br \/>\n\u4ece Postman\u53d1\u9001\u8bf7\u6c42\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\">https:\/\/localhost:5001\/api\/companies<\/a> <\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/0501.jpg\" ><\/p>\n<p>We can check our log messages to make sure that logging is working as well.<br \/>\n\u6211\u4eec\u53ef\u4ee5\u68c0\u67e5\u65e5\u5fd7\u6d88\u606f\u4ee5\u786e\u4fdd\u65e5\u5fd7\u8bb0\u5f55\u4e5f\u6b63\u5e38\u5de5\u4f5c\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>5 GLOBAL ERROR HANDLING 5 \u5168\u5c40\u9519\u8bef\u5904\u7406 Exception handling helps us deal with the unexpected behavior of our system. To handle exceptions, we use the try-catch block in our code as well as the finally keyword to clean up our resources afterward.\u200c \u5f02\u5e38\u5904\u7406\u6709\u52a9\u4e8e\u6211\u4eec\u5904\u7406\u7cfb\u7edf\u7684\u610f\u5916\u884c\u4e3a\u3002\u4e3a\u4e86\u5904\u7406\u5f02\u5e38\uff0c\u6211\u4eec\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528 try-catch \u5757\uff0c\u7136\u540e\u4f7f\u7528 finally \u5173\u952e\u5b57\u6765\u6e05\u7406\u6211\u4eec\u7684\u8d44\u6e90\u3002 Even though there is nothing wrong with the try-catch [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1108","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1108","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1108"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1108\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1108"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1108"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1108"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}