{"id":36917,"date":"2018-02-27T15:30:07","date_gmt":"2018-02-27T23:30:07","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/webdev\/?p=12135"},"modified":"2018-02-27T15:30:07","modified_gmt":"2018-02-27T23:30:07","slug":"asp-net-core-2-1-web-apis","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-2-1-web-apis\/","title":{"rendered":"ASP.NET Core 2.1.0-preview1: Improvements for building Web APIs"},"content":{"rendered":"<p>ASP.NET Core 2.1 adds a number of features that make it easier and more convenient to build Web APIs. These features include Web API controller specific conventions, more robust input processing and error handling, and JSON patch improvements.<\/p>\n<p>Please note that some of these features require enabling MVC compatibility with 2.1, so be sure to check out the post on <a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/introducing-compatibility-version-in-mvc\/\">MVC compatibility versions<\/a> as well.<\/p>\n<h3 id=\"-apicontroller-and-actionresult-t-\">[ApiController] and ActionResult<\/h3>\n<p>ASP.NET Core 2.1 introduces new Web API controller specific conventions that make Web API development more convenient. These conventions can be applied to a controller using the new <code>[ApiController]<\/code> attribute:<\/p>\n<ul>\n<li>Automatically respond with a 400 when validation errors occur &#8211; no need to check the model state in your action method<\/li>\n<li>Infer smarter defaults for action parameters: <code>[FromBody]<\/code> for complex types, <code>[FromRoute]<\/code> when possible, otherwise <code>[FromQuery]<\/code><\/li>\n<li>Require attribute routing &#8211; actions are not accessible by convention-based routes<\/li>\n<\/ul>\n<p>You can also now return <code>ActionResult&lt;T&gt;<\/code> from your Web API actions, which allows you to return arbitrary action results or a specific return type (thanks to some clever use of implicit cast operators). Most Web API action methods have a specific return type, but also need to be able to return multiple different action results.<\/p>\n<p>Here&#8217;s an example Web API controller that uses these new enhancements:<\/p>\n<pre><code class=\"lang-c#\">[&lt;span class=\"hljs-name\"&gt;Route&lt;\/span&gt;(&lt;span class=\"hljs-string\"&gt;\"api\/[controller]\"&lt;\/span&gt;)]\n[&lt;span class=\"hljs-name\"&gt;ApiController&lt;\/span&gt;]\npublic class ProductsController : ControllerBase\n{\n    private readonly ProductsRepository _repository&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n\n    public ProductsController(&lt;span class=\"hljs-name\"&gt;ProductsRepository&lt;\/span&gt; repository)\n    {\n        _repository = repository&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n    }\n\n    [&lt;span class=\"hljs-name\"&gt;HttpGet&lt;\/span&gt;]\n    public IEnumerable&lt;Product&gt; Get()\n    {\n        return _repository.GetProducts()&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n    }\n\n    [&lt;span class=\"hljs-name\"&gt;HttpGet&lt;\/span&gt;(&lt;span class=\"hljs-string\"&gt;\"{id}\"&lt;\/span&gt;)]\n    public ActionResult&lt;Product&gt; Get(&lt;span class=\"hljs-name\"&gt;int&lt;\/span&gt; id)\n    {\n        if (&lt;span class=\"hljs-name\"&gt;!_repository.TryGetProduct&lt;\/span&gt;(&lt;span class=\"hljs-name\"&gt;id&lt;\/span&gt;, out var product))\n        {\n            return NotFound()&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n        }\n        return product&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n    }\n\n    [&lt;span class=\"hljs-name\"&gt;HttpPost&lt;\/span&gt;]\n    [&lt;span class=\"hljs-name\"&gt;ProducesResponseType&lt;\/span&gt;(&lt;span class=\"hljs-name\"&gt;201&lt;\/span&gt;)]\n    public ActionResult&lt;Product&gt; Post(&lt;span class=\"hljs-name\"&gt;Product&lt;\/span&gt; product)\n    {\n        _repository.AddProduct(&lt;span class=\"hljs-name\"&gt;product&lt;\/span&gt;)&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n        return CreatedAtAction(&lt;span class=\"hljs-name\"&gt;nameof&lt;\/span&gt;(&lt;span class=\"hljs-name\"&gt;Get&lt;\/span&gt;), new { id = product.Id }, product)&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n    }\n}\n<\/code><\/pre>\n<p>Because these conventions are more descriptive tools like <a href=\"https:\/\/github.com\/domaindrivendev\/swashbuckle\">Swashbuckle<\/a> or <a href=\"https:\/\/github.com\/RSuter\/NSwag\">NSwag<\/a> can do a better job generating an <a href=\"https:\/\/github.com\/OAI\/OpenAPI-Specification\">OpenAPI specification<\/a> for this Web API that includes information like return types, parameter sources, and possible error responses without needing addition attributes.<\/p>\n<h3 id=\"better-input-processing\">Better input processing<\/h3>\n<p>ASP.NET Core 2.1 does a much better job of providing appropriate error information when the request body fails to deserialize or the JSON is invalid.<\/p>\n<p>For example, in ASP.NET Core 2.0 if your Web API received a request with a JSON property that had the wrong type (like a string instead of an int) you get a generic error message, like this:<\/p>\n<pre><code class=\"lang-json\">{\n  &lt;span class=\"hljs-attr\"&gt;\"count\"&lt;\/span&gt;: [\n    &lt;span class=\"hljs-string\"&gt;\"The input was not valid.\"&lt;\/span&gt;\n  ]\n}\n<\/code><\/pre>\n<p>In 2.1 we provide more detailed error information about what was wrong with the request including path and line number information:<\/p>\n<pre><code class=\"lang-json\">{\n  &lt;span class=\"hljs-attr\"&gt;\"count\"&lt;\/span&gt;: [\n    &lt;span class=\"hljs-string\"&gt;\"Could not convert string to integer: abc. Path 'count', line 1, position 16.\"&lt;\/span&gt;\n  ]\n}\n<\/code><\/pre>\n<p>Similarly, if the request is syntactically invalid (ex. missing a curly brace) then 2.1 will let you know:<\/p>\n<pre><code class=\"lang-json\">{\n  &lt;span class=\"hljs-attr\"&gt;\"\"&lt;\/span&gt;: [\n    &lt;span class=\"hljs-string\"&gt;\"Unexpected end when reading JSON. Path '', line 1, position 1.\"&lt;\/span&gt;\n  ]\n}\n<\/code><\/pre>\n<p>You can also now add validation attributes to top level parameters of your action method. For example, you can mark a query string parameter as required like this:<\/p>\n<pre><code class=\"lang-c#\">[&lt;span class=\"hljs-name\"&gt;HttpGet&lt;\/span&gt;(&lt;span class=\"hljs-string\"&gt;\"test\/{testId}\"&lt;\/span&gt;)]\npublic ActionResult&lt;TestResult&gt; Get(&lt;span class=\"hljs-name\"&gt;&lt;span class=\"hljs-builtin-name\"&gt;string&lt;\/span&gt;&lt;\/span&gt; testId, [&lt;span class=\"hljs-name\"&gt;Required&lt;\/span&gt;]string name)\n<\/code><\/pre>\n<h3 id=\"problem-details\">Problem Details<\/h3>\n<p>In this release we added support for <a href=\"https:\/\/tools.ietf.org\/html\/rfc7807\">RFC 7807 &#8211; Problem Details for HTTP APIs<\/a> as a standardized format for returning machine readable error responses from HTTP APIs.<\/p>\n<p>To update your Web API controllers to return Problem Details responses for invalid requests you can add the following code to your <code>ConfigureServices<\/code> method:<\/p>\n<pre><code class=\"lang-c#\">services.Configure&lt;ApiBehaviorOptions&gt;(options =&gt;\n{\n    options.InvalidModelStateResponseFactory = &lt;span class=\"hljs-built_in\"&gt;context&lt;\/span&gt; =&gt;\n    {\n        var problemDetails = new ValidationProblemDetails(&lt;span class=\"hljs-built_in\"&gt;context&lt;\/span&gt;.ModelState)\n        {\n            &lt;span class=\"hljs-keyword\"&gt;Instance &lt;\/span&gt;= &lt;span class=\"hljs-built_in\"&gt;context&lt;\/span&gt;.HttpContext.Request.Path,\n            Status = StatusCodes.Status&lt;span class=\"hljs-number\"&gt;400B&lt;\/span&gt;adRequest,\n            Type = &lt;span class=\"hljs-string\"&gt;\"https:\/\/asp.net\/core\"&lt;\/span&gt;,\n            Detail = &lt;span class=\"hljs-string\"&gt;\"Please refer to the errors property for additional details.\"&lt;\/span&gt;\n        }&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n        return new &lt;span class=\"hljs-keyword\"&gt;BadRequestObjectResult(problemDetails)\n&lt;\/span&gt;        {\n            ContentTypes = { &lt;span class=\"hljs-string\"&gt;\"application\/problem+json\"&lt;\/span&gt;, &lt;span class=\"hljs-string\"&gt;\"application\/problem+xml\"&lt;\/span&gt; }\n        }&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n    }&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n})&lt;span class=\"hljs-comment\"&gt;;&lt;\/span&gt;\n<\/code><\/pre>\n<p>You can also return a Problem Details response from your API action for an invalid request using the <code>ValidationProblem()<\/code> helper method.<\/p>\n<p>An example Problem Details response for an invalid request looks like this (where the content type is <code>application\/problem+json<\/code>):<\/p>\n<pre><code class=\"lang-json\">{\n  &lt;span class=\"hljs-attr\"&gt;\"errors\"&lt;\/span&gt;: {\n    &lt;span class=\"hljs-attr\"&gt;\"Text\"&lt;\/span&gt;: [\n      &lt;span class=\"hljs-string\"&gt;\"The Text field is required.\"&lt;\/span&gt;\n    ]\n  },\n  &lt;span class=\"hljs-attr\"&gt;\"type\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"https:\/\/asp.net\/core\"&lt;\/span&gt;,\n  &lt;span class=\"hljs-attr\"&gt;\"title\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"One or more validation errors occurred.\"&lt;\/span&gt;,\n  &lt;span class=\"hljs-attr\"&gt;\"status\"&lt;\/span&gt;: &lt;span class=\"hljs-number\"&gt;400&lt;\/span&gt;,\n  &lt;span class=\"hljs-attr\"&gt;\"detail\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Please refer to the errors property for additional details.\"&lt;\/span&gt;,\n  &lt;span class=\"hljs-attr\"&gt;\"instance\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"\/api\/values\"&lt;\/span&gt;\n}\n<\/code><\/pre>\n<h3 id=\"json-patch-improvements\">JSON Patch improvements<\/h3>\n<p>JSON Patch defines a JSON document structure for implementing HTTP PATCH semantics. A JSON Patch document defines a sequence of operations (add, remove, replace, copy, etc.) that can be applied to a JSON resource.<\/p>\n<p>ASP.NET Core has supported JSON Patch since it first shipped, but in 2.1 we&#8217;ve added support for the test operation. The test operation allows to check for specific values before applying the patch. If any test operations fail then the whole patch fails.<\/p>\n<p>A Web API controller action that supports JSON Patch looks like this:<\/p>\n<pre><code class=\"lang-c#\">[HttpPatch(&lt;span class=\"hljs-string\"&gt;\"{id}\"&lt;\/span&gt;)]\n&lt;span class=\"hljs-function\"&gt;&lt;span class=\"hljs-keyword\"&gt;public&lt;\/span&gt; ActionResult&lt;Value&gt; &lt;span class=\"hljs-title\"&gt;Patch&lt;\/span&gt;(&lt;span class=\"hljs-params\"&gt;&lt;span class=\"hljs-keyword\"&gt;int&lt;\/span&gt; id, JsonPatchDocument&lt;Value&gt; patch&lt;\/span&gt;)\n&lt;\/span&gt;{\n    &lt;span class=\"hljs-keyword\"&gt;var&lt;\/span&gt; &lt;span class=\"hljs-keyword\"&gt;value&lt;\/span&gt; = &lt;span class=\"hljs-keyword\"&gt;new&lt;\/span&gt; Value { ID = id, Text = &lt;span class=\"hljs-string\"&gt;\"Do\"&lt;\/span&gt; };\n\n    patch.ApplyTo(&lt;span class=\"hljs-keyword\"&gt;value&lt;\/span&gt;, ModelState);\n\n    &lt;span class=\"hljs-keyword\"&gt;if&lt;\/span&gt; (!ModelState.IsValid)\n    {\n        &lt;span class=\"hljs-keyword\"&gt;return&lt;\/span&gt; BadRequest(ModelState);\n    }\n\n    &lt;span class=\"hljs-keyword\"&gt;return&lt;\/span&gt; &lt;span class=\"hljs-keyword\"&gt;value&lt;\/span&gt;;\n}\n<\/code><\/pre>\n<p>Where the <code>Value<\/code> type is defined as follows:<\/p>\n<pre><code class=\"lang-c#\">&lt;span class=\"hljs-keyword\"&gt;public&lt;\/span&gt; &lt;span class=\"hljs-keyword\"&gt;class&lt;\/span&gt; &lt;span class=\"hljs-title\"&gt;Value&lt;\/span&gt;\n{\n    &lt;span class=\"hljs-keyword\"&gt;public&lt;\/span&gt; &lt;span class=\"hljs-keyword\"&gt;int&lt;\/span&gt; ID { &lt;span class=\"hljs-keyword\"&gt;get&lt;\/span&gt;; &lt;span class=\"hljs-keyword\"&gt;set&lt;\/span&gt;; }\n\n    &lt;span class=\"hljs-keyword\"&gt;public&lt;\/span&gt; &lt;span class=\"hljs-keyword\"&gt;string&lt;\/span&gt; Text { &lt;span class=\"hljs-keyword\"&gt;get&lt;\/span&gt;; &lt;span class=\"hljs-keyword\"&gt;set&lt;\/span&gt;; }\n\n    &lt;span class=\"hljs-keyword\"&gt;public&lt;\/span&gt; IDictionary&lt;&lt;span class=\"hljs-keyword\"&gt;int&lt;\/span&gt;, &lt;span class=\"hljs-keyword\"&gt;string&lt;\/span&gt;&gt; Status { &lt;span class=\"hljs-keyword\"&gt;get&lt;\/span&gt;; } = &lt;span class=\"hljs-keyword\"&gt;new&lt;\/span&gt; Dictionary&lt;&lt;span class=\"hljs-keyword\"&gt;int&lt;\/span&gt;, &lt;span class=\"hljs-keyword\"&gt;string&lt;\/span&gt;&gt;();\n}\n<\/code><\/pre>\n<p>The following JSON Patch request successfully adds a value to the <code>Status<\/code> dictionary (note that we&#8217;ve also added support for non-string dictionary keys, like <code>int<\/code>, <code>Guid<\/code>, etc.):<\/p>\n<p><strong>Successful request<\/strong><\/p>\n<pre><code class=\"lang-json\">[\n  { &lt;span class=\"hljs-attr\"&gt;\"op\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"test\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"path\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"\/text\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"value\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Do\"&lt;\/span&gt; },\n  { &lt;span class=\"hljs-attr\"&gt;\"op\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"add\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"path\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"\/status\/1\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"value\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Done!\"&lt;\/span&gt; }\n]\n<\/code><\/pre>\n<p><strong>Successful response<\/strong><\/p>\n<pre><code class=\"lang-json\">{\n  &lt;span class=\"hljs-attr\"&gt;\"id\"&lt;\/span&gt;: &lt;span class=\"hljs-number\"&gt;123&lt;\/span&gt;,\n  &lt;span class=\"hljs-attr\"&gt;\"text\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Do\"&lt;\/span&gt;,\n  &lt;span class=\"hljs-attr\"&gt;\"status\"&lt;\/span&gt;: {\n    &lt;span class=\"hljs-attr\"&gt;\"1\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Done!\"&lt;\/span&gt;\n  }\n}\n<\/code><\/pre>\n<p>Conversely the following JSON Patch request fails because the value of the <code>text<\/code> property doesn&#8217;t match:<\/p>\n<p><strong>Failed request<\/strong><\/p>\n<pre><code class=\"lang-json\">[\n  { &lt;span class=\"hljs-attr\"&gt;\"op\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"test\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"path\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"\/text\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"value\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Do not\"&lt;\/span&gt; },\n  { &lt;span class=\"hljs-attr\"&gt;\"op\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"add\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"path\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"\/status\/1\"&lt;\/span&gt;, &lt;span class=\"hljs-attr\"&gt;\"value\"&lt;\/span&gt;: &lt;span class=\"hljs-string\"&gt;\"Done!\"&lt;\/span&gt; }\n]\n<\/code><\/pre>\n<p><strong>Failed response<\/strong><\/p>\n<pre><code class=\"lang-json\">{\n  \"Value\": [\n    \"The current value '&lt;span class=\"hljs-keyword\"&gt;Do&lt;\/span&gt;' &lt;span class=\"hljs-built_in\"&gt;at&lt;\/span&gt; &lt;span class=\"hljs-built_in\"&gt;path&lt;\/span&gt; 'text' is &lt;span class=\"hljs-keyword\"&gt;not&lt;\/span&gt; equal to the test value '&lt;span class=\"hljs-keyword\"&gt;Do&lt;\/span&gt; &lt;span class=\"hljs-keyword\"&gt;not&lt;\/span&gt;'.\"\n  ]\n}\n<\/code><\/pre>\n<h3 id=\"summary\">Summary<\/h3>\n<p>We hope you enjoy these Web API improvements. Please give them a try and let us know what you think. If you hit any issues or have feedback please file issues on <a href=\"https:\/\/github.com\/aspnet\/home\/issues\">GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ASP.NET Core 2.1 adds a number of features that make it easier and more convenient to build Web APIs. These features include Web API controller specific conventions, more robust input processing and error handling, and JSON patch improvements. Please note that some of these features require enabling MVC compatibility with 2.1, so be sure to [&hellip;]<\/p>\n","protected":false},"author":417,"featured_media":21411,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197,7509],"tags":[7542,7544],"class_list":["post-36917","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","category-aspnetcore","tag-asp-net-core-2-1-0-preview1","tag-web-apis"],"acf":[],"blog_post_summary":"<p>ASP.NET Core 2.1 adds a number of features that make it easier and more convenient to build Web APIs. These features include Web API controller specific conventions, more robust input processing and error handling, and JSON patch improvements. Please note that some of these features require enabling MVC compatibility with 2.1, so be sure to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/36917","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/417"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=36917"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/36917\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/21411"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=36917"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=36917"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=36917"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}