{"id":10005,"date":"2017-04-02T09:47:17","date_gmt":"2017-04-02T16:47:17","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/?p=10005"},"modified":"2023-07-12T10:18:47","modified_gmt":"2023-07-12T17:18:47","slug":"how-to-create-your-own-templates-for-dotnet-new","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/how-to-create-your-own-templates-for-dotnet-new\/","title":{"rendered":"How to create your own templates for dotnet new"},"content":{"rendered":"<p>You can now create your own templates for <code>dotnet new<\/code>. Creating and installing your own templates is an experimental feature at this point, but one that we are seeing significant interest in and that deserves more of your feedback before we enable it for broad use with .NET Core 2.0. The version of <code>dotnet new<\/code> shipped with the .NET Core 1.0 SDK has a new command line parameter <code>install<\/code>. This is an undocumented feature, and is not currently included in the help output.<\/p>\n<p>You can try the new template experience if you have the new SDK or Visual Studio 2017 installed. If you didn&#8217;t you can right now.<\/p>\n<p>The goal of this post is to connect with developers who are interested in creating templates. If you maintain a library or framework project on GitHub, then you are a great candidate to be a template author. There are lots of other cases too, where creating templates makes sense. If you can create a sample, you can create a template. It&#8217;s not hard at all.<\/p>\n<p>In the last update for .NET Core, we have updated <code>dotnet new<\/code>.\nThis new version of <code>dotnet new<\/code> is now built on top of the new <a href=\"https:\/\/github.com\/dotnet\/templating\/\">Template Engine<\/a>, which\nis a library that we are developing. To learn more about how to use <code>dotnet new<\/code> see the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/articles\/core\/tools\/dotnet-new\">docs<\/a>.\nIn this article, we&#8217;ll show how to create some custom templates and then use them from <code>dotnet new<\/code>.<\/p>\n<p>Over the past several years we have seen a lot of interest in creating custom templates. We also heard that it&#8217;s too\ndifficult to create and maintain templates with the existing tools. Because of that we wanted to make it easy to create, maintain and share templates. Let&#8217;s dive into the demos, and see how to create some templates.\nEverything that we cover here is in a GitHub repository at https:\/\/github.com\/sayedihashimi\/dotnet-new-samples.<\/p>\n<p>I have a web project which I&#8217;d like to create a template out of. The template project can be found at <a href=\"https:\/\/github.com\/sayedihashimi\/dotnet-new-samples\/tree\/master\/OriginalSource\/Sayedha.StarterWeb\">Sayedha.StarterWeb<\/a>. This is a modified version of\nthe mvc template which is availables\nout of the box. Before you create a template out of this, let&#8217;s run the sample to see what was created. After running <code>dotnet restore<\/code>, and <code>dotnet run<\/code>, you can view the app at <code>http:\/\/localhost:5000<\/code> (or if running in Visual Studio it will launch automatically when you run the app).\nTHe following screenshot of this app running on my machine (I&#8217;m creating these samples on a Mac, but you can use any platform).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/07\/runapp01.png\" alt=\"app screenshot\" \/><\/p>\n<p>The app will look pretty familiar if you&#8217;ve created an app with this template in Visual Studio. There are also some strings that need to be replaced when\nyou create a template out of this. For example the namespace is set to <code>SayedHa.StarterWeb<\/code>. This should be updated to match the project name created. Now let&#8217;s create a template out of this and then you can start adding the replacements\nthat are needed.<\/p>\n<h2>How to create a basic template<\/h2>\n<p>To create a template from an existing project you will need to add a new file <code>.template.config\\template.json<\/code>. You should place the <code>.template.config<\/code> folder at the root\nof the files which should become the template. For example, in this case I&#8217;m going to add the <code>.template.config<\/code> directory in the <code>Sayedha.StarterWeb<\/code>\nfolder. This is the same folder that contains the <code>.csproj<\/code> project file. Let&#8217;s take a look at the content of the <code>template.json<\/code> file.<\/p>\n<pre><code class=\"language-json\">{\r\n  \"author\": \"Sayed Ibrahim Hashimi\",\r\n  \"classifications\": [ \"Web\" ], \r\n  \"name\": \"Sayed Starter Web\",\r\n  \"identity\": \"Sayedha.StarterWeb\",        \/\/ Unique name for this template\r\n  \"shortName\": \"sayedweb\",                 \/\/ Short name that can be used on the cli\r\n  \"tags\": {\r\n    \"language\": \"C#\"                       \/\/ Specify that this template is in C#.\r\n  },\r\n  \"sourceName\": \"Sayedha.StarterWeb\",      \/\/ Will replace the string 'Sayedha.StarterWeb' with the value provided via -n.\r\n  \"preferNameDirectory\" : \"true\"\r\n}<\/code><\/pre>\n<p>The contents of <code>template.json<\/code> shown above are all pretty straight forward. The <code>sourceName<\/code> field is an optional field that you should pay more attention to. I&#8217;ll tell you why.&#8221;\nWhen a user invokes <code>dotnet new<\/code> and specifies a new project name, by using <code>--name<\/code>, the project is created, and the string value for <code>sourceName<\/code>\nwill be replaced with the value provided for <code>--name<\/code>. In the <code>template.json<\/code> example above, <code>sourceName<\/code> is set to <code>Sayedha.StarterWeb<\/code>. This enables all instances of that string to be re-written by the user-provided value specified at the command line, with <code>--name<\/code>.\nThis string is also used to subsitute the namespace name in the <code>.cs<\/code> file for the project.\nWhen a new project is created with the template, these values will be updated. We will discuss <code>preferNameDirectory<\/code> later.\nLet&#8217;s try out our template now and see that in action.<\/p>\n<p>Now that we have created the template, it&#8217;s time to test it out. The first thing to do\nis to install the template. To do that, execute the command <code>dotnet new --install &lt;PATH&gt;<\/code> where <code>&lt;PATH&gt;<\/code> is the path to the folder containing\n<code>.template.config<\/code>. When that command is executed it will discover any template files under that path and then populate the\nlist of available templates. The output of running that command on my machine is below. In the output you can see\nthat the <code>sayedweb<\/code> template appears.<\/p>\n<pre><code class=\"language-shell\">$ dotnet new install \/Users\/sayedhashimi\/Documents\/mycode\/dotnet-new-samples\/01-basic-template\/SayedHa.StarterWeb\r\nTemplates                                 Short Name      Language      Tags\r\n--------------------------------------------------------------------------------------\r\nConsole Application                       console         [C#], F#      Common\/Console\r\nClass library                             classlib        [C#], F#      Common\/Library\r\nUnit Test Project                         mstest          [C#], F#      Test\/MSTest\r\nxUnit Test Project                        xunit           [C#], F#      Test\/xUnit\r\nSayed Starter Web                         sayedweb        [C#]          Web\r\nEmpty ASP.NET Core Web Application        web             [C#]          Web\/Empty\r\nMVC ASP.NET Core Web Application          mvc             [C#], F#      Web\/MVC\r\nWeb API ASP.NET Core Web Application      webapi          [C#]          Web\/WebAPI\r\nSolution File                             sln                           Solution\r\n\r\nExamples:\r\n    dotnet new mvc --auth None --framework netcoreapp1.0\r\n    dotnet new sln\r\n    dotnet new --help<\/code><\/pre>\n<p>Here you can see that the new template is included in the template list as expected.\nBefore moving on to create a new project using this template, there are a few important things to mention about this release.\nAfter running install, to reset your templates back to the default list you can run the command <code>dotnet new --debug:reinit<\/code>. We don&#8217;t\ncurrently have support for <code>uninstall<\/code>, but we are working on that. Now let&#8217;s move on to using this template.<\/p>\n<p>To create a new project you can run the following command.<\/p>\n<pre><code class=\"language-shell\">$ dotnet new sayedweb -n Contoso.Web -o Contoso.Web\r\nContent generation time: 150.1564 ms\r\nThe template \"Sayed Starter Web\" created successfully.<\/code><\/pre>\n<p>After executing this command, the project was created in a new folder named <code>Contoso.Web<\/code>. In addition, all the namespace elements in the .cs files\nhave been updated to be <code>namespace Contoso.Web<\/code> instead of <code>namespace SayedHa.StarterWeb<\/code>. If you recall from the previous screenshot there were\ntwo things that needed to be updated in the app: the title and the copyright. Let&#8217;s see how you can add these parameters to the template.<\/p>\n<h2>How to create a template with replaceable parameters<\/h2>\n<p>Now that you have created a basic template, let&#8217;s see how you can customize this a bit by adding parameters. There are two elements in the home page that\nshould be updated when the template is used.<\/p>\n<ul>\n<li>Title<\/li>\n<li>Copyright<\/li>\n<\/ul>\n<p>For each of these, you will create a parameter that can be customized by the user during project creation. To make these changes the only file\nthat you will need to modify is the <code>template.json<\/code> file. The following snippet contains the updated <code>template.json<\/code> file content (source files are located in the <a href=\"https:\/\/github.com\/sayedihashimi\/dotnet-new-samples\/tree\/master\/02-add-parameters\">02-add-parameters<\/a> folder).<\/p>\n<pre><code class=\"language-json\">{\r\n  \"author\": \"Sayed Ibrahim Hashimi\",\r\n  \"classifications\": [ \"Web\" ], \r\n  \"name\": \"Sayed Starter Web\",\r\n  \"identity\": \"SayedHa.StarterWeb\",         \r\n  \"shortName\": \"sayedweb\",\r\n  \"tags\": {\r\n    \"language\": \"C#\"\r\n  },\r\n  \"sourceName\": \"SayedHa.StarterWeb\",\r\n  \"symbols\":{\r\n    \"copyrightName\": {\r\n      \"type\": \"parameter\",\r\n      \"defaultValue\": \"John Smith\",\r\n      \"replaces\":\"Sayed Ibrahim Hashimi\"\r\n    },\r\n    \"title\": {\r\n      \"type\": \"parameter\",\r\n      \"defaultValue\": \"Hello Web\",\r\n      \"replaces\":\"Sayed Web\"\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<p>Here you have added a new element <code>symbols<\/code> with two child elements, one for each parameter. Let&#8217;s look at the <code>copyrightName<\/code> element a bit closer.\nWhen creating a parameter, the <code>type<\/code> value will be <code>parameter<\/code>. The <code>replaces<\/code> element defines the text which will be replaced. In this case <code>Sayed Ibrahim Hashimi<\/code> will be replaced. If the user doesn&#8217;t pass in a value when invoking this template, the <code>defaultValue<\/code> value will be applied\nto that. In this case, the default is <code>John Smith<\/code>.<\/p>\n<p>Now that you&#8217;ve added the two parameters you need, let&#8217;s test it with <code>dotnet new<\/code>. Since you changed the <code>template.json<\/code> file, you will need to re-invoke\n<code>dotnet new -i<\/code> again to update the template metadata. After installing the template again, let&#8217;s see what the help output looks like.\nAfter executing <code>dotnet new sayedweb -h<\/code>, in addition to the default help output you see the following.<\/p>\n<pre><code class=\"language-shell\">Sayed Starter Web (C#)\r\nAuthor: Sayed Ibrahim Hashimi\r\nOptions:\r\n  -c|--copyrightName\r\n                      string - Optional\r\n                      Default: John Smith\r\n\r\n  -t|--title\r\n                      string - Optional\r\n                      Default: Hello Web<\/code><\/pre>\n<p>Here you can see the two parameters which you defined in <code>template.json<\/code>. The following is an example of invoking this template and customizing these values.<\/p>\n<pre><code class=\"language-shell\">&gt; dotnet new sayedweb -n Contosog.Web -o Contoso.Web -c Contoso -t ContosoAdmin<\/code><\/pre>\n<p>This will result in the <code>_Layout.cshtml<\/code> file being updated. The <code>&lt;title&gt;<\/code> and <code>&lt;footer&gt;<\/code> elements are both updated.<\/p>\n<pre><code class=\"language-html\">&lt;title&gt;@ViewData[\"Title\"] - Contoso&lt;\/title&gt;<\/code><\/pre>\n<pre><code class=\"language-html\">&lt;footer&gt;\r\n     &lt;p&gt;&amp;copy; 2017 - Contoso&lt;\/p&gt;\r\n&lt;\/footer&gt;<\/code><\/pre>\n<p>Now that you&#8217;ve shown how to add a parameter which replaces some text content in the source project, let&#8217;s move to a more interesting example by adding optional content.<\/p>\n<h2>Add optional content<\/h2>\n<p>The existing template that you have created has a few pages, including a Contact page. Our next step is to make the Contact page an\noptional part of the template. The Contact page is integrated into the project in the following ways.<\/p>\n<ul>\n<li>Method in <code>Controllers\/HomeController.cs<\/code><\/li>\n<li>View in <code>Views\/Home\/Contact.cshtml<\/code><\/li>\n<li>Link in <code>Views\/Home\/Shared\/_Layout.cshtml<\/code><\/li>\n<\/ul>\n<p>Before you start modifying the sources the first thing you should do is to create a new parameter, <code>EnableContactPage<\/code>, in the <code>template.json<\/code> file.\nIn the following snippet you can see what needs to be added for this new parameter.<\/p>\n<pre><code class=\"language-json\">\"EnableContactPage\":{\r\n  \"type\": \"parameter\",\r\n  \"dataType\":\"bool\",\r\n  \"defaultValue\": \"false\"\r\n}<\/code><\/pre>\n<p>Here you used <code>\"dataType\":\"bool\"<\/code> to indicate that this parameter should support <code>true<\/code>\/<code>false<\/code> values. Now you will use the value of this parameter\nto determine if content will be added to the project. First let&#8217;s see how you can exclude <code>Contact.cshtml<\/code> when <code>EnableContactPage<\/code> is set to false.\nTo exclude a file from being processed during creation, you need to add a new element into the <code>template.json<\/code> file. The required content to add is.<\/p>\n<pre><code class=\"language-json\">\"sources\": [\r\n    {\r\n      \"modifiers\": [\r\n        {\r\n          \"condition\": \"(!EnableContactPage)\",\r\n          \"exclude\": [\r\n            \"Views\/Home\/Contact.cshtml\"\r\n          ]\r\n        }\r\n      ]\r\n    }\r\n  ]<\/code><\/pre>\n<p>Here you&#8217;ve added a modifier to the <code>sources<\/code> element which excludes the <code>Views\/Home\/Contact.cshtml<\/code> if <code>EnableContactPage<\/code> is <code>false<\/code>. The expression used in the condition here, <code>(EnableContentPage)<\/code>, is very basic but you can create more complex conditions using operators such as <code>&amp;&amp;<\/code>, <code>||<\/code>, <code>!<\/code>, <code>&lt;<\/code>, <code>&gt;=<\/code>, etc. For more info see https:\/\/aka.ms\/dotnetnew-template-config. Now let&#8217;s see how you can modify the controller and the layout page to conditionally omit the Contact specific content.<\/p>\n<p>Below is the modified version of <code>HomeController.cs<\/code> file that contains the condition for the Contact method.<\/p>\n<p>The following code block contains the contents of <code>HomeController.cs<\/code>. This shows how you can make the <code>Contact<\/code> method\nconditional based on the value of <code>EnableContactPage<\/code>.<\/p>\n<pre><code class=\"language-cs\">using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Microsoft.AspNetCore.Mvc;\r\n\r\nnamespace SayedHa.StarterWeb.Controllers\r\n{\r\n    public class HomeController : Controller\r\n    {\r\n        public IActionResult Index()\r\n        {\r\n            return View();\r\n        }\r\n\r\n        public IActionResult About()\r\n        {\r\n            ViewData[\"Message\"] = \"Your application description page.\";\r\n\r\n            return View();\r\n        }\r\n\r\n#if (EnableContactPage)\r\n        public IActionResult Contact()\r\n        {\r\n            ViewData[\"Message\"] = \"Your contact page.\";\r\n\r\n            return View();\r\n        }\r\n\r\n#endif\r\n        public IActionResult Error()\r\n        {\r\n            return View();\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<p>Here you use a C# <code>#if<\/code> preprocessor directive to define an optional section in the template. When editing template source files, the idea is that the\nfiles should be editable in a way that allows the files to still be &#8220;runnable&#8221;. For example, in this case, instead of modifying the C# file\nby adding elements which are invalid, the <code>#if<\/code>\/<code>#endif<\/code> directives are used for template regions. Because of this each file type has its\nown syntax for conditional regions. For more info on what syntax is used for each file type see https:\/\/aka.ms\/dotnetnew-template-config.<\/p>\n<p>When this template is processed, if <code>EnableContactPage<\/code> is true then the <code>Contact<\/code> controller will be present in the <code>HomeController.cs<\/code> file.\nOtherwise it will not be present. In addition to the Contact method in the Controller, there is a link in the <code>_Layout.cshtml<\/code> file which\nbe omitted if the Contact page is not created. The code fragment shows the definition of the navbar from the <code>_Layout.cshtml<\/code> file.<\/p>\n<pre><code class=\"language-html\">    &lt;div class=\"navbar-collapse collapse\"&gt;\r\n        &lt;ul class=\"nav navbar-nav\"&gt;\r\n            &lt;li&gt;&lt;a asp-area=\"\" asp-controller=\"Home\" asp-action=\"Index\"&gt;Home&lt;\/a&gt;&lt;\/li&gt;\r\n            &lt;li&gt;&lt;a asp-area=\"\" asp-controller=\"Home\" asp-action=\"About\"&gt;About&lt;\/a&gt;&lt;\/li&gt;\r\n@*#if (EnableContactPage)\r\n            &lt;li&gt;&lt;a asp-area=\"\" asp-controller=\"Home\" asp-action=\"Contact\"&gt;Contact&lt;\/a&gt;&lt;\/li&gt;\r\n#endif*@\r\n        &lt;\/ul&gt;\r\n    &lt;\/div&gt;<\/code><\/pre>\n<p>Here the tag helper element creating the Contact link is surrounded with a condition to check the value of <code>EnableContactPage<\/code>.\nSimilar to the controller, if <code>EnableContactPage<\/code> is <code>false<\/code> then this link (along with <code>#if<\/code>\/<code>#endif<\/code> lines) will not be present in the generated <code>_Layout.cshtml<\/code> file. The full config file is available at <a href=\"https:\/\/github.com\/sayedihashimi\/dotnet-new-samples\/blob\/master\/03-optional-page\/SayedHa.StarterWeb\/.template.config\/template.json\"><code>template.config<\/code><\/a>.\nLet&#8217;s move on to the next example, giving the user a set of choices.<\/p>\n<h2>Add a choice from a list of options<\/h2>\n<p>In the project the background color is set in the <code>site.css<\/code>, and <code>site.min.css<\/code>, to <code>skyblue<\/code>. You now want to create a new parameter for the template\nand give the user a choice of different background colors to choose from. To do this you will create a new template parameter, and define the available\nchoices in the <code>template.json<\/code> file. The parameter name that you are going to create is <code>BackgroundColor<\/code>. Here is the snippet to create this new parameter.<\/p>\n<pre><code class=\"language-json\">\"BackgroundColor\":{\r\n  \"type\":\"parameter\",\r\n  \"datatype\": \"choice\",\r\n  \"defaultValue\":\"aliceblue\",\r\n  \"choices\": [\r\n    {\r\n      \"choice\": \"aliceblue\",\r\n      \"description\": \"Alice Blue\"\r\n    },\r\n    {\r\n      \"choice\": \"dimgray\",\r\n      \"description\":\"dimgray\"\r\n    },\r\n    {\r\n      \"choice\":\"skyblue\",\r\n      \"description\":\"skyblue\"\r\n    }\r\n  ],\r\n  \"replaces\":\"skyblue\"\r\n}<\/code><\/pre>\n<p>Here we define the name of the parameter, the available choices, the default value (aliceblue), and the string that it replaces &#8216;skyblue&#8217;.<\/p>\n<h2>How to create projects with the name matching the directory<\/h2>\n<p>Earlier we saw a property in the <code>template.json<\/code> file, <code>preferNameDirectory<\/code>, which we skipped over. This flag helps simplify\ncreating projects where the name of the project matches the folder name. Most project templates should have this parameter set to true.<\/p>\n<p>For example, earlier you created a project with the command <code>dotnet new sayedweb -n Contoso.Web -o Contoso.Web<\/code>. This created a new project named\n<code>Contoso.Web<\/code> in a folder with the same name. This can be simplified by adding <code>\"preferNameDirectory\":\"true\"<\/code> in the <code>template.json<\/code> file. When a project is created using a template that has this set to true the project name will match the directory name (assuming that the <code>--name<\/code>\nparameter is not passed in). With this approach, instead of calling <code>dotnet new<\/code> with both <code>-n<\/code> and <code>-o<\/code> it can be simplified to the commands shown in the following code block.<\/p>\n<pre><code class=\"language-shell\">&gt; mkdir Contoso.Web\r\n&gt; cd Contoso.Web\r\n&gt; dotnet new sayedweb<\/code><\/pre>\n<p>When the project is created the name of the folder, <code>Contoso.Web<\/code> will be used as the project name, and it will be generated into the\ncurrent directory.<\/p>\n<h2>Closing<\/h2>\n<p>In this post, we have shown how you can get started with creating your own custom templates for <code>dotnet new<\/code>. We are still working on enabling the end user scenarios where templates are acquired and used.\nIn this release, the <code>--install<\/code> switch is hidden because it&#8217;s currently in preview. The syntax of this command is likely to change. After installing templates, to reset your templates back to the default list you can run the command <code>dotnet new --debug:reinit<\/code>\nIn the following section, you&#8217;ll find some links to existing resources.\nPlease share your comments here and file <a href=\"https:\/\/github.com\/dotnet\/templating\">issues<\/a> as needed. We&#8217;re very excited to see the\nawesome templates that the community creates.\nIn addition to this blog, we may post <code>dotnet new<\/code> related posts to the <a href=\"https:\/\/blogs.msdn.microsoft.com\/webdev\/\">.NET Web Developer Blog<\/a>.<\/p>\n<h2>Resources<\/h2>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/templating\">Template Engine repository<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/templating\/wiki\/Available-templates-for-dotnet-new\">Templates available for <code>dotnet new<\/code><\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/templating\/wiki\">wiki<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/templating\/wiki\/%22Runnable-Project%22-Templates\"><code>template.json<\/code> reference<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>You can now create your own templates for `dotnet new`, and this doc walks you through the process of creating one.<\/p>\n","protected":false},"author":357,"featured_media":46523,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[7416,7752],"class_list":["post-10005","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-templates","tag-templating"],"acf":[],"blog_post_summary":"<p>You can now create your own templates for `dotnet new`, and this doc walks you through the process of creating one.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/10005","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\/357"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=10005"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/10005\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/46523"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=10005"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=10005"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=10005"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}