{"id":160,"date":"2022-08-26T14:05:11","date_gmt":"2022-08-26T06:05:11","guid":{"rendered":"https:\/\/miie.net\/?p=160"},"modified":"2022-08-26T14:05:11","modified_gmt":"2022-08-26T06:05:11","slug":"ultimate-asp-net-core-web-api-chapter4-handing-get-requests","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=160","title":{"rendered":"Chapter4:HANDING GET REQUESTS"},"content":{"rendered":"<h1>4 HANDING GET REQUESTS<\/h1>\n<p>We\u2019re all set to add some business logic to our application. But before that, let\u2019s talk a bit about controller classes and routing because they play an important part while working with HTTP requests.<br \/>\n\u6211\u4eec\u90fd\u51c6\u5907\u5411\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u4e00\u4e9b\u4e1a\u52a1\u903b\u8f91\u3002\u4f46\u5728\u6b64\u4e4b\u524d\uff0c\u8ba9\u6211\u4eec\u6765\u8c08\u8c08\u63a7\u5236\u5668\u7c7b\u548c\u8def\u7531\uff0c\u56e0\u4e3a\u5b83\u4eec\u5728\u5904\u7406HTTP\u8bf7\u6c42\u65f6\u8d77\u7740\u91cd\u8981\u4f5c\u7528\u3002<\/p>\n<h2>4.1 Controllers and Routing in WEB API<\/h2>\n<p>Controllers should only be responsible for handling requests, model validation, and returning responses to the frontend or some HTTP client. Keeping business logic away from controllers is a good way to keep them lightweight, and our code more readable and maintainable.<br \/>\n\u63a7\u5236\u5668\u5e94\u8be5\u53ea\u8d1f\u8d23\u5904\u7406\u8bf7\u6c42\u3001\u6a21\u578b\u9a8c\u8bc1\u4ee5\u53ca\u5c06\u54cd\u5e94\u8fd4\u56de\u5230\u524d\u7aef\u6216\u67d0\u4e9b HTTP \u5ba2\u6237\u7aef\u3002\u4f7f\u4e1a\u52a1\u903b\u8f91\u8fdc\u79bb\u63a7\u5236\u5668\u662f\u4f7f\u5b83\u4eec\u4fdd\u6301\u8f7b\u91cf\u7ea7\u7684\u597d\u65b9\u6cd5\uff0c\u5e76\u4e14\u6211\u4eec\u7684\u4ee3\u7801\u66f4\u5177\u53ef\u8bfb\u6027\u548c\u53ef\u7ef4\u62a4\u6027\u3002<\/p>\n<p>To create the controller, right-click on the Controllers folder inside the main project and then Add=&gt;Controller. Then from the menu, choose API Controller Class and name it <strong>CompaniesController.cs<\/strong>.<br \/>\n\u82e5\u8981\u521b\u5efa\u63a7\u5236\u5668\uff0c\u8bf7\u53f3\u952e\u5355\u51fb\u4e3b\u9879\u76ee\u4e2d\u7684\u201cControllers\u201d\u6587\u4ef6\u5939\uff0c\u7136\u540e\u5355\u51fb\u201cAdd=&gt;Controller\u201d\u3002\u7136\u540e\uff0c\u4ece\u83dc\u5355\u4e2d\u9009\u62e9\u201cAPI \u63a7\u5236\u5668\u7c7b\u201d\u5e76\u5c06\u5176\u547d\u540d\u4e3a\u201c<strong>CompaniesController.cs<\/strong>\u201d\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4001.jpg\" alt=\"alt\" \/><\/p>\n<p>Our controller should be generated with the default code inside:<br \/>\n\u6211\u4eec\u7684\u63a7\u5236\u5668\u5e94\u8be5\u4f7f\u7528\u5185\u90e8\u7684\u9ed8\u8ba4\u4ee3\u7801\u751f\u6210\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Microsoft.AspNetCore.Mvc;\nnamespace CompanyEmployees.Controllers\n{\n    &#x5B;Route(&quot;api\/&#x5B;controller]&quot;)]\n    &#x5B;ApiController]\n    public class CompaniesController : ControllerBase\n    {\n    }\n}\n<\/pre>\n<\/p>\n<p>Every web API controller class inherits from the <strong>ControllerBase<\/strong> abstract class, which provides all necessary behavior for the derived class.<br \/>\n\u6bcf\u4e2a Web API \u63a7\u5236\u5668\u7c7b\u90fd\u7ee7\u627f\u81ea<strong>ControllerBase<\/strong>\u62bd\u8c61\u7c7b\uff0c\u5b83\u4e3a\u6d3e\u751f\u7c7b\u63d0\u4f9b\u6240\u6709\u5fc5\u8981\u7684\u884c\u4e3a\u3002<\/p>\n<p>Also, above the controller class we can see this part of the code:<br \/>\n\u53e6\u5916\uff0c\u5728\u63a7\u5236\u5668\u7c7b\u7684\u4e0a\u65b9\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u8fd9\u90e8\u5206\u4ee3\u7801\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;Route\uff08\u201capi\/&#x5B;controller]\u201d\uff09] \n<\/pre>\n<p>This attribute represents routing and we are going to talk more about routing inside Web APIs.<br \/>\n\u6b64\u5c5e\u6027\u8868\u793a\u8def\u7531\uff0c\u6211\u4eec\u5c06\u66f4\u591a\u5730\u8ba8\u8bba Web API \u4e2d\u7684\u8def\u7531\u3002<\/p>\n<p>Web API routing routes incoming HTTP requests to the particular action method inside the Web API controller. As soon as we send our HTTP request, the MVC framework parses that request and tries to match it to an action in the controller.<br \/>\nWeb API \u8def\u7531\u5c06\u4f20\u5165\u7684 HTTP \u8bf7\u6c42\u8def\u7531\u5230 Web API \u63a7\u5236\u5668\u5185\u7684\u7279\u5b9a\u64cd\u4f5c\u65b9\u6cd5\u3002\u4e00\u65e6\u6211\u4eec\u53d1\u9001 HTTP \u8bf7\u6c42\uff0cMVC \u6846\u67b6\u5c31\u4f1a\u89e3\u6790\u8be5\u8bf7\u6c42\uff0c\u5e76\u5c1d\u8bd5\u5c06\u5176\u4e0e\u63a7\u5236\u5668\u4e2d\u7684\u64cd\u4f5c\u8fdb\u884c\u5339\u914d\u3002<\/p>\n<p>There are two ways to implement routing in the project:<br \/>\n\u6709\u4e24\u79cd\u65b9\u6cd5\u53ef\u4ee5\u5728\u9879\u76ee\u4e2d\u5b9e\u73b0\u8def\u7531\uff1a<\/p>\n<ul>\n<li>Convention based routing and<br \/>\n\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531<\/li>\n<li>Attribute routing<br \/>\n\u5c5e\u6027\u8def\u7531<\/li>\n<\/ul>\n<p><strong>Convention based routing<\/strong> is called such because it establishes a convention for the URL paths. <strong>The first part<\/strong> creates the mapping for the controller name, the <strong>second part<\/strong> creates the mapping for the action method, and <strong>the third part<\/strong> is used for the optional parameter. We can configure this type of routing in the <strong>Startup<\/strong> class in the <strong>Configure<\/strong> method:<br \/>\n\u4e4b\u6240\u4ee5\u79f0\u4e3a\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\uff0c\u662f\u56e0\u4e3a\u5b83\u4e3a URL \u8def\u5f84\u5efa\u7acb\u4e86\u7ea6\u5b9a\u3002 <strong>\u7b2c\u4e00\u90e8\u5206<\/strong> \u4e3a\u63a7\u5236\u5668\u540d\u79f0\u521b\u5efa\u6620\u5c04\uff0c<strong>\u7b2c\u4e8c\u90e8\u5206<\/strong> \u4e3a\u64cd\u4f5c\u65b9\u6cd5\u521b\u5efa\u6620\u5c04\uff0c<strong>\u7b2c\u4e09\u90e8\u5206<\/strong> \u7528\u4e8e\u53ef\u9009\u53c2\u6570\u3002\u6211\u4eec\u53ef\u4ee5\u5728 <strong>Configure<\/strong> \u65b9\u6cd5\u7684 <strong>Startup<\/strong> \u7c7b\u4e2d\u914d\u7f6e\u8fd9\u79cd\u7c7b\u578b\u7684\u8def\u7531\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4002.jpg\" alt=\"alt\" \/><\/p>\n<p><strong><em>Attribute routing<\/em><\/strong> uses the attributes to map the routes directly to the action methods inside the controller. Usually, we place the base route above the controller class, as you can see in our Web API controller class. Similarly, for the specific action methods, we create their routes right above them.<br \/>\n<strong>\u5c5e\u6027\u8def\u7531<\/strong>\u4f7f\u7528\u5c5e\u6027\u5c06\u8def\u7531\u76f4\u63a5\u6620\u5c04\u5230\u63a7\u5236\u5668\u5185\u7684\u64cd\u4f5c\u65b9\u6cd5\u3002\u901a\u5e38\uff0c\u6211\u4eec\u5c06\u57fa\u672c\u8def\u7531\u653e\u5728\u63a7\u5236\u5668\u7c7b\u4e4b\u4e0a\uff0c\u5982 Web API \u63a7\u5236\u5668\u7c7b\u6240\u793a\u3002\u540c\u6837\uff0c\u5bf9\u4e8e\u7279\u5b9a\u7684\u64cd\u4f5c\u65b9\u6cd5\uff0c\u6211\u4eec\u5728\u5b83\u4eec\u4e0a\u65b9\u521b\u5efa\u5b83\u4eec\u7684\u8def\u7531\u3002<\/p>\n<p>While working with the Web API project, the ASP.NET Core team suggests that we shouldn\u2019t use Convention-based Routing, but Attribute routing instead.<br \/>\n\u5728\u4f7f\u7528 Web API \u9879\u76ee\u65f6\uff0cASP.NET Core \u56e2\u961f\u5efa\u8bae\u6211\u4eec\u4e0d\u5e94\u8be5\u4f7f\u7528\u57fa\u4e8e\u7ea6\u5b9a\u7684\u8def\u7531\uff0c\u800c\u5e94\u8be5\u4f7f\u7528\u5c5e\u6027\u8def\u7531\u3002<\/p>\n<p>Different actions can be executed on the resource with the same URI, but with different HTTP Methods. In the same manner for different actions, we can use the same HTTP Method, but different URIs. Let\u2019s explain this quickly.<br \/>\n\u53ef\u4ee5\u5bf9\u5177\u6709\u76f8\u540c URI \u4f46\u4f7f\u7528\u4e0d\u540c HTTP \u65b9\u6cd5\u7684\u8d44\u6e90\u6267\u884c\u4e0d\u540c\u7684\u64cd\u4f5c\u3002\u5bf9\u4e8e\u4e0d\u540c\u7684\u64cd\u4f5c\uff0c\u6211\u4eec\u53ef\u4ee5\u4ee5\u76f8\u540c\u7684\u65b9\u5f0f\u4f7f\u7528\u76f8\u540c\u7684HTTP\u65b9\u6cd5\uff0c\u4f46URI\u4e0d\u540c\u3002\u8ba9\u6211\u4eec\u5feb\u901f\u89e3\u91ca\u4e00\u4e0b\u3002<\/p>\n<p>For Get request, Post, or Delete, we use the same URI \/api\/companies but we use different HTTP Methods like GET, POST, or DELETE. But if we send a request for all companies or just one company, we are going to use the same GET method but different URIs (<strong>\/api\/companies<\/strong> for all companies and <strong>\/api\/companies\/{companyId}<\/strong> for a single company).<br \/>\n\u5bf9\u4e8e\u83b7\u53d6\u8bf7\u6c42\u3001\u53d1\u5e03\u6216\u5220\u9664\uff0c\u6211\u4eec\u4f7f\u7528\u76f8\u540c\u7684 URI \/api\/companies\uff0c\u4f46\u6211\u4eec\u4f7f\u7528\u4e0d\u540c\u7684 HTTP \u65b9\u6cd5\uff0c\u5982 GET\u3001POST \u6216 DELETE\u3002\u4f46\u662f\uff0c\u5982\u679c\u6211\u4eec\u4e3a\u6240\u6709\u516c\u53f8\u6216\u4ec5\u4e00\u5bb6\u516c\u53f8\u53d1\u9001\u8bf7\u6c42\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u76f8\u540c\u7684GET\u65b9\u6cd5\uff0c\u4f46\u4f7f\u7528\u4e0d\u540c\u7684URI\uff08\u5bf9\u4e8e\u6240\u6709\u516c\u53f8<strong>\/api\/companies<\/strong>\uff0c\u5bf9\u4e8e\u5355\u4e2a\u516c\u53f8\uff0c<strong>\/api\/companies\/{companyId}<\/strong>\uff09\u3002<\/p>\n<p>We are going to understand this even more once we start implementing different actions in our controller.<br \/>\n\u4e00\u65e6\u6211\u4eec\u5f00\u59cb\u5728\u63a7\u5236\u5668\u4e2d\u5b9e\u73b0\u4e0d\u540c\u7684\u64cd\u4f5c\uff0c\u6211\u4eec\u5c06\u8fdb\u4e00\u6b65\u4e86\u89e3\u8fd9\u4e00\u70b9\u3002<\/p>\n<h2>4.2 Naming Our Resources<\/h2>\n<p>The resource name in the URI should always be a noun and not an action. That means if we want to create a route to get all companies, we should create this route: <strong>api\/companies<\/strong> and not this one:<strong> \/api\/getCompanies<\/strong>.<br \/>\nURI \u4e2d\u7684\u8d44\u6e90\u540d\u79f0\u5e94\u59cb\u7ec8\u662f\u540d\u8bcd\uff0c\u800c\u4e0d\u662f\u64cd\u4f5c\u3002\u8fd9\u610f\u5473\u7740\uff0c\u5982\u679c\u6211\u4eec\u60f3\u521b\u5efa\u4e00\u6761\u8def\u7ebf\u6765\u83b7\u5f97\u6240\u6709\u516c\u53f8\uff0c\u6211\u4eec\u5e94\u8be5\u521b\u5efa\u6b64\u8def\u7531\uff1a<strong>api\/companies<\/strong>\uff0c\u800c\u4e0d\u662f\u6b64\u8def\u7531\uff1a<strong>\/api\/getCompanies<\/strong>.<\/p>\n<p>The noun used in URI represents the resource and helps the consumer to understand what type of resource we are working with. So, we shouldn\u2019t choose the noun products or orders when we work with the companies resource; the noun should always be companies. Therefore, by following this convention if our resource is employees (and we are going to work with this type of resource), the noun should be employees.<br \/>\nURI \u4e2d\u4f7f\u7528\u7684\u540d\u8bcd\u8868\u793a\u8d44\u6e90\uff0c\u5e76\u5e2e\u52a9\u4f7f\u7528\u8005\u4e86\u89e3\u6211\u4eec\u6b63\u5728\u4f7f\u7528\u7684\u8d44\u6e90\u7c7b\u578b\u3002\u56e0\u6b64\uff0c\u5f53\u6211\u4eec\u4e0e\u516c\u53f8\u8d44\u6e90\u5408\u4f5c\u65f6\uff0c\u6211\u4eec\u4e0d\u5e94\u8be5\u9009\u62e9\u540d\u8bcd\u4ea7\u54c1\u6216\u8ba2\u5355;\u540d\u8bcd\u5e94\u59cb\u7ec8\u662f\u516c\u53f8\u3002\u56e0\u6b64\uff0c\u5982\u679c\u6211\u4eec\u7684\u8d44\u6e90\u662f\u96c7\u5458\uff08\u6211\u4eec\u5c06\u4f7f\u7528\u8fd9\u79cd\u7c7b\u578b\u7684\u8d44\u6e90\uff09\uff0c\u5219\u9075\u5faa\u6b64\u7ea6\u5b9a\uff0c\u540d\u8bcd\u5e94\u8be5\u662f\u96c7\u5458\u3002<\/p>\n<p>Another important part we need to pay attention to is the hierarchy between our resources. In our example, we have a Company as a principal entity and an Employee as a dependent entity. When we create a route for a dependent entity, we should follow a slightly different convention: <strong>\/api\/principalResource\/{principalId}\/dependentResource<\/strong>.<br \/>\n\u6211\u4eec\u9700\u8981\u6ce8\u610f\u7684\u53e6\u4e00\u4e2a\u91cd\u8981\u90e8\u5206\u662f\u8d44\u6e90\u4e4b\u95f4\u7684\u5c42\u6b21\u7ed3\u6784\u3002\u5728\u6211\u4eec\u7684\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u5c06\u516c\u53f8\u4f5c\u4e3a\u4e3b\u5b9e\u4f53\uff0c\u5c06\u5458\u5de5\u4f5c\u4e3a\u4ece\u5c5e\u5b9e\u4f53\u3002\u5f53\u6211\u4eec\u4e3a\u4f9d\u8d56\u5b9e\u4f53\u521b\u5efa\u8def\u7531\u65f6\uff0c\u6211\u4eec\u5e94\u8be5\u9075\u5faa\u4e00\u4e2a\u7a0d\u5fae\u4e0d\u540c\u7684\u7ea6\u5b9a\uff1a<br \/>\n<strong>\/api\/principalResource\/{principalId}\/dependentResource<\/strong>.<\/p>\n<p>Because our employees can\u2019t exist without a company, the route for the employee's resource should be:<strong>\/api\/companies\/{companyId}\/employees.<\/strong><br \/>\n\u7531\u4e8e\u6211\u4eec\u7684\u5458\u5de5\u79bb\u4e0d\u5f00\u516c\u53f8\uff0c\u56e0\u6b64\u5458\u5de5\u8d44\u6e90\u7684\u9014\u5f84\u5e94\u8be5\u662f\uff1a<br \/>\n<strong>\/api\/companies\/{companyId}\/employees<\/strong>.<\/p>\n<p>With all of this in mind, we can start with the Get requests.<br \/>\n\u8003\u8651\u5230\u6240\u6709\u8fd9\u4e9b\uff0c\u6211\u4eec\u53ef\u4ee5\u4ece Get \u8bf7\u6c42\u5f00\u59cb\u3002<\/p>\n<h2>4.4 Getting All Companies From the Database<\/h2>\n<p>So let\u2019s start.<br \/>\n\u8ba9\u6211\u4eec\u5f00\u59cb\u5427\u3002<\/p>\n<p>The first thing we are going to do is to change the base route from <strong>[Route(&quot;api\/[controller]&quot;)]<\/strong> to <strong>[Route(&quot;api\/companies&quot;)]<\/strong>. Even though the first route will work just fine, with the second example we are more specific to show that this routing should point to the <strong>CompaniesController<\/strong> class.<br \/>\n\u6211\u4eec\u8981\u505a\u7684\u7b2c\u4e00\u4ef6\u4e8b\u662f\u6539\u53d8\u57fa\u672c\u8def\u7ebf\u4ece  <strong>[Route(&quot;api\/[controller]&quot;)]<\/strong> \u5230 <strong>[Route(&quot;api\/companies&quot;)]<\/strong> \u3002\u5c3d\u7ba1\u7b2c\u4e00\u4e2a\u8def\u7531\u53ef\u4ee5\u6b63\u5e38\u5de5\u4f5c\uff0c\u4f46\u5728\u7b2c\u4e8c\u4e2a\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u66f4\u5177\u4f53\u5730\u8868\u660e\u6b64\u8def\u7531\u5e94\u6307\u5411 <strong>CompaniesController<\/strong> \u7c7b\u3002<\/p>\n<p>Now it is time to create the first action method to return all the companies from the database. Let\u2019s create a definition for the <strong>GetAllCompanies<\/strong> method in the <strong>ICompanyRepository<\/strong> interface:<br \/>\n\u73b0\u5728\u662f\u65f6\u5019\u521b\u5efa\u7b2c\u4e00\u4e2a\u64cd\u4f5c\u65b9\u6cd5\u4ee5\u4ece\u6570\u636e\u5e93\u4e2d\u8fd4\u56de\u6240\u6709\u516c\u53f8\u4e86\u3002\u8ba9\u6211\u4eec\u5728 <strong>ICompanyRepository<\/strong> \u63a5\u53e3\u4e2d\u4e3a <strong>GetAllCompanies<\/strong> \u65b9\u6cd5\u521b\u5efa\u4e00\u4e2a\u5b9a\u4e49\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Entities.Models;\nusing System.Collections.Generic;\nnamespace Contracts\n{\n    public interface ICompanyRepository {\n        IEnumerable&lt;Company&gt; GetAllCompanies(bool trackChanges);\n    }\n}\n\n<\/pre>\n<p>For this to work, we need to add a reference from the <strong>Entities<\/strong> project to the <strong>Contracts<\/strong> project. But we are going to stop here for a moment to draw your attention to one important thing.<br \/>\n\u4e3a\u6b64\uff0c\u6211\u4eec\u9700\u8981\u5c06\u201c <strong>Entities<\/strong> \u201d\u9879\u76ee\u4e2d\u7684\u5f15\u7528\u6dfb\u52a0\u5230\u201c <strong>Contracts<\/strong> \u201d\u9879\u76ee\u3002\u4f46\u662f\uff0c\u6211\u4eec\u5c06\u5728\u8fd9\u91cc\u505c\u7559\u7247\u523b\uff0c\u63d0\u8bf7\u4f60\u4eec\u6ce8\u610f\u4e00\u4ef6\u91cd\u8981\u7684\u4e8b\u60c5\u3002<\/p>\n<p>In our main project, we are referencing the LoggerService, Repository, and Entities projects. Since both the LoggerService and Repository projects have a reference for the Contracts project (which has a reference to the Entities project; we just added it) this means that the main project has a reference for the Entities project as well through the LoggerService or Repository projects. That said, we can remove the Entities reference from the main project:<br \/>\n\u5728\u6211\u4eec\u7684\u4e3b\u9879\u76ee\u4e2d\uff0c\u6211\u4eec\u5f15\u7528\u4e86 LoggerService\u3001Repository \u548c Entities \u9879\u76ee\u3002\u7531\u4e8e LoggerService \u548c Repository \u9879\u76ee\u90fd\u6709\u5bf9 Contracts \u9879\u76ee\u7684\u5f15\u7528\uff08\u8be5\u9879\u76ee\u5177\u6709\u5bf9 Entities \u9879\u76ee\u7684\u5f15\u7528;\u6211\u4eec\u521a\u521a\u6dfb\u52a0\u4e86\u5b83\uff09\uff0c\u8fd9\u610f\u5473\u7740\u4e3b\u9879\u76ee\u4e5f\u901a\u8fc7 LoggerService \u6216 Repository \u9879\u76ee\u83b7\u5f97\u4e86\u5b9e\u4f53\u9879\u76ee\u7684\u5f15\u7528\u3002\u4e5f\u5c31\u662f\u8bf4\uff0c\u6211\u4eec\u53ef\u4ee5\u4ece\u4e3b\u9879\u76ee\u4e2d\u5220\u9664\u5b9e\u4f53\u5f15\u7528\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4003.jpg\" alt=\"alt\" \/><\/p>\n<p>Now, we can continue with the interface implementation in the <strong>CompanyRepository<\/strong> class:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u7ee7\u7eed\u5728<strong>CompanyRepository<\/strong>\u7c7b\u5b9e\u73b0\u63a5\u53e3\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Entities.Models;\nusing Entities;\nusing System.Collections.Generic;\nusing System.Linq;\nnamespace Repository\n{\n    public class CompanyRepository : RepositoryBase&lt;Company&gt;, ICompanyRepository\n    { \n        public CompanyRepository(RepositoryContext repositoryContext) : base(repositoryContext) { }\n        public IEnumerable&lt;Company&gt; GetAllCompanies(bool trackChanges) =&gt; \n            FindAll(trackChanges).OrderBy(c =&gt; c.Name).ToList();\n    }\n}\n<\/pre>\n<p>Finally, we have to return companies by using the <strong>GetAllCompanies<\/strong> method inside the Web API controller.<br \/>\n\u6700\u540e\uff0c\u6211\u4eec\u5fc5\u987b\u901a\u8fc7\u4f7f\u7528<strong>GetAllCompanies<\/strong>\u6765\u56de\u62a5\u516c\u53f8\u65b9\u6cd5\u5728 Web API \u63a7\u5236\u5668\u4e2d\u3002<\/p>\n<p>The purpose of the action methods inside the Web API controllers is not only to return results. It is the main purpose, but not the only one. We need to pay attention to the status codes of our Web API responses as well. Additionally, we are going to decorate our actions with the HTTP attributes which will mark the type of the HTTP request to that action.<br \/>\nWeb API \u63a7\u5236\u5668\u4e2d\u7684\u64cd\u4f5c\u65b9\u6cd5\u7684\u76ee\u7684\u4e0d\u4ec5\u4ec5\u662f\u8fd4\u56de\u7ed3\u679c\u3002\u8fd9\u662f\u4e3b\u8981\u76ee\u7684\uff0c\u4f46\u4e0d\u662f\u552f\u4e00\u7684\u76ee\u7684\u3002\u6211\u4eec\u8fd8\u9700\u8981\u6ce8\u610f Web API \u54cd\u5e94\u7684\u72b6\u6001\u4ee3\u7801\u3002\u6b64\u5916\uff0c\u6211\u4eec\u5c06\u4f7f\u7528HTTP\u5c5e\u6027\u6765\u4fee\u9970\u6211\u4eec\u7684\u64cd\u4f5c\uff0c\u8fd9\u4e9b\u5c5e\u6027\u5c06HTTP\u8bf7\u6c42\u7684\u7c7b\u578b\u6807\u8bb0\u4e3a\u8be5\u64cd\u4f5c\u3002<\/p>\n<p>So, let\u2019s modify the <strong>CompaniesController<\/strong> :<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u4fee\u6539 <strong>CompaniesController<\/strong> \u63a7\u5236\u5668\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Microsoft.AspNetCore.Mvc;\nusing System;\n\nnamespace CompanyEmployees.Controllers\n{\n    &#x5B;Route(&quot;api\/companies&quot;)]\n    &#x5B;ApiController]\n    public class CompaniesController : ControllerBase\n    {\n        private readonly IRepositoryManager _repository;\n        private readonly ILoggerManager _logger;\n        public CompaniesController(IRepositoryManager repository, ILoggerManager logger)\n        {\n            _repository = repository;\n            _logger = logger;\n        }\n\n        &#x5B;HttpGet]\n        public IActionResult GetCompanies()\n        {\n            try\n            {\n                var companies = _repository.Company.GetAllCompanies(trackChanges: false);\n                return Ok(companies);\n            }\n            catch (Exception ex) {\n                _logger.LogError($&quot;Something went wrong in the {nameof(GetCompanies)} action {ex}&quot;); \n                return StatusCode(500, &quot;Internal server error&quot;); }\n        }\n    }\n\n}\n\n<\/pre>\n<p>Let\u2019s explain this code a bit.<br \/>\n\u8ba9\u6211\u4eec\u7a0d\u5fae\u89e3\u91ca\u4e00\u4e0b\u8fd9\u6bb5\u4ee3\u7801\u3002<\/p>\n<p>First of all, we inject the logger and repository services inside the constructor. Then by decorating the <strong>GetCompanies<\/strong> action with the <strong>[HttpGet]<\/strong> attribute, we are mapping this action to the GET request. Then, we use both injected services to log the messages and to get the data from the repository class.<br \/>\n\u9996\u5148\uff0c\u6211\u4eec\u5c06\u8bb0\u5f55\u5668\u548c\u5b58\u50a8\u5e93\u670d\u52a1\u6ce8\u5165\u6784\u9020\u51fd\u6570\u4e2d\u3002\u7136\u540e\u901a\u8fc7\u88c5\u9970 <strong>GetCompanies<\/strong> \u64cd\u4f5c <strong>[HttpGet]<\/strong> \u5c5e\u6027\uff0c\u6211\u4eec\u5c06\u6b64\u64cd\u4f5c\u6620\u5c04\u5230 GET \u8bf7\u6c42\u3002\u7136\u540e\uff0c\u6211\u4eec\u4f7f\u7528\u4e24\u4e2a\u6ce8\u5165\u7684\u670d\u52a1\u6765\u8bb0\u5f55\u6d88\u606f\u5e76\u4ece\u5b58\u50a8\u5e93\u7c7b\u4e2d\u83b7\u53d6\u6570\u636e\u3002<\/p>\n<p>The <strong>IActionResult<\/strong> interface supports using a variety of methods, which return not only the result but also the status codes. In this situation, the <strong>OK<\/strong> method returns all the companies and also the status code 200 \u2014which stands for <strong>OK<\/strong>. If an exception occurs, we are going to return the internal server error with the status code 500.<br \/>\n<strong>IActionResult<\/strong> \u63a5\u53e3\u652f\u6301\u4f7f\u7528\u591a\u79cd\u65b9\u6cd5\uff0c\u8fd9\u4e9b\u65b9\u6cd5\u4e0d\u4ec5\u8fd4\u56de\u7ed3\u679c\uff0c\u8fd8\u8fd4\u56de\u72b6\u6001\u4ee3\u7801\u3002\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c<strong>OK<\/strong> \u65b9\u6cd5\u8fd4\u56de\u6240\u6709\u516c\u53f8\u4ee5\u53ca\u72b6\u6001\u4ee3\u7801 200 \u3002\u8fd9\u4ee3\u8868 <strong>OK<\/strong> \u3002\u5982\u679c\u53d1\u751f\u5f02\u5e38\uff0c\u6211\u4eec\u5c06\u8fd4\u56de\u72b6\u6001\u4ee3\u7801\u4e3a 500 \u7684\u5185\u90e8\u670d\u52a1\u5668\u9519\u8bef\u3002<\/p>\n<p>Because there is no route attribute right above the action, the route for the <strong>GetCompanies action<\/strong> will be <strong>api\/companies<\/strong> which is the route placed on top of our controller.<br \/>\n\u7531\u4e8e\u64cd\u4f5c\u6b63\u4e0a\u65b9\u6ca1\u6709\u8def\u7531\u5c5e\u6027\uff0c\u56e0\u6b64 GetCompanies \u64cd\u4f5c\u7684\u8def\u7531\u5c06\u662f a<strong>pi\/companies<\/strong>\uff0c\u8fd9\u662f\u653e\u7f6e\u5728\u63a7\u5236\u5668\u9876\u90e8\u7684\u8def\u7531\u3002<\/p>\n<h2>4.4 Testing the Result with Postman<\/h2>\n<p>To check the result, we are going to use a great tool named Postman, which helps a lot with sending requests and displaying responses. If you download our exercise files, you will find the file <strong>Bonus 2-CompanyEmployeesRequests.postman_collection.json<\/strong>, which contains a request collection divided for each chapter of this book. You can import them in Postman to save yourself the time of manually typing them:<br \/>\n\u4e3a\u4e86\u68c0\u67e5\u7ed3\u679c\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u4e00\u4e2a\u540d\u4e3aPostman\u7684\u51fa\u8272\u5de5\u5177\uff0c\u8be5\u5de5\u5177\u5bf9\u53d1\u9001\u8bf7\u6c42\u548c\u663e\u793a\u54cd\u5e94\u6709\u5f88\u5927\u5e2e\u52a9\u3002\u5982\u679c\u60a8\u4e0b\u8f7d\u6211\u4eec\u7684\u7ec3\u4e60\u6587\u4ef6\uff0c\u60a8\u4f1a\u53d1\u73b0 <strong>Bonus 2-CompanyEmployeesRequests.postman_collection.json<\/strong> \u6587\u4ef6\uff0c\u5176\u4e2d\u5305\u542b\u9488\u5bf9\u672c\u4e66\u6bcf\u4e2a\u7ae0\u8282\u5212\u5206\u7684\u8bf7\u6c42\u96c6\u5408\u3002\u60a8\u53ef\u4ee5\u5728Postman\u4e2d\u5bfc\u5165\u5b83\u4eec\uff0c\u4ee5\u8282\u7701\u624b\u52a8\u952e\u5165\u5b83\u4eec\u7684\u65f6\u95f4\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4004.jpg\" alt=\"alt\" \/><\/p>\n<p>Please note that some GUID values will be different for your project, so you have to change them according to your values.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u67d0\u4e9b GUID \u503c\u5bf9\u4e8e\u60a8\u7684\u9879\u76ee\u4f1a\u6709\u6240\u4e0d\u540c\uff0c\u56e0\u6b64\u60a8\u5fc5\u987b\u6839\u636e\u81ea\u5df1\u7684\u503c\u66f4\u6539\u5b83\u4eec\u3002<\/p>\n<p>So let\u2019s start the application by pressing the F5 button and check that it is now listening on the https:localhost:5001 address:<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u901a\u8fc7\u6309 F5 \u6309\u94ae\u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u68c0\u67e5\u5b83\u73b0\u5728\u662f\u5426\u6b63\u5728\u4fa6\u542c https\uff1alocalhost\uff1a5001 \u5730\u5740\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4005.jpg\" alt=\"alt\" \/><\/p>\n<p>If this is not the case, you probably ran it in the IIS mode; so turn the application off and start it again, but in the CompanyEmployees mode:<br \/>\n\u5982\u679c\u4e0d\u662f\u8fd9\u79cd\u60c5\u51b5\uff0c\u60a8\u53ef\u80fd\u5728IIS\u6a21\u5f0f\u4e0b\u8fd0\u884c\u5b83;\u56e0\u6b64\uff0c\u5173\u95ed\u5e94\u7528\u7a0b\u5e8f\u5e76\u91cd\u65b0\u542f\u52a8\u5b83\uff0c\u4f46\u5728\u516c\u53f8\u5458\u5de5\u6a21\u5f0f\u4e0b\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4006.jpg\" alt=\"alt\" \/><\/p>\n<p>Now, we can use Postman to test the result:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528Postman\u6765\u6d4b\u8bd5\u7ed3\u679c\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\"><a href=\"https:\/\/localhost:5001\/api\/companies\"><a href=\"https:\/\/localhost:5001\/api\/companies\">https:\/\/localhost:5001\/api\/companies<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4007.jpg\" alt=\"alt\" \/><\/p>\n<p>Excellent, everything is working as planned. But we are missing something. We are using the Company entity to map our requests to the database and then returning it as a result to the client, and this is not a good practice. So, in the next part, we are going to learn how to improve our code with DTO classes.<br \/>\n\u975e\u5e38\u597d\uff0c\u4e00\u5207\u90fd\u6309\u8ba1\u5212\u5de5\u4f5c\u3002\u4f46\u662f\u6211\u4eec\u9519\u8fc7\u4e86\u4e00\u4e9b\u4e1c\u897f\u3002\u6211\u4eec\u4f7f\u7528\u516c\u53f8\u5b9e\u4f53\u5c06\u6211\u4eec\u7684\u8bf7\u6c42\u6620\u5c04\u5230\u6570\u636e\u5e93\uff0c\u7136\u540e\u5c06\u5176\u4f5c\u4e3a\u7ed3\u679c\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef\uff0c\u8fd9\u4e0d\u662f\u4e00\u4e2a\u597d\u7684\u505a\u6cd5\u3002\u56e0\u6b64\uff0c\u5728\u4e0b\u4e00\u90e8\u5206\u4e2d\uff0c\u6211\u4eec\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528 DTO \u7c7b\u6539\u8fdb\u4ee3\u7801\u3002<\/p>\n<h2>4.5 DTO Classes vs. Entity Model Classes<\/h2>\n<p>Data transfer object (DTO) is an object that we use to transport data between the client and server applications.<br \/>\n\u6570\u636e\u4f20\u8f93\u5bf9\u8c61 \uff08DTO\uff09 \u662f\u6211\u4eec\u7528\u4e8e\u5728\u5ba2\u6237\u7aef\u548c\u670d\u52a1\u5668\u5e94\u7528\u7a0b\u5e8f\u4e4b\u95f4\u4f20\u8f93\u6570\u636e\u7684\u5bf9\u8c61\u3002<\/p>\n<p>So, as we said in a previous section of this book, it is not a good practice to return entities in the Web API response; we should instead use data transfer objects. But why is that?<br \/>\n\u56e0\u6b64\uff0c\u6b63\u5982\u6211\u4eec\u5728\u672c\u4e66\u7684\u4e0a\u4e00\u8282\u4e2d\u6240\u8bf4\uff0c\u5728Web API\u54cd\u5e94\u4e2d\u8fd4\u56de\u5b9e\u4f53\u4e0d\u662f\u4e00\u4e2a\u597d\u7684\u505a\u6cd5;\u6211\u4eec\u5e94\u8be5\u6539\u7528\u6570\u636e\u4f20\u8f93\u5bf9\u8c61\u3002\u4f46\u8fd9\u662f\u4e3a\u4ec0\u4e48\u5462\uff1f<\/p>\n<p>Well, EF Core uses model classes to map them to the tables in the database and that is the main purpose of a model class. But as we saw, our models have navigational properties and sometimes we don\u2019t want to map them in an API response. So, we can use DTO to remove any property or concatenate properties into a single property.<br \/>\n\u597d\u5427\uff0cEF Core \u4f7f\u7528\u6a21\u578b\u7c7b\u5c06\u5b83\u4eec\u6620\u5c04\u5230\u6570\u636e\u5e93\u4e2d\u7684\u8868\uff0c\u8fd9\u662f\u6a21\u578b\u7c7b\u7684\u4e3b\u8981\u7528\u9014\u3002\u4f46\u6b63\u5982\u6211\u4eec\u6240\u770b\u5230\u7684\uff0c\u6211\u4eec\u7684\u6a21\u578b\u5177\u6709\u5bfc\u822a\u5c5e\u6027\uff0c\u6709\u65f6\u6211\u4eec\u4e0d\u60f3\u5728 API \u54cd\u5e94\u4e2d\u6620\u5c04\u5b83\u4eec\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 DTO \u5220\u9664\u4efb\u4f55\u5c5e\u6027\u6216\u5c06\u5c5e\u6027\u8fde\u63a5\u6210\u5355\u4e2a\u5c5e\u6027\u3002<\/p>\n<p>Moreover, there are situations where we want to map all the properties from a model class to the result \u2014 but still, we want to use DTO instead. The reason is if we change the database, we also have to change the properties in a model \u2014 but that doesn\u2019t mean our clients want the result changed. So, by using DTO, the result will stay as it was before the model changes.<br \/>\n\u6b64\u5916\uff0c\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u6211\u4eec\u5e0c\u671b\u5c06\u6240\u6709\u5c5e\u6027\u4ece\u6a21\u578b\u7c7b\u6620\u5c04\u5230\u7ed3\u679c\uff0c \u4f46\u4ecd\u7136\u5e0c\u671b\u4f7f\u7528 DTO\u3002\u539f\u56e0\u662f\uff0c\u5982\u679c\u6211\u4eec\u66f4\u6539\u6570\u636e\u5e93\uff0c\u6211\u4eec\u8fd8\u5fc5\u987b\u66f4\u6539\u6a21\u578b\u4e2d\u7684\u5c5e\u6027 - \u4f46\u8fd9\u5e76\u4e0d\u610f\u5473\u7740\u6211\u4eec\u7684\u5ba2\u6237\u5e0c\u671b\u66f4\u6539\u7ed3\u679c\u3002\u56e0\u6b64\uff0c\u901a\u8fc7\u4f7f\u7528 DTO\uff0c\u7ed3\u679c\u5c06\u4fdd\u6301\u6a21\u578b\u66f4\u6539\u4e4b\u524d\u7684\u72b6\u6001\u3002<\/p>\n<p>As we can see, keeping these objects separate (the DTO and model classes) leads to a more robust and maintainable code in our application.<br \/>\n\u6b63\u5982\u6211\u4eec\u6240\u770b\u5230\u7684\uff0c\u5c06\u8fd9\u4e9b\u5bf9\u8c61\u5206\u5f00\uff08DTO\u548c\u6a21\u578b\u7c7b\uff09\u4f1a\u5bfc\u81f4\u6211\u4eec\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4ee3\u7801\u66f4\u52a0\u5065\u58ee\u548c\u53ef\u7ef4\u62a4\u3002<\/p>\n<p>Now, when we know why should we separate DTO from a model class in our code, let\u2019s create the folder <strong>DataTransferObjects<\/strong> in the <strong>Entities<\/strong> project with the <strong>CompanyDto<\/strong> class inside:<br \/>\n\u73b0\u5728\uff0c\u5f53\u6211\u4eec\u77e5\u9053\u4e3a\u4ec0\u4e48\u8981\u5728\u4ee3\u7801\u4e2d\u5c06 DTO \u4e0e\u6a21\u578b\u7c7b\u5206\u5f00\u65f6\uff0c\u8ba9\u6211\u4eec\u5728 <strong>Entities<\/strong> \u9879\u76ee\u4e2d\u521b\u5efa\u6587\u4ef6\u5939 <strong>DataTransferObjects<\/strong>\uff0c\u5176\u4e2d\u5305\u542b <strong>CompanyDto<\/strong> \u7c7b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing System;\nnamespace Entities.DataTransferObjects\n{\n    public class CompanyDto { \n        public Guid Id { get; set; } \n        public string Name { get; set; } \n        public string FullAddress { get; set; } \n    }\n}\n<\/pre>\n<p>We have removed the <strong>Employees<\/strong> property and we are going to use the <strong>FullAddress<\/strong> property to concatenate the <strong>Address<\/strong> and <strong>Country<\/strong> properties from the <strong>Company<\/strong> class. Furthermore, we are not using validation attributes in this class, because we are going to use this class only to return a response to the client. Therefore, validation attributes are not required.<br \/>\n\u6211\u4eec\u5220\u9664\u4e86\u201c <strong>Employees<\/strong> \u201d\u5c5e\u6027\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u201c <strong>FullAddress<\/strong> \u201d\u5c5e\u6027\u4ece\u201c <strong>Company<\/strong> \u201d\u7c7b\u4e2d\u8fde\u63a5\u201c <strong>Address<\/strong> \u548c\u201c <strong>Country<\/strong> \u201d\u5c5e\u6027\u3002\u6b64\u5916\uff0c\u6211\u4eec\u4e0d\u5728\u6b64\u7c7b\u4e2d\u4f7f\u7528\u9a8c\u8bc1\u5c5e\u6027\uff0c\u56e0\u4e3a\u6211\u4eec\u5c06\u4ec5\u4f7f\u7528\u6b64\u7c7b\u5411\u5ba2\u6237\u7aef\u8fd4\u56de\u54cd\u5e94\u3002\u56e0\u6b64\uff0c\u9a8c\u8bc1\u5c5e\u6027\u4e0d\u662f\u5fc5\u9700\u7684\u3002<\/p>\n<p>So, let\u2019s open and modify the <strong>GetCompanies<\/strong> action:<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u6253\u5f00\u5e76\u4fee\u6539 <strong>GetCompanies<\/strong> \u64cd\u4f5c\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Contracts;\nusing Entities.DataTransferObjects;\nusing Microsoft.AspNetCore.Mvc;\nusing System;\nusing System.Linq;\nnamespace CompanyEmployees.Controllers\n{\n    &#x5B;Route(&quot;api\/companies&quot;)]\n    &#x5B;ApiController]\n    public class CompaniesController : ControllerBase\n    {\n        private readonly IRepositoryManager _repository;\n        private readonly ILoggerManager _logger;\n        public CompaniesController(IRepositoryManager repository, ILoggerManager logger)\n        {\n            _repository = repository;\n            _logger = logger;\n        }\n\n        \/\/&#x5B;HttpGet]\n        \/\/public IActionResult GetCompanies()\n        \/\/{\n        \/\/    try\n        \/\/    {\n        \/\/        var companies = _repository.Company.GetAllCompanies(trackChanges: false);\n        \/\/        return Ok(companies);\n        \/\/    }\n        \/\/    catch (Exception ex) {\n        \/\/        _logger.LogError($&quot;Something went wrong in the {nameof(GetCompanies)} action {ex}&quot;); \n        \/\/        return StatusCode(500, &quot;Internal server error&quot;); }\n        \/\/}\n\n        &#x5B;HttpGet]\n        public IActionResult GetCompanies()\n        {\n            try\n            {\n                var companies = _repository.Company.GetAllCompanies(trackChanges: false);\n                var companiesDto = companies.Select(c =&gt; new CompanyDto\n                {\n                    Id = c.Id,\n                    Name = c.Name,\n                    FullAddress = string.Join(&#039; &#039;, c.Address, c.Country)\n                }).ToList();\n                return Ok(companiesDto);\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError($&quot;Something went wrong in the {nameof(GetCompanies)} action {ex}&quot;);\n                return StatusCode(500, &quot;Internal server error&quot;);\n            }\n        }\n    }\n}\n<\/pre>\n<\/p>\n<p>Let\u2019s start our application and test it with the same request from Postman:<br \/>\n\u8ba9\u6211\u4eec\u542f\u52a8\u6211\u4eec\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u4f7f\u7528Postman\u7684\u76f8\u540c\u8bf7\u6c42\u5bf9\u5176\u8fdb\u884c\u6d4b\u8bd5\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\"><a href=\"https:\/\/localhost:5001\/api\/companies\"><a href=\"https:\/\/localhost:5001\/api\/companies\">https:\/\/localhost:5001\/api\/companies<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4008.jpg\" alt=\"alt\" \/><\/p>\n<p>This time we get our CompanyDto result, which is a more preferred way. But this can be improved as well. If we take a look at our mapping code in the <strong>GetCompanies<\/strong> action, we can see that we manually map all the properties. Sure, it is okay for a few fields \u2014 but what if we have a lot more? There is a better and cleaner way to map our classes and that is by using the Automapper.<br \/>\n\u8fd9\u6b21\u6211\u4eec\u5f97\u5230\u4e86\u6211\u4eec\u7684\u516c\u53f8Dto\u7ed3\u679c\uff0c\u8fd9\u662f\u4e00\u79cd\u66f4\u53ef\u53d6\u7684\u65b9\u5f0f\u3002\u4f46\u8fd9\u4e5f\u53ef\u4ee5\u6539\u8fdb\u3002\u5982\u679c\u6211\u4eec\u770b\u4e00\u4e0b <strong>GetCompanies<\/strong> \u64cd\u4f5c\u4e2d\u7684\u6620\u5c04\u4ee3\u7801\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u6211\u4eec\u624b\u52a8\u6620\u5c04\u6240\u6709\u5c5e\u6027\u3002\u5f53\u7136\uff0c\u5bf9\u4e8e\u4e00\u4e9b\u9886\u57df\u6765\u8bf4\u6ca1\u5173\u7cfb - \u4f46\u662f\u5982\u679c\u6211\u4eec\u6709\u66f4\u591a\u7684\u9886\u57df\u5462\uff1f\u6709\u4e00\u79cd\u66f4\u597d\u3001\u66f4\u7b80\u6d01\u7684\u65b9\u6cd5\u6765\u6620\u5c04\u6211\u4eec\u7684\u7c7b\uff0c\u90a3\u5c31\u662f\u4f7f\u7528\u81ea\u52a8\u6620\u5c04\u5668\u3002<\/p>\n<p>4.6 Using AutoMapper in ASP.NET Core<br \/>\nAutoMapper is a library that helps us with mapping objects in our applications. By using this library, we are going to remove the code for manual mapping \u2014 thus making the action readable and maintainable.<br \/>\n\u81ea\u52a8\u6620\u5c04\u5668\u662f\u4e00\u4e2a\u5e93\uff0c\u53ef\u5e2e\u52a9\u6211\u4eec\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6620\u5c04\u5bf9\u8c61\u3002\u901a\u8fc7\u4f7f\u7528\u8fd9\u4e2a\u5e93\uff0c\u6211\u4eec\u5c06\u5220\u9664\u7528\u4e8e\u624b\u52a8\u6620\u5c04\u7684\u4ee3\u7801 \uff0c\u4ece\u800c\u4f7f\u64cd\u4f5c\u53ef\u8bfb\u4e14\u53ef\u7ef4\u62a4\u3002\u200c<\/p>\n<p>So, to install AutoMapper, let\u2019s open a Package Manager Console window and run the following command:<br \/>\n\u56e0\u6b64\uff0c\u8981\u5b89\u88c5\u81ea\u52a8\u6620\u5c04\u5668\uff0c\u8ba9\u6211\u4eec\u6253\u5f00\u7a0b\u5e8f\u5305\u7ba1\u7406\u5668\u63a7\u5236\u53f0\u7a97\u53e3\u5e76\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nPM&gt; Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection\n<\/pre>\n<p>After installation, we are going to register this library in the <strong>ConfigureServices<\/strong> method:<br \/>\n\u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u6211\u4eec\u5c06\u5728\u914d\u7f6e\u670d\u52a1\u65b9\u6cd5\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nservices.AddAutoMapper(typeof(Startup));\n<\/pre>\n<p>As soon as our library is registered, we are going to create a profile class where we specify the source and destination objects for mapping:<br \/>\n\u6ce8\u518c\u5e93\u540e\uff0c\u6211\u4eec\u5c06\u521b\u5efa\u4e00\u4e2a\u914d\u7f6e\u6587\u4ef6\u7c7b\uff0c\u5728\u5176\u4e2d\u6307\u5b9a\u7528\u4e8e\u6620\u5c04\u7684\u6e90\u5bf9\u8c61\u548c\u76ee\u6807\u5bf9\u8c61\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing AutoMapper;\nusing Entities.DataTransferObjects;\nusing Entities.Models;\n\nnamespace CompanyEmployees\n{\n    public class MappingProfile : Profile { \n        public MappingProfile() { \n            CreateMap&lt;Company, CompanyDto&gt;()\n                .ForMember(c =&gt; c.FullAddress, \n                opt =&gt; opt.MapFrom(x =&gt; string.Join(&#039; &#039;, x.Address, x.Country))); \n        } \n    }\n}\n<\/pre>\n<p>The <strong>MappingProfile<\/strong> class must inherit from the AutoMapper\u2019s <strong>Profile<\/strong> class. In the constructor, we are using the <strong>CreateMap<\/strong> method where we specify the source object and the destination object to map to. Because we have the <strong>FullAddress<\/strong> property in our DTO class, which contains both the <strong>Address<\/strong> and the <strong>Country<\/strong> from the model class, we have to specify additional mapping rules with the <strong>ForMember<\/strong> method.<br \/>\n<strong>MappingProfile<\/strong> \u7c7b\u5fc5\u987b\u4ece\u81ea\u52a8\u6620\u5c04\u5668\u7684 <strong>Profile<\/strong> \u7c7b\u7ee7\u627f\u3002\u5728\u6784\u9020\u51fd\u6570\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528 <strong>CreateMap<\/strong> \u65b9\u6cd5\uff0c\u5728\u5176\u4e2d\u6307\u5b9a\u8981\u6620\u5c04\u5230\u7684\u6e90\u5bf9\u8c61\u548c\u76ee\u6807\u5bf9\u8c61\u3002\u7531\u4e8e\u6211\u4eec\u5728 DTO \u7c7b\u4e2d\u5177\u6709 <strong>FullAddress<\/strong> \u5c5e\u6027\uff0c\u8be5\u5c5e\u6027\u540c\u65f6\u5305\u542b\u6a21\u578b\u7c7b\u4e2d\u7684<strong>Address<\/strong> and the <strong>Country<\/strong> \uff0c\u56e0\u6b64\u6211\u4eec\u5fc5\u987b\u4f7f\u7528 <strong>ForMember<\/strong> \u65b9\u6cd5\u6307\u5b9a\u5176\u4ed6\u6620\u5c04\u89c4\u5219\u3002<\/p>\n<p>Now, we can use AutoMapper in our controller like any other service registered in IoC:<br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u53ef\u4ee5\u5728\u63a7\u5236\u5668\u4e2d\u4f7f\u7528\u81ea\u52a8\u6620\u5c04\u5668\uff0c\u5c31\u50cf\u5728 IoC \u4e2d\u6ce8\u518c\u7684\u4efb\u4f55\u5176\u4ed6\u670d\u52a1\u4e00\u6837\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing AutoMapper;\nusing Contracts;\nusing Entities.DataTransferObjects;\nusing Microsoft.AspNetCore.Mvc;\nusing System;\nusing System.Collections.Generic;\n\nnamespace CompanyEmployees.Controllers\n{\n    &#x5B;Route(&quot;api\/companies&quot;)]\n    &#x5B;ApiController]\n    public class CompaniesController : ControllerBase\n    {\n        private readonly IRepositoryManager _repository; \n        private readonly ILoggerManager _logger; \n        private readonly IMapper _mapper; \n        public CompaniesController(IRepositoryManager repository, ILoggerManager logger, IMapper mapper) \n        { \n            _repository = repository; \n            _logger = logger; \n            _mapper = mapper;\n        }\n\n        &#x5B;HttpGet]\n        public IActionResult GetCompanies()\n        {\n            try\n            {\n                var companies = _repository.Company.GetAllCompanies(trackChanges: false);\n                var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies);\n                return Ok(companiesDto);\n            }\n            catch (Exception ex)\n            {\n                _logger.LogError($&quot;Something went wrong in the {nameof(GetCompanies)} action {ex}&quot;);\n                return StatusCode(500, &quot;Internal server error&quot;);\n            }\n        }\n    }\n}\n<\/pre>\n<p>Excellent.Let\u2019s use Postman again to send the request to test our app:<br \/>\n\u975e\u5e38\u597d\u3002\u8ba9\u6211\u4eec\u518d\u6b21\u4f7f\u7528Postman\u53d1\u9001\u8bf7\u6c42\u4ee5\u6d4b\u8bd5\u6211\u4eec\u7684\u5e94\u7528\u7a0b\u5e8f\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\"><a href=\"https:\/\/localhost:5001\/api\/companies\"><a href=\"https:\/\/localhost:5001\/api\/companies\">https:\/\/localhost:5001\/api\/companies<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_4009.jpg\" alt=\"alt\" \/><\/p>\n<p>\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u4e00\u5207\u90fd\u5728\u6309\u9884\u671f\u5de5\u4f5c\uff0c\u4f46\u73b0\u5728\u6709\u4e86\u66f4\u597d\u7684\u4ee3\u7801\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>4 HANDING GET REQUESTS We\u2019re all set to add some business logic to our application. But before that, let\u2019s talk a bit about controller classes and routing because they play an important part while working with HTTP requests. \u6211\u4eec\u90fd\u51c6\u5907\u5411\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u4e00\u4e9b\u4e1a\u52a1\u903b\u8f91\u3002\u4f46\u5728\u6b64\u4e4b\u524d\uff0c\u8ba9\u6211\u4eec\u6765\u8c08\u8c08\u63a7\u5236\u5668\u7c7b\u548c\u8def\u7531\uff0c\u56e0\u4e3a\u5b83\u4eec\u5728\u5904\u7406HTTP\u8bf7\u6c42\u65f6\u8d77\u7740\u91cd\u8981\u4f5c\u7528\u3002 4.1 Controllers and Routing in WEB API Controllers should only be responsible for handling requests, model [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[23],"class_list":["post-160","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-ultimate-asp-net-core-web-api"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/160","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=160"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/160\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=160"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=160"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=160"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}