{"id":1162,"date":"2025-05-27T14:48:19","date_gmt":"2025-05-27T06:48:19","guid":{"rendered":"https:\/\/www.hyy.net\/?p=1162"},"modified":"2025-05-27T14:48:19","modified_gmt":"2025-05-27T06:48:19","slug":"ultimate-asp-net-core-web-api-32-bonus-1-response-performance-improvements","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=1162","title":{"rendered":"Ultimate ASP.NET Core Web API 32 BONUS 1 &#8211; RESPONSE PERFORMANCE IMPROVEMENTS"},"content":{"rendered":"<p>32 BONUS 1 - RESPONSE PERFORMANCE IMPROVEMENTS<br \/>\n32 \u5956\u52b1 1 - \u54cd\u5e94\u6027\u80fd\u6539\u8fdb<\/p>\n<p>As mentioned in section 6.1.1, we will show you an alternative way of handling error responses. To repeat, with custom exceptions, we have great control of returning error responses to the client due to the global error handler, which is pretty fast if we use it correctly. Also, the code is pretty clean and straightforward since we don\u2019t have to care about the return types and additional validation in the service methods.\u200c<br \/>\n\u5982 6.1.1 \u8282\u6240\u8ff0\uff0c\u6211\u4eec\u5c06\u5411\u60a8\u5c55\u793a\u4e00\u79cd\u5904\u7406\u9519\u8bef\u54cd\u5e94\u7684\u66ff\u4ee3\u65b9\u6cd5\u3002\u91cd\u590d\u4e00\u904d\uff0c\u5bf9\u4e8e\u81ea\u5b9a\u4e49\u5f02\u5e38\uff0c\u7531\u4e8e\u5168\u5c40\u9519\u8bef\u5904\u7406\u7a0b\u5e8f\uff0c\u6211\u4eec\u53ef\u4ee5\u5f88\u597d\u5730\u63a7\u5236\u5c06\u9519\u8bef\u54cd\u5e94\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef\uff0c\u5982\u679c\u6211\u4eec\u6b63\u786e\u4f7f\u7528\u5b83\uff0c\u8fd9\u5c06\u975e\u5e38\u5feb\u3002\u6b64\u5916\uff0c\u4ee3\u7801\u975e\u5e38\u7b80\u6d01\u660e\u4e86\uff0c\u56e0\u4e3a\u6211\u4eec\u4e0d\u5fc5\u5173\u5fc3\u670d\u52a1\u65b9\u6cd5\u4e2d\u7684\u8fd4\u56de\u7c7b\u578b\u548c\u5176\u4ed6\u9a8c\u8bc1\u3002<\/p>\n<p>Even though some libraries enable us to write custom responses, for example, OneOf, we still like to create our abstraction logic, which is tested by us and fast. Additionally, we want to show you the whole creation process for such a flow.<br \/>\n\u5c3d\u7ba1\u4e00\u4e9b\u5e93\u5141\u8bb8\u6211\u4eec\u7f16\u5199\u81ea\u5b9a\u4e49\u54cd\u5e94\uff0c\u4f8b\u5982 OneOf\uff0c\u4f46\u6211\u4eec\u4ecd\u7136\u559c\u6b22\u521b\u5efa\u6211\u4eec\u7684\u62bd\u8c61\u903b\u8f91\uff0c\u5b83\u7531\u6211\u4eec\u6d4b\u8bd5\u5e76\u4e14\u5feb\u901f\u3002\u6b64\u5916\uff0c\u6211\u4eec\u8fd8\u60f3\u5411\u60a8\u5c55\u793a\u6b64\u7c7b\u6d41\u7a0b\u7684\u6574\u4e2a\u521b\u5efa\u8fc7\u7a0b\u3002<\/p>\n<p>For this example, we will use an existing project from part 6 and modify it to implement our API Response flow.<br \/>\n\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u5c06\u4f7f\u7528 \u7b2c 6 \u90e8\u5206\u4e2d\u7684\u73b0\u6709\u9879\u76ee\uff0c\u5e76\u5bf9\u5176\u8fdb\u884c\u4fee\u6539\u4ee5\u5b9e\u73b0\u6211\u4eec\u7684 API \u54cd\u5e94\u6d41\u3002<\/p>\n<h2>32.1 Adding Response Classes to the Project<\/h2>\n<p>32.1 \u5411\u9879\u76ee\u6dfb\u52a0\u54cd\u5e94\u7c7b<\/p>\n<p>Let\u2019s start with the API response model classes.\u200c<br \/>\n\u8ba9\u6211\u4eec\u4ece API \u54cd\u5e94\u6a21\u578b\u7c7b\u5f00\u59cb\u3002<\/p>\n<p>The first thing we are going to do is create a new Responses folder in the Entities project. Inside that folder, we are going to add our first class:<br \/>\n\u6211\u4eec\u8981\u505a\u7684\u7b2c\u4e00\u4ef6\u4e8b\u662f\u5728 Entities \u9879\u76ee\u4e2d\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 Responses \u6587\u4ef6\u5939\u3002\u5728\u8be5\u6587\u4ef6\u5939\u4e2d\uff0c\u6211\u4eec\u5c06\u6dfb\u52a0\u6211\u4eec\u7684\u7b2c\u4e00\u4e2a\u7c7b\uff1a<\/p>\n<pre><code>public abstract class ApiBaseResponse { public bool Success { get; set; } protected ApiBaseResponse(bool success) =&gt; Success = success; }<\/code><\/pre>\n<p>This is an abstract class, which will be the main return type for all of our methods where we have to return a successful result or an error result. It also contains a single Success property stating whether the action was successful or not.<br \/>\n\u8fd9\u662f\u4e00\u4e2a\u62bd\u8c61\u7c7b\uff0c\u5b83\u5c06\u662f\u6211\u4eec\u6240\u6709\u65b9\u6cd5\u7684\u4e3b\u8981\u8fd4\u56de\u7c7b\u578b\uff0c\u6211\u4eec\u5fc5\u987b\u8fd4\u56de\u6210\u529f\u7ed3\u679c\u6216\u9519\u8bef\u7ed3\u679c\u3002\u5b83\u8fd8\u5305\u542b\u4e00\u4e2a Success \u5c5e\u6027\uff0c\u7528\u4e8e\u8bf4\u660e\u4f5c\u662f\u5426\u6210\u529f\u3002<\/p>\n<p>Now, if our result is successful, we are going to create only one class in the same folder:<br \/>\n\u73b0\u5728\uff0c\u5982\u679c\u6211\u4eec\u7684\u7ed3\u679c\u6210\u529f\uff0c\u6211\u4eec\u5c06\u53ea\u5728\u540c\u4e00\u4e2a\u6587\u4ef6\u5939\u4e2d\u521b\u5efa\u4e00\u4e2a\u7c7b\uff1a<\/p>\n<pre><code>public sealed class ApiOkResponse&lt;TResult&gt; : ApiBaseResponse { public TResult Result { get; set; } public ApiOkResponse(TResult result) :base(true) { Result = result; } }<\/code><\/pre>\n<p>We are going to use this class as a return type for a successful result. It inherits from the ApiBaseResponse and populates the Success property to true through the constructor. It also contains a single Result property of type TResult. We will store our concrete result in this property, and since we can have different result types in different methods, this property is a generic one.<br \/>\n\u6211\u4eec\u5c06\u4f7f\u7528\u8fd9\u4e2a\u7c7b\u4f5c\u4e3a\u6210\u529f\u7ed3\u679c\u7684\u8fd4\u56de\u7c7b\u578b\u3002\u5b83\u7ee7\u627f\u81ea ApiBaseResponse\uff0c\u5e76\u901a\u8fc7\u6784\u9020\u51fd\u6570\u5c06 Success \u5c5e\u6027\u586b\u5145\u4e3a true\u3002\u5b83\u8fd8\u5305\u542b\u4e00\u4e2a TResult \u7c7b\u578b\u7684 Result \u5c5e\u6027\u3002\u6211\u4eec\u5c06\u5177\u4f53\u7ed3\u679c\u5b58\u50a8\u5728\u6b64\u5c5e\u6027\u4e2d\uff0c\u7531\u4e8e\u6211\u4eec\u53ef\u4ee5\u5728\u4e0d\u540c\u7684\u65b9\u6cd5\u4e2d\u5177\u6709\u4e0d\u540c\u7684\u7ed3\u679c\u7c7b\u578b\uff0c\u56e0\u6b64\u6b64\u5c5e\u6027\u662f\u901a\u7528\u5c5e\u6027\u3002<\/p>\n<p>That\u2019s all regarding the successful responses. Let\u2019s move one to the error classes.<br \/>\n\u8fd9\u5c31\u662f\u5173\u4e8e\u6210\u529f\u54cd\u5e94\u7684\u5168\u90e8\u5185\u5bb9\u3002\u8ba9\u6211\u4eec\u5c06\u4e00\u4e2a\u79fb\u52a8\u5230 error \u7c7b\u3002<\/p>\n<p>For the error responses, we will follow the same structure as we have for the exception classes. So, we will have base abstract classes for NotFound or BadRequest or any other error responses, and then concrete implementations for these classes like CompanyNotFound or CompanyBadRequest, etc.<br \/>\n\u5bf9\u4e8e\u9519\u8bef\u54cd\u5e94\uff0c\u6211\u4eec\u5c06\u9075\u5faa\u4e0e\u5f02\u5e38\u7c7b\u76f8\u540c\u7684\u7ed3\u6784\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u5c06\u4e3a NotFound \u6216 BadRequest \u6216\u4efb\u4f55\u5176\u4ed6\u9519\u8bef\u54cd\u5e94\u63d0\u4f9b\u57fa\u672c\u62bd\u8c61\u7c7b\uff0c\u7136\u540e\u4e3a\u8fd9\u4e9b\u7c7b\u63d0\u4f9b\u5177\u4f53\u5b9e\u73b0\uff0c\u4f8b\u5982 CompanyNotFound \u6216 CompanyBadRequest \u7b49\u3002<\/p>\n<p>That said, let\u2019s use the same folder to create an abstract error class:<br \/>\n\u4e5f\u5c31\u662f\u8bf4\uff0c\u8ba9\u6211\u4eec\u4f7f\u7528\u76f8\u540c\u7684\u6587\u4ef6\u5939\u6765\u521b\u5efa\u4e00\u4e2a\u62bd\u8c61\u9519\u8bef\u7c7b\uff1a<\/p>\n<pre><code>public abstract class ApiNotFoundResponse : ApiBaseResponse { public string Message { get; set; } public ApiNotFoundResponse(string message) : base(false) { Message = message; } }<\/code><\/pre>\n<p>This class also inherits from the ApiBaseResponse, populates the Success property to false, and has a single Message property for the error message.<br \/>\n\u6b64\u7c7b\u8fd8\u7ee7\u627f\u81ea ApiBaseResponse\uff0c\u5c06 Success \u5c5e\u6027\u586b\u5145\u4e3a false\uff0c\u5e76\u4e14\u5177\u6709\u9519\u8bef\u6d88\u606f\u7684\u5355\u4e2a Message \u5c5e\u6027\u3002<\/p>\n<p>In the same manner, we can create the ApiBadRequestResponse class:<br \/>\n\u4ee5\u540c\u6837\u7684\u65b9\u5f0f\uff0c\u6211\u4eec\u53ef\u4ee5\u521b\u5efa ApiBadRequestResponse \u7c7b\uff1a<\/p>\n<pre><code>public abstract class ApiBadRequestResponse : ApiBaseResponse { public string Message { get; set; } public ApiBadRequestResponse(string message) : base(false) { Message = message; } }<\/code><\/pre>\n<p>This is the same implementation as the previous one. The important thing to notice is that both of these classes are abstract.<br \/>\n\u8fd9\u4e0e\u4e0a\u4e00\u4e2a\u5b9e\u73b0\u76f8\u540c\u3002\u9700\u8981\u6ce8\u610f\u7684\u91cd\u8981\u4e00\u70b9\u662f\uff0c\u8fd9\u4e24\u4e2a\u7c7b\u90fd\u662f\u62bd\u8c61\u7684\u3002<\/p>\n<p>To continue, let\u2019s create a concrete error response:<br \/>\n\u4e3a\u4e86\u7ee7\u7eed\uff0c\u8ba9\u6211\u4eec\u521b\u5efa\u4e00\u4e2a\u5177\u4f53\u7684\u9519\u8bef\u54cd\u5e94\uff1a<\/p>\n<pre><code>public sealed class CompanyNotFoundResponse : ApiNotFoundResponse { public CompanyNotFoundResponse(Guid id) : base($&quot;Company with id: {id} is not found in db.&quot;) { } }<\/code><\/pre>\n<p>The class inherits from the ApiNotFoundResponse abstract class, which again inherits from the ApiBaseResponse class. It accepts an id parameter and creates a message that sends to the base class.<br \/>\n\u8be5\u7c7b\u7ee7\u627f\u81ea ApiNotFoundResponse \u62bd\u8c61\u7c7b\uff0c\u800c ApiNotFoundResponse \u62bd\u8c61\u7c7b\u53c8\u7ee7\u627f\u81ea ApiBaseResponse \u7c7b\u3002\u5b83\u63a5\u53d7 id \u53c2\u6570\u5e76\u521b\u5efa\u53d1\u9001\u5230\u57fa\u7c7b\u7684\u6d88\u606f\u3002<\/p>\n<p>We are not going to create the CompanyBadRequestResponse class because we are not going to need it in our example. But the principle is the same.<br \/>\n\u6211\u4eec\u4e0d\u6253\u7b97\u521b\u5efa CompanyBadRequestResponse \u7c7b\uff0c\u56e0\u4e3a\u5728\u6211\u4eec\u7684\u793a\u4f8b\u4e2d\u4e0d\u9700\u8981\u5b83\u3002\u4f46\u539f\u7406\u662f\u4e00\u6837\u7684\u3002<\/p>\n<h2>32.2 Service Layer Modification<\/h2>\n<p>32.2 \u670d\u52a1\u5c42\u4fee\u6539<\/p>\n<p>Now that we have the response model classes, we can start with the service layer modification.\u200c<br \/>\n\u73b0\u5728\u6211\u4eec\u6709\u4e86\u54cd\u5e94\u6a21\u578b\u7c7b\uff0c\u6211\u4eec\u53ef\u4ee5\u4ece\u670d\u52a1\u5c42\u4fee\u6539\u5f00\u59cb\u3002<\/p>\n<p>Let\u2019s start with the ICompanyService interface:<br \/>\n\u8ba9\u6211\u4eec\u4ece ICompanyService \u63a5\u53e3\u5f00\u59cb\uff1a<\/p>\n<pre><code>public interface ICompanyService { ApiBaseResponse GetAllCompanies(bool trackChanges); ApiBaseResponse GetCompany(Guid companyId, bool trackChanges); }<\/code><\/pre>\n<p>We don\u2019t return concrete types in our methods anymore. Instead of the IEnumerable<CompanyDto> or CompanyDto return types, we return the ApiBaseResponse type. This will enable us to return either the success result or to return any of the error response results.<br \/>\n\u6211\u4eec\u4e0d\u518d\u5728\u65b9\u6cd5\u4e2d\u8fd4\u56de\u5177\u4f53\u7c7b\u578b\u3002 \u6211\u4eec\u8fd4\u56de ApiBaseResponse \u7c7b\u578b\uff0c\u800c\u4e0d\u662f IEnumerable \u6216 CompanyDto \u8fd4\u56de\u7c7b\u578b\u3002\u8fd9\u5c06\u4f7f\u6211\u4eec\u80fd\u591f\u8fd4\u56de\u6210\u529f\u7ed3\u679c\u6216\u8fd4\u56de\u4efb\u4f55\u9519\u8bef\u54cd\u5e94\u7ed3\u679c\u3002<\/p>\n<p>After the interface modification, we can modify the CompanyService class:<br \/>\n\u4fee\u6539\u63a5\u53e3\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u4fee\u6539 CompanyService \u7c7b\uff1a<\/p>\n<pre><code>public ApiBaseResponse GetAllCompanies(bool trackChanges) { var companies = _repository.Company.GetAllCompanies(trackChanges); var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies); return new ApiOkResponse&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companiesDto); } public ApiBaseResponse GetCompany(Guid id, bool trackChanges) { var company = _repository.Company.GetCompany(id, trackChanges); if (company is null) return new CompanyNotFoundResponse(id); var companyDto = _mapper.Map&lt;CompanyDto&gt;(company); return new ApiOkResponse&lt;CompanyDto&gt;(companyDto); }<\/code><\/pre>\n<p>Both method signatures are modified to use APIBaseResponse, and also the return types are modified accordingly. Additionally, in the GetCompany method, we are not using an exception class to return an error result but the CompanyNotFoundResponse class. With the ApiBaseResponse abstraction, we are safe to return multiple types from our method as long as they inherit from the ApiBaseResponse abstract class. Here you could also log some messages with _logger.<br \/>\n\u4e24\u4e2a\u65b9\u6cd5\u7b7e\u540d\u90fd\u88ab\u4fee\u6539\u4e3a\u4f7f\u7528 APIBaseResponse\uff0c\u5e76\u4e14\u8fd4\u56de\u7c7b\u578b\u4e5f\u76f8\u5e94\u5730\u88ab\u4fee\u6539\u3002\u6b64\u5916\uff0c\u5728 GetCompany \u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u6ca1\u6709\u4f7f\u7528\u5f02\u5e38\u7c7b\u6765\u8fd4\u56de\u9519\u8bef\u7ed3\u679c\uff0c\u800c\u662f\u4f7f\u7528 CompanyNotFoundResponse \u7c7b\u3002\u4f7f\u7528 ApiBaseResponse \u62bd\u8c61\uff0c\u6211\u4eec\u53ef\u4ee5\u5b89\u5168\u5730\u4ece\u65b9\u6cd5\u4e2d\u8fd4\u56de\u591a\u4e2a\u7c7b\u578b\uff0c\u53ea\u8981\u5b83\u4eec\u7ee7\u627f\u81ea ApiBaseResponse \u62bd\u8c61\u7c7b\u3002\u5728\u8fd9\u91cc\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 _logger \u8bb0\u5f55\u4e00\u4e9b\u6d88\u606f\u3002<\/p>\n<p>One more thing to notice here.<br \/>\n\u8fd9\u91cc\u8fd8\u6709\u4e00\u70b9\u9700\u8981\u6ce8\u610f\u3002<\/p>\n<p>In the GetAllCompanies method, we don\u2019t have an error response just a successful one. That means we didn\u2019t have to implement our Api response flow, and we could\u2019ve left the method unchanged (in the interface and this class). If you want that kind of implementation it is perfectly fine. We just like consistency in our projects, and due to that fact, we\u2019ve changed both methods.<br \/>\n\u5728 GetAllCompanies \u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u6ca1\u6709\u9519\u8bef\u54cd\u5e94\uff0c\u53ea\u6709\u4e00\u4e2a\u6210\u529f\u7684\u54cd\u5e94\u3002\u8fd9\u610f\u5473\u7740\u6211\u4eec\u4e0d\u5fc5\u5b9e\u73b0 Api \u54cd\u5e94\u6d41\uff0c\u5e76\u4e14\u53ef\u4ee5\u4fdd\u6301\u65b9\u6cd5\u4e0d\u53d8\uff08\u5728\u63a5\u53e3\u548c\u8fd9\u4e2a\u7c7b\u4e2d\uff09\u3002\u5982\u679c\u4f60\u60f3\u8981\u8fd9\u79cd\u5b9e\u73b0\uff0c\u90a3\u5b8c\u5168\u6ca1\u95ee\u9898\u3002\u6211\u4eec\u5c31\u50cf\u6211\u4eec\u9879\u76ee\u4e2d\u7684\u4e00\u81f4\u6027\u4e00\u6837\uff0c\u56e0\u6b64\uff0c\u6211\u4eec\u6539\u53d8\u4e86\u8fd9\u4e24\u79cd\u65b9\u6cd5\u3002<\/p>\n<h2>32.3 Controller Modification<\/h2>\n<p>32.3 \u63a7\u5236\u5668\u4fee\u6539<\/p>\n<p>Before we start changing the actions in the CompaniesController, we have to create a way to handle error responses and return them to the client \u2013 similar to what we have with the global error handler middleware.\u200c<br \/>\n\u5728\u6211\u4eec\u5f00\u59cb\u66f4\u6539 CompaniesController \u4e2d\u7684\u4f5c\u4e4b\u524d\uff0c\u6211\u4eec\u5fc5\u987b\u521b\u5efa\u4e00\u79cd\u65b9\u6cd5\u6765\u5904\u7406\u9519\u8bef\u54cd\u5e94\u5e76\u5c06\u5176\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef \u2013 \u7c7b\u4f3c\u4e8e\u6211\u4eec\u4f7f\u7528\u5168\u5c40\u9519\u8bef\u5904\u7406\u7a0b\u5e8f\u4e2d\u95f4\u4ef6\u7684\u65b9\u6cd5\u3002<\/p>\n<p>We are not going to create any additional middleware but another controller base class inside the Presentation\/Controllers folder:<br \/>\n\u6211\u4eec\u4e0d\u6253\u7b97\u521b\u5efa\u4efb\u4f55\u5176\u4ed6\u4e2d\u95f4\u4ef6\uff0c\u800c\u662f\u5728 Presentation\/Controllers \u6587\u4ef6\u5939\u4e2d\u521b\u5efa\u53e6\u4e00\u4e2a\u63a7\u5236\u5668\u57fa\u7c7b\uff1a<\/p>\n<pre><code>public class ApiControllerBase : ControllerBase { public IActionResult ProcessError(ApiBaseResponse baseResponse) { return baseResponse switch { ApiNotFoundResponse =&gt; NotFound(new ErrorDetails { Message = ((ApiNotFoundResponse)baseResponse).Message, StatusCode = StatusCodes.Status404NotFound }), ApiBadRequestResponse =&gt; BadRequest(new ErrorDetails { Message = ((ApiBadRequestResponse)baseResponse).Message, StatusCode = StatusCodes.Status400BadRequest }), _ =&gt; throw new NotImplementedException() }; } }<\/code><\/pre>\n<p>This class inherits from the ControllerBase class and implements a single ProcessError action accepting an ApiBaseResponse parameter. Inside the action, we are inspecting the type of the sent parameter, and based on that type we return an appropriate message to the client. A similar thing we did in the exception middleware class.<br \/>\n\u6b64\u7c7b\u7ee7\u627f\u81ea ControllerBase \u7c7b\uff0c\u5e76\u5b9e\u73b0\u63a5\u53d7 ApiBaseResponse \u53c2\u6570\u7684\u5355\u4e2a ProcessError\u4f5c\u3002\u5728\u4f5c\u4e2d\uff0c\u6211\u4eec\u5c06\u68c0\u67e5\u5df2\u53d1\u9001\u53c2\u6570\u7684\u7c7b\u578b\uff0c\u5e76\u6839\u636e\u8be5\u7c7b\u578b\u5411\u5ba2\u6237\u7aef\u8fd4\u56de\u9002\u5f53\u7684\u6d88\u606f\u3002\u6211\u4eec\u5728\u5f02\u5e38\u4e2d\u95f4\u4ef6\u7c7b\u4e2d\u505a\u4e86\u7c7b\u4f3c\u7684\u4e8b\u60c5\u3002<\/p>\n<p>If you add additional error response classes to the Response folder, you only have to add them here to process the response for the client.<br \/>\n\u5982\u679c\u5411 Response \u6587\u4ef6\u5939\u6dfb\u52a0\u5176\u4ed6\u9519\u8bef\u54cd\u5e94\u7c7b\uff0c\u5219\u53ea\u9700\u5728\u6b64\u5904\u6dfb\u52a0\u5b83\u4eec\u5373\u53ef\u5904\u7406\u5ba2\u6237\u7aef\u7684\u54cd\u5e94\u3002<\/p>\n<p>Additionally, this is where we can see the advantage of our abstraction approach.<br \/>\n\u6b64\u5916\uff0c\u8fd9\u5c31\u662f\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u62bd\u8c61\u65b9\u6cd5\u7684\u4f18\u52bf\u7684\u5730\u65b9\u3002<\/p>\n<p>Now, we can modify our CompaniesController:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u4fee\u6539\u6211\u4eec\u7684 CompaniesController\uff1a<\/p>\n<pre><code>[Route(&quot;api\/companies&quot;)] [ApiController] public class CompaniesController : ApiControllerBase { private readonly IServiceManager _service; public CompaniesController(IServiceManager service) =&gt; _service = service; [HttpGet] public IActionResult GetCompanies() { var baseResult = _service.CompanyService.GetAllCompanies(trackChanges: false); var companies = ((ApiOkResponse&lt;IEnumerable&lt;CompanyDto&gt;&gt;)baseResult).Result; return Ok(companies); } [HttpGet(&quot;{id:guid}&quot;)] public IActionResult GetCompany(Guid id) { var baseResult = _service.CompanyService.GetCompany(id, trackChanges: false); if (!baseResult.Success) return ProcessError(baseResult); var company = ((ApiOkResponse&lt;CompanyDto&gt;)baseResult).Result; return Ok(company); } }<\/code><\/pre>\n<p>Now our controller inherits from the ApiControllerBase, which inherits from the ControllerBase class. In the GetCompanies action, we extract the result from the service layer and cast the baseResult variable to the concrete ApiOkResponse type, and use the Result property to extract our required result of type IEnumerable<CompanyDto>.<br \/>\n\u73b0\u5728\u6211\u4eec\u7684\u63a7\u5236\u5668\u7ee7\u627f\u81ea ApiControllerBase\uff0c\u800c ApiControllerBase \u7ee7\u627f\u81ea ControllerBase \u7c7b\u3002\u5728 GetCompanies\u4f5c\u4e2d\uff0c\u6211\u4eec\u4ece\u670d\u52a1\u5c42\u63d0\u53d6\u7ed3\u679c\uff0c\u5e76\u5c06 baseResult \u53d8\u91cf\u8f6c\u6362\u4e3a\u5177\u4f53\u7684 ApiOkResponse \u7c7b\u578b\uff0c\u5e76\u4f7f\u7528 Result \u5c5e\u6027\u63d0\u53d6\u6240\u9700\u7684 IEnumerable \u7c7b\u578b\u7ed3\u679c\u3002<\/p>\n<p>We do a similar thing for the GetCompany action. Of course, here we check if our result is successful and if it\u2019s not, we return the result of the ProcessError method.<br \/>\n\u6211\u4eec\u5bf9 GetCompany\u4f5c\u6267\u884c\u7c7b\u4f3c\u7684\u4f5c\u3002\u5f53\u7136\uff0c\u8fd9\u91cc\u6211\u4eec\u68c0\u67e5\u7ed3\u679c\u662f\u5426\u6210\u529f\uff0c\u5982\u679c\u4e0d\u662f\uff0c\u6211\u4eec\u8fd4\u56de ProcessError \u65b9\u6cd5\u7684\u7ed3\u679c\u3002<\/p>\n<p>And that\u2019s it.<br \/>\n\u5c31\u662f\u8fd9\u6837\u3002<\/p>\n<p>We can leave the solution as is, but we mind having these castings inside our actions \u2013 they can be moved somewhere else making them reusable and our actions cleaner. So, let\u2019s do that.<br \/>\n\u6211\u4eec\u53ef\u4ee5\u4fdd\u6301\u89e3\u51b3\u65b9\u6848\u4e0d\u53d8\uff0c\u4f46\u6211\u4eec\u4ecb\u610f\u5728\u6211\u4eec\u7684 action \u4e2d\u653e\u7f6e\u8fd9\u4e9b\u94f8\u4ef6\u2014\u2014\u5b83\u4eec\u53ef\u4ee5\u79fb\u52a8\u5230\u5176\u4ed6\u5730\u65b9\uff0c\u4f7f\u5b83\u4eec\u53ef\u91cd\u7528\uff0c\u6211\u4eec\u7684 action \u66f4\u5e72\u51c0\u3002\u6240\u4ee5\uff0c\u8ba9\u6211\u4eec\u5f00\u59cb\u5427\u3002<\/p>\n<p>In the same project, we are going to create a new Extensions folder and a new ApiBaseResponseExtensions class:<br \/>\n\u5728\u540c\u4e00\u4e2a\u9879\u76ee\u4e2d\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 Extensions \u6587\u4ef6\u5939\u548c\u4e00\u4e2a\u65b0\u7684 ApiBaseResponseExtensions \u7c7b\uff1a<\/p>\n<pre><code>public static class ApiBaseResponseExtensions { public static TResultType GetResult&lt;TResultType&gt;(this ApiBaseResponse apiBaseResponse) =&gt; ((ApiOkResponse&lt;TResultType&gt;)apiBaseResponse).Result; }<\/code><\/pre>\n<p>The GetResult method will extend the ApiBaseResponse type and return the result of the required type.<br \/>\nGetResult \u65b9\u6cd5\u5c06\u6269\u5c55 ApiBaseResponse \u7c7b\u578b\u5e76\u8fd4\u56de\u6240\u9700\u7c7b\u578b\u7684\u7ed3\u679c\u3002<\/p>\n<p>Now, we can modify actions inside the controller:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u4fee\u6539\u63a7\u5236\u5668\u5185\u90e8\u7684 action\uff1a<\/p>\n<pre><code>[HttpGet] public IActionResult GetCompanies() { var baseResult = _service.CompanyService.GetAllCompanies(trackChanges: false); var companies = baseResult.GetResult&lt;IEnumerable&lt;CompanyDto&gt;&gt;(); return Ok(companies); } [HttpGet(&quot;{id:guid}&quot;)] public IActionResult GetCompany(Guid id) { var baseResult = _service.CompanyService.GetCompany(id, trackChanges: false); if (!baseResult.Success) return ProcessError(baseResult); var company = baseResult.GetResult&lt;CompanyDto&gt;(); return Ok(company); }<\/code><\/pre>\n<p>This is much cleaner and easier to read and understand.<br \/>\n\u8fd9\u66f4\u7b80\u6d01\uff0c\u66f4\u5bb9\u6613\u9605\u8bfb\u548c\u7406\u89e3\u3002<\/p>\n<p>32.4 Testing the API Response Flow<\/p>\n<h2>32.4 \u6d4b\u8bd5 API \u54cd\u5e94\u6d41<\/h2>\n<p>Now we can start our application, open Postman, and send some requests.\u200c<br \/>\n\u73b0\u5728\u6211\u4eec\u53ef\u4ee5\u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\uff0c\u6253\u5f00 Postman\uff0c\u5e76\u53d1\u9001\u4e00\u4e9b\u8bf7\u6c42\u3002<\/p>\n<p>Let\u2019s try to get all the companies:<br \/>\n\u8ba9\u6211\u4eec\u5c1d\u8bd5\u83b7\u53d6\u6240\u6709\u516c\u53f8\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\">https:\/\/localhost:5001\/api\/companies<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/3201.jpg\" alt=\"alt text\" \/><\/p>\n<p>Then, we can try to get a single company:<br \/>\n\u7136\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u5c1d\u8bd5\u83b7\u5f97\u4e00\u5bb6\u516c\u53f8\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce3\">https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce3<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/3202.jpg\" alt=\"alt text\" \/><\/p>\n<p>And finally, let\u2019s try to get a company that does not exist:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u5c1d\u8bd5\u83b7\u5f97\u4e00\u5bb6\u4e0d\u5b58\u5728\u7684\u516c\u53f8\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce2\">https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce2<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/3203.jpg\" alt=\"alt text\" \/><\/p>\n<p>And we have our response with a proper status code and response body. Excellent.<br \/>\n\u6211\u4eec\u7684\u54cd\u5e94\u5177\u6709\u9002\u5f53\u7684\u72b6\u6001\u4ee3\u7801\u548c\u54cd\u5e94\u6b63\u6587\u3002\u975e\u5e38\u597d\u3002<\/p>\n<p>We have a solution that is easy to implement, fast, and extendable.<br \/>\n\u6211\u4eec\u6709\u4e00\u4e2a\u6613\u4e8e\u5b9e\u65bd\u3001\u5feb\u901f\u4e14\u53ef\u6269\u5c55\u7684\u89e3\u51b3\u65b9\u6848\u3002<\/p>\n<p>Our suggestion is to go with custom exceptions since they are easier to implement and fast as well. But if you have an app flow where you have to return error responses at a much higher rate and thus maybe impact the app\u2019s performance, the APi Response flow is the way to go.<br \/>\n\u6211\u4eec\u5efa\u8bae\u4f7f\u7528\u81ea\u5b9a\u4e49\u5f02\u5e38\uff0c\u56e0\u4e3a\u5b83\u4eec\u66f4\u5bb9\u6613\u5b9e\u73b0\u4e14\u901f\u5ea6\u66f4\u5feb\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6d41\u5fc5\u987b\u4ee5\u66f4\u9ad8\u7684\u901f\u7387\u8fd4\u56de\u9519\u8bef\u54cd\u5e94\uff0c\u4ece\u800c\u53ef\u80fd\u4f1a\u5f71\u54cd\u5e94\u7528\u7a0b\u5e8f\u7684\u6027\u80fd\uff0c\u90a3\u4e48 APi \u54cd\u5e94\u6d41\u5c31\u662f\u60a8\u7684\u4e0d\u4e8c\u4e4b\u9009\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>32 BONUS 1 &#8211; RESPONSE PERFORMANCE IMPROVEMENTS 32 \u5956\u52b1 1 &#8211; \u54cd\u5e94\u6027\u80fd\u6539\u8fdb As mentioned in section 6.1.1, we will show you an alternative way of handling error responses. To repeat, with custom exceptions, we have great control of returning error responses to the client due to the global error handler, which is pretty fast if [&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-1162","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1162","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=1162"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1162\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1162"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1162"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1162"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}