├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── activities ├── 01-InstallTooling.md ├── 02-CreateEmptyWebProject.md ├── 03-CreateControllerAndAction.md ├── 04-ReturningJsonData.md ├── 05-BindingFromQueryString.md ├── 06-BindingFromRouteParameters.md ├── 07-AcceptingPostedData.md ├── 08-Configuration.md ├── 09-Logging.md ├── 10-InitialDockerfile.md ├── 11-ProductionDockerfile.md ├── 12-DockerCompose.md ├── 13-UnitTests.md └── 14-IntegrationTests.md ├── images ├── 0-EditSampleApiProject.png ├── 1-PostmanRequest.png ├── 2-LoggingOutput.png ├── 3-AddTestProject.png ├── 4-TestProjectReferenceViaUi.png ├── 5-TestProjectMoq.png ├── 6-EditTestProject.png └── 7-AppSettingsJson.png ├── resources └── .dockerignore └── steps ├── 02-Create-Solution-And-Project ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ └── Startup.cs ├── 03-Create-Controller-And-Action ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Controllers │ └── SampleController.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ └── Startup.cs ├── 04-Returning-JSON-Data ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Controllers │ └── SampleController.cs │ ├── OutputModels │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ └── Startup.cs ├── 05-Binding-Data-From-Query-String ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Controllers │ └── SampleController.cs │ ├── OutputModels │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ └── Startup.cs ├── 06-Binding-From-Route-Parameters ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Controllers │ └── SampleController.cs │ ├── OutputModels │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ └── Startup.cs ├── 07-Accepting-Posted-Data ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ └── Startup.cs ├── 08-Configuration ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Configuration │ └── MessagesConfiguration.cs │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── 09-Logging ├── SampleApi.sln ├── global.json └── src │ └── SampleApi │ ├── Configuration │ └── MessagesConfiguration.cs │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── 10-Initial-Dockerfile ├── .dockerignore ├── SampleApi.sln ├── dockerfile ├── global.json └── src │ └── SampleApi │ ├── Configuration │ └── MessagesConfiguration.cs │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── 11-Production-Dockerfile ├── .dockerignore ├── SampleApi.sln ├── dockerfile ├── global.json └── src │ └── SampleApi │ ├── Configuration │ └── MessagesConfiguration.cs │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── 12-Docker-Compose ├── .dockerignore ├── SampleApi.sln ├── docker-compose.yml ├── dockerfile ├── global.json └── src │ └── SampleApi │ ├── Configuration │ └── MessagesConfiguration.cs │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── 13-Unit-Tests ├── .dockerignore ├── SampleApi.sln ├── docker-compose.yml ├── dockerfile ├── global.json ├── src │ └── SampleApi │ │ ├── Configuration │ │ └── MessagesConfiguration.cs │ │ ├── Controllers │ │ └── SampleController.cs │ │ ├── InputModels │ │ └── CalculateInputModel.cs │ │ ├── OutputModels │ │ ├── CalculateOutputModel.cs │ │ └── HelloOutputModel.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── SampleApi.csproj │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json └── test │ └── SampleApi.Test │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.Test.csproj │ └── SampleControllerTests.cs ├── 14-Integration-Tests ├── .dockerignore ├── SampleApi.sln ├── docker-compose.yml ├── dockerfile ├── global.json ├── src │ └── SampleApi │ │ ├── Configuration │ │ └── MessagesConfiguration.cs │ │ ├── Controllers │ │ └── SampleController.cs │ │ ├── InputModels │ │ └── CalculateInputModel.cs │ │ ├── OutputModels │ │ ├── CalculateOutputModel.cs │ │ └── HelloOutputModel.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── SampleApi.csproj │ │ ├── Startup.cs │ │ ├── appsettings.development.json │ │ └── appsettings.json └── test │ ├── SampleApi.IntegrationTest │ ├── Properties │ │ └── launchSettings.json │ ├── SampleApi.IntegrationTest.csproj │ ├── SampleControllerIntegrationTests.cs │ └── xunit.runner.json │ └── SampleApi.Test │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.Test.csproj │ └── SampleControllerTests.cs └── 15-Testing-In-Docker ├── .dockerignore ├── SampleApi.sln ├── docker-compose.yml ├── dockerfile ├── global.json ├── src └── SampleApi │ ├── Configuration │ └── MessagesConfiguration.cs │ ├── Controllers │ └── SampleController.cs │ ├── InputModels │ └── CalculateInputModel.cs │ ├── OutputModels │ ├── CalculateOutputModel.cs │ └── HelloOutputModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── SampleApi.csproj │ ├── Startup.cs │ ├── appsettings.development.json │ └── appsettings.json └── test ├── SampleApi.IntegrationTest ├── Properties │ └── launchSettings.json ├── SampleApi.IntegrationTest.csproj ├── SampleControllerIntegrationTests.cs └── xunit.runner.json └── SampleApi.Test ├── Properties └── launchSettings.json ├── SampleApi.Test.csproj └── SampleControllerTests.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Steve J Gordon (https://www.stevejgordon.co.uk) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core and Docker Workshop 2 | 3 | ## Pre-requisite Setup 4 | 5 | 1. Clone this repository locally 6 | 1. Install Postman for Windows - [Download Postman](https://www.getpostman.com/apps) 7 | 8 | ## What You'll Be Building 9 | 10 | In this workshop you will build a simple ASP.NET Core API from scratch, initially using the .NET SDK CLI. The finished API will have two endpoints, one returning data via HTTP GET and the other accepting data via HTTP POST. It will make use of ASP.NET Core configuration. 11 | 12 | You'll then create a Docker image for your API and run it as a Docker container. 13 | 14 | ## Activities 15 | 16 | | Activity | Topics | 17 | | ----- | ---- | 18 | | [Activity #1](/activities/01-InstallTooling.md) | Install the .NET Core SDK and Docker tooling | 19 | | [Activity #2](/activities/02-CreateEmptyWebProject.md) | Create an empty web project via the command line | 20 | | [Activity #3](/activities/03-CreateControllerAndAction.md) | Create a controller and action | 21 | | [Activity #4](/activities/04-ReturningJsonData.md) | Returning JSON Data | 22 | | [Activity #5](/activities/05-BindingFromQueryString.md) | Binding data from the query string | 23 | | [Activity #6](/activities/06-BindingFromRouteParameters.md) | Binding data from route parameters | 24 | | [Activity #7](/activities/07-AcceptingPostedData.md) | Accepting posted data | 25 | | [Activity #8](/activities/08-Configuration.md) | Application configuration | 26 | | [Activity #9](/activities/09-Logging.md) | Working with logging | 27 | | [Activity #10](/activities/10-InitialDockerfile.md) | Initial Dockerfile | 28 | | [Activity #11](/activities/11-ProductionDockerfile.md) | Production Dockerfile | 29 | | [Activity #12](/activities/12-DockerCompose.md) | Working with Docker Compose | 30 | | [Activity #13](/activities/13-UnitTests.md) | Adding some unit tests | 31 | | [Activity #14](/activities/14-IntegrationTests.md) | Adding an integration test | -------------------------------------------------------------------------------- /activities/01-InstallTooling.md: -------------------------------------------------------------------------------- 1 | # Install .NET SDK 2 | 3 | ## What's the plan? 4 | 5 | We're going make sure the correct version of the SDK for .NET Core is installed and if not, install it. 6 | 7 | ## Activity Steps 8 | 9 | 1. Verify the .NET SDK version installed on your PC. Open a command prompt and run the following command... 10 | 11 | ```dotnet --version``` 12 | 13 | The version should be **2.1.403** or higher. 14 | 15 | 2. If you require a newer version, you can download and install it from [.NET SDK for Visual Studio](https://www.microsoft.com/net/download/visual-studio-sdks) 16 | 17 | 3. You can view additional information with the following command... 18 | 19 | ```dotnet --info``` 20 | 21 | This command lists all of the SDK and runtime versions installed on your PC. 22 | 23 | ## End of Activity 24 | 25 | Continue to [Activity 2](02-CreateEmptyWebProject.md). 26 | 27 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/02-CreateEmptyWebProject.md: -------------------------------------------------------------------------------- 1 | # Creating an Empty Web Project 2 | 3 | For this activity, please ensure you have completed [Activity 1](01-InstallTooling.md) to install the required tooling. 4 | 5 | ## What's the plan? 6 | 7 | We're going to use the command line and dotnet CLI to create the structure for our API project and solution. 8 | 9 | We could use the Visual Studio UI and tooling for this also, but let's see how easily the cross platform CLI supports working with projects and templates. 10 | 11 | ## Activity Steps 12 | 13 | 1. Open a command prompt from your preferred project directory e.g. 'C:\Projects\'. 14 | 2. Create a 'SampleApi' folder. 15 | 16 | ```md SampleApi``` 17 | 18 | 3. Change to the 'SampleApi' folder. 19 | 20 | ```cd SampleApi``` 21 | 22 | 4. Pin the SDK version for these activies to 2.1.403 23 | 24 | ```dotnet new globaljson --sdk-version 2.1.403``` 25 | 26 | 5. Use the dotnet CLI to create an empty web project called SampleApi under a directory called 'src'. 27 | 28 | ```dotnet new web --name SampleApi --no-https -o src/SampleApi``` 29 | 30 | 6. Use the dotnet CLI to create a new empty solution 31 | 32 | ```dotnet new sln``` 33 | 34 | 7. Add the SampleApi project to the solution. 35 | 36 | ```dotnet sln add .\src\SampleApi\SampleApi.csproj``` 37 | 38 | 8. Use the dotnet CLI to build the solution. 39 | 40 | ```dotnet build``` 41 | 42 | 9. Use the dotnet CLI to run the application 43 | 44 | ```dotnet run --project src/SampleApi/SampleApi.csproj``` 45 | 46 | *NOTE: By default the application will be hosted on port 5000. If this conflicts with anything you can override the port using the switch --urls "http://localhost:PORT"* 47 | 48 | 10. Open a browser to test the application by navigating to http://localhost:5000 49 | 50 | To finish, we'll clean up some items added by the default web project template which we do not need. This is not absolutely required but keeps everything clean. 51 | 52 | 11. Explore to the 'src/SampleApi' directory from Windows Explorer. 53 | 12. Open the src/SampleApi/SampleApi.csproj file in the text editor of your choosing. 54 | 55 | 13. Remove the entire ItemGroup which specified the folder include for the wwwroot folder. 56 | 14. Remove the package reference to 'Microsoft.AspNetCore.Razor.Design' which is not needed for an API project. 57 | 58 | The csproj file for SampleApi should look like this once you have finished... 59 | 60 | ```xml 61 | 62 | 63 | 64 | netcoreapp2.1 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ``` 73 | 74 | 15. In Windows Explorer, delete the 'wwwroot' folder. This is not needed for an API project since we will not serve any static files. 75 | 76 | ## End of Activity 77 | 78 | By this point you should be able to double click the solution file to open the SampleApi project in Visual Studio 2017. 79 | 80 | The completed example for this activity can be found in the '/steps/02-Create-Solution-And-Project' folder. 81 | 82 | Continue to [Activity 3](03-CreateControllerAndAction.md). 83 | 84 | [Return to README and activity links](../README.md) 85 | -------------------------------------------------------------------------------- /activities/03-CreateControllerAndAction.md: -------------------------------------------------------------------------------- 1 | # Creating a Controller and Action 2 | 3 | For this activity, please ensure you have completed [Activity 2](02-CreateEmptyWebProject.md). 4 | 5 | ## What's the plan? 6 | 7 | We're going to create our first ASP.NET Core MVC controller and an action to handle requests to the root path for the API. 8 | 9 | ## Activity Steps 10 | 11 | 1. Open your SampleApi.sln in Visual Studio 2017. 12 | 1. Create a new folder call 'Controllers' under the SampleApi project. 13 | 1. Create a class called 'SampleController' in the Controllers folder. 14 | 1. Update the class to inherit from the ControllerBase abstract class. 15 | 16 | ``` csharp 17 | public class SampleController : ControllerBase 18 | ``` 19 | 20 | *NOTE: This will require the a using statement for 'Microsoft.AspNetCore.Mvc'.* 21 | 22 | 5. Add the [ApiController] attribute to the class. 23 | 24 | ``` csharp 25 | [ApiController] 26 | public class SampleController : ControllerBase 27 | ``` 28 | 29 | 6. Create a method as follows... 30 | 31 | ``` csharp 32 | [Route("")] 33 | public string Hello() 34 | { 35 | return "Hello"; 36 | } 37 | ``` 38 | 39 | 7. Open the Startup class. 40 | 8. Add the following line to the ConfigureServices method to register the MVC services with DI. 41 | 42 | ```csharp 43 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 44 | ``` 45 | 46 | *NOTE: A using statement will be required for 'Microsoft.AspNetCore.Mvc'.* 47 | 48 | 9. Replace the app.Run call in the 'Configure' method with the following... 49 | 50 | ```csharp 51 | app.UseMvc(); 52 | ``` 53 | 54 | 7. Run the application by pressing F5. 55 | 56 | ## End of Activity 57 | 58 | The completed example for this activity can be found in the '/steps/03-Create-Controller-And-Action' folder. 59 | 60 | Continue to [Activity 4](04-ReturningJsonData.md). 61 | 62 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/04-ReturningJsonData.md: -------------------------------------------------------------------------------- 1 | # Returning JSON Data 2 | 3 | For this activity, please ensure you have completed [Activity 3](03-CreateControllerAndAction.md) and have your solution open in Visual Studio 2017. 4 | 5 | ## What's the plan? 6 | 7 | We're going to update our action to return a JSON result. 8 | 9 | ## Activity Steps 10 | 11 | 1. Create a new folder call 'OutputModels' inside the SampleApi project. 12 | 2. Create a class called 'HelloOutputModel' in the OutputModels folder. 13 | 3. Add the following Property declaration to the class... 14 | 15 | ``` csharp 16 | public string WelcomeMessage { get; set; } = "Hello"; 17 | ``` 18 | 19 | 4. Update the 'Hello' method in the 'SampleController' so that the method returns `ActionResult` 20 | 21 | ``` csharp 22 | public ActionResult Hello() 23 | ``` 24 | 25 | 5. Update the 'Hello' method to instantiate an instance of the HelloOutputModel before the return statement. 26 | 27 | ``` csharp 28 | var model = new HelloOutputModel(); 29 | ``` 30 | *NOTE: You'll need to include a using statement for 'SampleApi.OutputModels'* 31 | 32 | 6. Update the return statement to return the object. 33 | 34 | ``` csharp 35 | return Ok(model); 36 | ``` 37 | 38 | 7. Run the application by pressing F5. 39 | 40 | ## End of Activity 41 | 42 | By the end of this activity your completed action method should now look like this... 43 | ``` csharp 44 | [Route("")] 45 | public ActionResult Hello() 46 | { 47 | var model = new HelloOutputModel(); 48 | return Ok(model); 49 | } 50 | ``` 51 | 52 | The completed example for this activity can be found in the '/steps/04-Returning-JSON-Data' folder. 53 | 54 | Continue to [Activity 5](05-BindingFromQueryString.md). 55 | 56 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/05-BindingFromQueryString.md: -------------------------------------------------------------------------------- 1 | # Binding Data from the QueryString 2 | 3 | For this activity, please ensure you have completed [Activity 4](04-ReturningJsonData.md) and have your solution open in Visual Studio 2017. 4 | 5 | ## What's the plan? 6 | 7 | We're going to explore binding data from the query string. 8 | 9 | ## Activity Steps 10 | 11 | 1. Update the method signature of the Hello method to accept a string argument called 'name'. 12 | 13 | ``` csharp 14 | public ActionResult Hello(string name) 15 | ``` 16 | 17 | 2. After the line which creates the HelloOutputModel, before the return statement, add the following line to update the WelcomeMessage on the model... 18 | 19 | ``` csharp 20 | model.WelcomeMessage = $"{model.WelcomeMessage} {name ?? "whoever you are!"}"; 21 | ``` 22 | 23 | 3. Run the application by pressing F5. 24 | 4. Append a query string parameter called 'name' with any value you want to the URL in your address bar. 25 | 26 | > ?name=Steve 27 | 28 | 5. Press Enter to navigate to the updated URL. 29 | 30 | ## End of Activity 31 | 32 | By the end of this activity your completed action method should now look like this... 33 | ``` csharp 34 | [Route("")] 35 | public ActionResult Hello(string name) 36 | { 37 | var model = new HelloOutputModel(); 38 | model.WelcomeMessage = $"{model.WelcomeMessage} {name ?? "whoever you are!"}"; 39 | return Ok(model); 40 | } 41 | ``` 42 | 43 | The completed example for this activity can be found in the '/steps/05-Binding-Data-From-Query-String' folder. 44 | 45 | Continue to [Activity 6](06-BindingFromRouteParameters.md). 46 | 47 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/06-BindingFromRouteParameters.md: -------------------------------------------------------------------------------- 1 | # Binding Data from the Route Parameters 2 | 3 | For this activity, please ensure you have completed [Activity 5](05-BindingFromQueryString.md) and have your solution open in Visual Studio 2017. 4 | 5 | ## What's the plan? 6 | 7 | We're going to explore binding data from route parameters. 8 | 9 | ## Activity Steps 10 | 11 | 1. Update the route attribute to include an optional route parameter. 12 | 13 | ``` csharp 14 | [Route("{name?}")] 15 | ``` 16 | 17 | 2. Run the application by pressing F5. 18 | 3. Add a suffix to the path of the URL to pass your name which will be bound to the action argument. e.g. '/Steve'. 19 | 4. Press Enter to navigate to the updated URL. 20 | 21 | ## End of Activity 22 | 23 | The completed example for this activity can be found in the '/steps/06-Binding-From-Route-Parameters' folder. 24 | 25 | Continue to [Activity 7](07-AcceptingPostedData.md). 26 | 27 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/07-AcceptingPostedData.md: -------------------------------------------------------------------------------- 1 | # Accepting Posted Data 2 | 3 | For this activity, please ensure you have completed [Activity 6](06-BindingFromRouteParameters.md) and have your solution open in Visual Studio 2017. 4 | 5 | ## What's the plan? 6 | 7 | We're going to add an action which will accept and handle POSTed data to a new endpoint on our API. 8 | 9 | ## Activity Steps 10 | 11 | 1. Create a new class called 'CalculateOutputModel' in the 'OutputModels' folder. 12 | 13 | ``` csharp 14 | public class CalculateOutputModel 15 | { 16 | public int Result { get; set; } 17 | } 18 | ``` 19 | 20 | 2. Create a new folder called 'InputModels' 21 | 3. Create a new class called 'CalculateInputModel' in the 'InputModels' folder. 22 | 4. Add to properties as follows... 23 | 24 | ``` csharp 25 | public int Number1 { get; set; } 26 | public int Number2 { get; set; } 27 | ``` 28 | 29 | 4. Add a new method to the SampleController called 'Calculate'... 30 | 31 | ``` csharp 32 | [HttpPost] 33 | [Route("calculate")] 34 | public ActionResult Calculate(CalculateInputModel input) 35 | { 36 | var result = new CalculateOutputModel { Result = input.Number1 + input.Number2 }; 37 | return Ok(result); 38 | } 39 | ``` 40 | *NOTE: You will require a using statement for 'SampleApi.InputModels'.* 41 | 42 | 5. Run the application by pressing F5. 43 | 44 | 6. Copy the URL from the address bar. 45 | 46 | 7. Open Postman (or your application of choice) and create a POST request to your copied URL, adding a path of '/Calculate'. 47 | 8. Add the following body to the POST request... 48 | 49 | ``` javascript 50 | { 51 | "number1": 10, 52 | "number2": 20 53 | } 54 | ``` 55 | 56 | *NOTE: If using postman, your UI should be similar to the following, although your port will likely differ.* 57 | 58 | ![Postman request](../images/1-PostmanRequest.png "Postman request") 59 | 60 | 9. Send the request and check the response. 61 | 10. Change your request body to include invalid data such as a string value... 62 | 63 | ``` javascript 64 | { 65 | "number1": 10, 66 | "number2": 'this is invalid' 67 | } 68 | ``` 69 | 70 | 11. Send the request and check the response. 71 | 72 | ## End of Activity 73 | 74 | The completed example for this activity can be found in the '/steps/07-Accepting-Posted-Data' folder. 75 | 76 | Continue to [Activity 8](08-Configuration.md). 77 | 78 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/08-Configuration.md: -------------------------------------------------------------------------------- 1 | 2 | # Configuration 3 | 4 | For this activity, please ensure you have completed [Activity 7](07-AcceptingPostedData.md) and have your solution open in Visual Studio 2017. 5 | 6 | ## What's the plan? 7 | 8 | We're going to add the concept of configuration to our API. We'll configure the application using an appsettings.json file. We'll use the Options pattern in our SampleController to retrieve the configured value which we'll use to control the output from an endpoint in our API. 9 | 10 | ## Activity Steps 11 | 12 | 1. Create a JSON file called 'appsettings.json' at the root of the project. 13 | 14 | ![AppSettings.json](../images/7-AppSettingsJson.png "AppSettings.json") 15 | 16 | 2. Add a 'Messages' section to the configuration file. Include a setting called 'WelcomeMessage' with a value of 'Hey'. Once complete, the contents of your appsettings.json file should be as follows... 17 | 18 | ``` javascript 19 | { 20 | "Messages": { 21 | "WelcomeMessage": "Hey" 22 | } 23 | } 24 | ``` 25 | 26 | 3. Create a new folder called 'Configuration' 27 | 4. Create a new class called 'MessagesConfiguration' in the 'Configuration' folder with a single `string` property called 'WelcomeMessage'. 28 | 29 | ``` csharp 30 | public class MessagesConfiguration 31 | { 32 | public string WelcomeMessage { get; set; } 33 | } 34 | ``` 35 | 36 | 5. At the top of the Startup class, add a public constructor accepting an IConfiguration object. Store this into a public read-only property. 37 | 38 | ``` csharp 39 | public IConfiguration Configuration { get; } 40 | 41 | public Startup(IConfiguration configuration) 42 | { 43 | Configuration = configuration; 44 | } 45 | ``` 46 | 47 | 6. In the ConfigureServices method of the Startup class; add a line to bind the configuration to our typed configuration class. Call the `Configure` on the `IServiceCollection` passing the type for the 'MessagesConfiguration' into the generic parameter. This method accepts an `IConfig` object which we get by calling `GetSection` on the `IConfiguration` property, passing the section name to read and bind. Once complete the ConfigureServices method should be as follows... 48 | 49 | ``` csharp 50 | public void ConfigureServices(IServiceCollection services) 51 | { 52 | services.Configure(Configuration.GetSection("Messages")); 53 | 54 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 55 | } 56 | ``` 57 | 58 | *NOTE: This will require a using statement for 'SampleApi.Configuration'.* 59 | 60 | 7. Add a constructor to the SampleController accepting an `IOptions` and store the object in a private read-only field. 61 | 62 | *NOTE: This will require a using statement for 'SampleApi.Configuration' and 'Microsoft.Extensions.Options'.* 63 | 64 | ``` csharp 65 | private readonly IOptions _messagesConfig; 66 | 67 | public SampleController(IOptions messagesConfig) 68 | { 69 | _messagesConfig = messagesConfig; 70 | } 71 | ``` 72 | 73 | 8. Update the 'Hello' method to use the welcome message from the configuration. 74 | 75 | ``` csharp 76 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 77 | ``` 78 | 79 | 9. Run the application by pressing F5. 80 | 10. Create a JSON file called 'appsettings.development.json' at the root of the project. 81 | 11. Copy in the 'Messages' section from appsettings.json' into the new configuration file. 82 | 12. Update the value of the 'WelcomeMessage' setting to 'Hey from dev'. Once complete the contents of your appsettings.json file should be as follows... 83 | 84 | ``` javascript 85 | { 86 | "Messages": { 87 | "WelcomeMessage": "Hey from dev" 88 | } 89 | } 90 | ``` 91 | 92 | 13. Run the application by pressing F5. 93 | 94 | ## End of Activity 95 | 96 | The completed example for this activity can be found in the '/steps/08-Configuration' folder. 97 | 98 | Continue to [Activity 9](09-Logging.md). 99 | 100 | [Return to README and activity links](../README.md) 101 | -------------------------------------------------------------------------------- /activities/09-Logging.md: -------------------------------------------------------------------------------- 1 | 2 | # Logging 3 | 4 | For this activity, please ensure you have completed [Activity 8](08-Configuration.md) and have your solution open in Visual Studio 2017. 5 | 6 | ## What's the plan? 7 | 8 | We're going to use the default ASP.NET Core logging (Microsoft.Extensions.Logging) library to log a message to the console from our 'Calculate' action. 9 | 10 | ## Activity Steps 11 | 12 | 1. Update the constructor of the SampleController to accept an `ILogger` and store the object in a private field. 13 | 14 | ``` csharp 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | ``` 23 | 24 | *NOTE: This will require a using statement for 'Microsoft.Extensions.Logging'.* 25 | 26 | 2. Update the Calculate method to make use of logging as follows... 27 | 28 | ``` csharp 29 | public ActionResult Calculate(CalculateInputModel input) 30 | { 31 | var result = new CalculateOutputModel(); 32 | 33 | try 34 | { 35 | result.Result = input.Number1 + input.Number2; 36 | _logger.LogInformation("Successfully calculated a result"); 37 | } 38 | catch (Exception ex) 39 | { 40 | _logger.LogError(ex, "Unable to calculate a result"); 41 | } 42 | return Ok(result); 43 | } 44 | ``` 45 | *NOTE: This will require a using statement for 'System'.* 46 | 47 | 3. Run the application by pressing F5. 48 | 4. Send a post request using Postman. Use the same steps found in [Activity 6](6-AcceptingPostedData.md) 49 | 5. In Visual Studio, check the output window, showing out from "ASP.NET Core Web Server". 50 | 51 | ![Logging output](../images/2-LoggingOutput.png "Logging output") 52 | 53 | 6. Add a development logging section to your appsettings.development.json file with the following values... 54 | 55 | ``` javascript 56 | "Logging": { 57 | "LogLevel": { 58 | "Default": "Debug", 59 | "System": "Information", 60 | "Microsoft": "Information" 61 | } 62 | }, 63 | ``` 64 | 65 | 7. Add a production logging section to your appsettings.json file with the following values... 66 | 67 | ``` javascript 68 | "Logging": { 69 | "LogLevel": { 70 | "Default": "Warning" 71 | } 72 | }, 73 | ``` 74 | 75 | ## End of Activity 76 | 77 | The completed example for this activity can be found in the '/steps/09-Logging' folder. 78 | 79 | Continue to [Activity 10](10-InitialDockerfile.md). 80 | 81 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/10-InitialDockerfile.md: -------------------------------------------------------------------------------- 1 | 2 | # Initial Dockerfile 3 | 4 | For this activity, please ensure you have completed [Activity 9](09-Logging.md). 5 | 6 | ## What's the plan? 7 | 8 | We're going to create a dockerfile to containerise our application. We'll use the dockerfile to build a Docker image for the application so that it can be distributed and used to launch a container. 9 | 10 | ## Activity Steps 11 | 12 | 1. Copy the '.dockerignore' file from the 'resources' folder of this workshop repository and paste it into the root of your solution. *The same folder where your .sln file exists* 13 | 2. Create a new text document in the your root directory called 'dockerfile'. **It is important that you do not include an extension for this file** 14 | 3. Edit the 'dockerfile' in VS Code or Notepad (or your text editor of choice) and add the following... 15 | 16 | ``` 17 | FROM microsoft/dotnet:2.1-sdk 18 | 19 | WORKDIR /app 20 | 21 | COPY src/SampleApi . 22 | 23 | RUN dotnet build 24 | 25 | EXPOSE 80 26 | 27 | ENTRYPOINT ["dotnet", "run"] 28 | ``` 29 | 30 | 4. Save the file. 31 | 5. Open a command prompt window in the same directory as the dockerfile. 32 | 6. Build a docker image using the 'docker build' command... 33 | 34 | 35 | ``` 36 | docker build -t aspnetcore-workshop . 37 | ``` 38 | 39 | 7. Once the build completes, run a docker container from the image using the 'docker run' command... 40 | 41 | 42 | ``` 43 | docker run -p 7001:80 aspnetcore-workshop 44 | ``` 45 | *NOTE: If the above fails because the port 7001 is use on your PC, try another port number.* 46 | 47 | 8. At this point, you should see some output from the ASP.NET Core logging after the application starts. 48 | 9. Open a browser window and navigate to [http://localhost:7001](http://localhost:7001/). 49 | 10. In the command prompt, press CTRL + C to detach from the container. 50 | 11. View the running containers with the 'docker container ps' command. 51 | 52 | 53 | ``` 54 | docker container ps 55 | ``` 56 | 57 | 12. A single entry should exist for the container we have just been running. The first column will be the container ID. 58 | 13. Kill the container using the 'docker stop' command. It accepts a container ID. You only need to provide the first 2 or 3 characters. 59 | 60 | ``` 61 | docker stop 62 | ``` 63 | 64 | 14. View the size of the image we created using the 'docker images' command. 65 | 66 | ``` 67 | docker images aspnetcore-workshop 68 | ``` 69 | 70 | ## End of Activity 71 | 72 | The completed example for this activity can be found in the '/steps/10-Initial-Dockerfile' folder. 73 | 74 | Continue to [Activity 11](11-ProductionDockerfile.md). 75 | 76 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/11-ProductionDockerfile.md: -------------------------------------------------------------------------------- 1 | 2 | # Production Dockerfile 3 | 4 | For this activity, please ensure you have completed [Activity 10](10-InitialDockerfile.md). 5 | 6 | ## What's the plan? 7 | 8 | We're going to improve our dockerfile, using a multistage build to produce a runtime based production image. 9 | 10 | ## Activity Steps 11 | 12 | 1. Edit the 'dockerfile' in VS Code or Notepad (or your text editor of choice) and add the following... 13 | 14 | ``` 15 | # Build image 16 | 17 | FROM microsoft/dotnet:2.1-sdk as publish 18 | 19 | WORKDIR /publish 20 | 21 | COPY src/SampleApi/*.csproj ./src/SampleApi/ 22 | 23 | RUN dotnet restore src/SampleApi/SampleApi.csproj --verbosity quiet 24 | 25 | COPY ./src ./src 26 | 27 | RUN dotnet publish src/SampleApi/SampleApi.csproj --output ../../out -c Release --no-restore --verbosity quiet /clp:ErrorsOnly 28 | 29 | # Optimised final image 30 | 31 | FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine 32 | 33 | ENTRYPOINT ["dotnet", "SampleApi.dll"] 34 | 35 | WORKDIR /app 36 | 37 | EXPOSE 80 38 | 39 | COPY --from=publish /publish/out . 40 | ``` 41 | 42 | 2. Save the file. 43 | 3. Open a command prompt window in the same directory as the dockerfile. 44 | 4. Build a docker image using the 'docker build' command... 45 | 46 | 47 | ``` 48 | docker build -t aspnetcore-workshop-prod . 49 | ``` 50 | 51 | 5. Once the build completes, run a docker container from the image using the 'docker run' command... 52 | 53 | 54 | ``` 55 | docker run -p 7001:80 aspnetcore-workshop-prod 56 | ``` 57 | *NOTE: If the above fails because the port 7001 is use on your PC, try another port number.* 58 | 59 | 6. At this point, you should see some output from the ASP.NET Core logging after the application starts. 60 | 7. Open a browser window and navigate to [http://localhost:7001](http://localhost:7001/). 61 | 8. In the command prompt, press CTRL + C to detach from the container. 62 | 9. View the running containers with the 'docker container ps' command. 63 | 64 | 65 | ``` 66 | docker container ps 67 | ``` 68 | 69 | 10. A single entry should exist for the container we have just been running. The first column will be the container ID. 70 | 11. Kill the container using the 'docker stop' command. It accepts a container ID. You only need to provide the first 2 or 3 characters. 71 | 72 | ``` 73 | docker stop 74 | ``` 75 | 76 | 12. View the size of the image we created using the 'docker images' command. 77 | 78 | ``` 79 | docker images aspnetcore-workshop-prod 80 | ``` 81 | 82 | ## End of Activity 83 | 84 | The completed example for this activity can be found in the '/steps/11-Production-Dockerfile' folder. 85 | 86 | Continue to [Activity 12](12-DockerCompose.md). 87 | 88 | [Return to README and activity links](../README.md) 89 | -------------------------------------------------------------------------------- /activities/12-DockerCompose.md: -------------------------------------------------------------------------------- 1 | 2 | # Docker Compose 3 | 4 | For this activity, please ensure you have completed [Activity 11](11-ProductionDockerfile.md). 5 | 6 | ## What's the plan? 7 | 8 | We're going to add a docker-compose file to support running our application without having to recall the docker switch to expose ports. We'll also override the configuration for the application at runtime of the container by passing in an environment variable. 9 | 10 | ## Activity Steps 11 | 12 | 1. Create a new text document in the root directory called 'docker-compose.yml'. 13 | 2. Edit the 'docker-compose.yml' in VS Code or Notepad (or your text editor of choice) and add the following... 14 | 15 | ``` 16 | version: '3' 17 | 18 | services: 19 | 20 | api-service: 21 | build: 22 | context: . 23 | ports: 24 | - "10001:80" 25 | environment: 26 | - "Messages__WelcomeMessage=Hello from Docker" 27 | container_name: sample-api-service 28 | ``` 29 | 30 | 3. Save the file. 31 | 4. Open a command prompt window in the same directory as the Docker compose file. 32 | 5. Run the compose file using the docker-compose command... 33 | 34 | 35 | ``` 36 | docker-compose up -d 37 | ``` 38 | *The -d switch starts in detached mode* 39 | 40 | 6. Open a browser window and navigate to [http://localhost:10001](http://localhost:10001/). 41 | 42 | *Note that the welcome message has been loaded from the environment variable set in the compose file* 43 | 44 | 7. In the command prompt, view the running containers 45 | 46 | ``` 47 | docker-compose ps 48 | ``` 49 | 50 | 8. Stop the container... 51 | 52 | ``` 53 | docker-compose stop 54 | ``` 55 | 56 | ## End of Activity 57 | 58 | The completed example for this activity can be found in the '/steps/12-Docker-Compose' folder. 59 | 60 | Continue to [Activity 13](13-UnitTests.md). 61 | 62 | [Return to README and activity links](../README.md) 63 | -------------------------------------------------------------------------------- /activities/13-UnitTests.md: -------------------------------------------------------------------------------- 1 | 2 | # Adding Unit Test Project 3 | 4 | For this activity, please ensure you have completed [Activity 12](12-DockerCompose.md). 5 | 6 | ## What's the plan? 7 | 8 | We're going to add a unit test project with a simple test against the 'Hello' action of our 'SampleController'. We'll run the tests via the UI and/or the command line via the dotnet CLI. Finally we'll update the dockerfile so that tests are run during the Docker image build process. 9 | 10 | ## Activity Steps 11 | 12 | 1. Create a new Solution Folder called 'Test'. 13 | 2. Add a new XUnit based unit test project called 'SampleApi.Test', ensuring the location places the new project into a physical directory called 'Test' under your solution root directory. 14 | 15 | ![Add Test Project](../images/3-AddTestProject.png "Add Test Project") 16 | 17 | 3. Add a project reference to the SampleApi project from the new SampleApi.Test project. 18 | 19 | ![Add Project Reference](../images/4-TestProjectReferenceViaUi.png "Add Project Reference") 20 | 21 | *If you prefer to do this via the command line you can run the following command from your solution root directory* 22 | 23 | ```dotnet add test/SampleApi.Test/SampleApi.Test.csproj reference src/SampleApi/SampleApi.csproj``` 24 | 25 | 4. Add a NuGet package for the 'Moq' mocking library 26 | 27 | ![Add Moq Package](../images/5-TestProjectMoq.png "Add Moq Package") 28 | 29 | *If you prefer to do this via the command line you can run the following command from your solution root directory* 30 | 31 | ```dotnet add test/SampleApi.Test/SampleApi.Test.csproj package Moq``` 32 | 33 | 5. Add a NuGet package for the 'FluentAssertions' library 34 | 35 | *If you prefer to do this via the command line you can run the following command from your solution root directory* 36 | 37 | ```dotnet add test/SampleApi.Test/SampleApi.Test.csproj package FluentAssertions``` 38 | 39 | 6. Edit your SampleApi.Test project 40 | 41 | ![Edit Test Project](../images/6-EditTestProject.png "Edit Test Project") 42 | 43 | 7. Change the SDK at the top of the project file from 'Microsoft.NET.Sdk' to 'Microsoft.NET.Sdk.Web'. 44 | 45 | 8. Add an implicit reference to the aspnetcore metapackages to the PackageReference ItemGroup section 46 | 47 | `````` 48 | 49 | At this point your project file should look like this... 50 | 51 | ```xml 52 | 53 | 54 | 55 | netcoreapp2.1 56 | false 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | ``` 74 | 75 | 9. Rename the default 'UnitTest1' class to 'SampleControllerTests' using your preferred refactoring approach (also updating the file name). 76 | 77 | 10. Rename the 'Test1' method to 'Hello_Should_ReturnExpectedWelcomeMessage' 78 | 79 | 11. Update your test so that it looks like the block below. This will exercise the 'Hello' action method... 80 | 81 | ```csharp 82 | [Fact] 83 | public void Hello_Should_ReturnExpectedWelcomeMessage() 84 | { 85 | const string welcomeMessage = "TEST"; 86 | const string name = "Bob"; 87 | 88 | var expectedWelcome = $"{welcomeMessage} {name}"; 89 | 90 | var mockOptions = new Mock>(); 91 | mockOptions.Setup(x => x.Value).Returns(new MessagesConfiguration { WelcomeMessage = welcomeMessage }); 92 | 93 | var sut = new SampleController(mockOptions.Object, new NullLogger()); 94 | 95 | var actionResult = sut.Hello(name); 96 | 97 | var okResult = actionResult.Result.Should().BeAssignableTo() 98 | .Which.Value.Should().BeAssignableTo() 99 | .Which.WelcomeMessage.Should().Be(expectedWelcome); 100 | } 101 | ``` 102 | 103 | 12. Include any required Using statements. 104 | 105 | 13. Run the unit test via your preferred test runner. 106 | 107 | 14. You can also execute the test via the dotnet CLI. Run the following command from your solution root directory... 108 | 109 | ```dotnet test test/SampleApi.Test/SampleApi.Test.csproj``` 110 | 111 | ## End of Activity 112 | 113 | The completed example for this activity can be found in the '/steps/13-Unit-Tests' folder. 114 | 115 | Continue to [Activity 14](14-IntegrationTests.md). 116 | 117 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /activities/14-IntegrationTests.md: -------------------------------------------------------------------------------- 1 | 2 | # Adding In Memory Integration Tests 3 | 4 | For this activity, please ensure you have completed [Activity 13](12-UnitTests.md). 5 | 6 | ## What's the plan? 7 | 8 | We're going to add an integration test project with a simple test against the 'Hello' endpoint of our API. 9 | 10 | ## Activity Steps 11 | 12 | 1. Create a new Solution Folder called 'Test'. 13 | 2. Within your 'Test' solution folder, add a new XUnit based unit test project called 'SampleApi.IntegrationTest', ensuring the location places the new project into the physical 'Test' directory under your solution root directory (the same folder as we used for Step 13). 14 | 15 | 3. Add a project reference to the SampleApi project from the new SampleApi.IntegrationTest project. 16 | 17 | *If you prefer to do this via the command line you can run the following command from your solution root directory* 18 | 19 | ```dotnet add test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj reference src/SampleApi/SampleApi.csproj``` 20 | 21 | 4. Add a NuGet package for the 'Microsoft.AspNetCore.Mvc.Testing' library 22 | 23 | *If you prefer to do this via the command line you can run the following command from your solution root directory* 24 | 25 | ```dotnet add test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj package Microsoft.AspNetCore.Mvc.Testing``` 26 | 27 | 5. Add a NuGet package for the 'FluentAssertions' library 28 | 29 | *If you prefer to do this via the command line you can run the following command from your solution root directory* 30 | 31 | ```dotnet add test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj package FluentAssertions``` 32 | 33 | 6. Edit your SampleApi.Test project by right clicking and choosing 'Edit SampleApi.IntegrationTest.csproj'. 34 | 35 | 7. Change the SDK at the top of the project file from 'Microsoft.NET.Sdk' to 'Microsoft.NET.Sdk.Web'. 36 | 37 | 8. Add an implicit reference to the aspnetcore metapackages to the PackageReference ItemGroup section. 38 | 39 | `````` 40 | 41 | At this point your project file should look like this (although your package reference order may differ)... 42 | 43 | ```xml 44 | 45 | 46 | 47 | netcoreapp2.1 48 | false 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ``` 66 | 67 | 9. Rename the default 'UnitTest1' class to 'SampleControllerIntegrationTests' using your preferred refactoring approach (also updating the file name). 68 | 69 | 10. Update your class to implement 'IClassFixture>'. 70 | 71 | ```csharp 72 | public class SampleControllerIntegrationTests : IClassFixture> 73 | ``` 74 | 75 | 11. Add a constructor which accepts a 'WebApplicationFactory' and store this to a private read-only field. 76 | 77 | ```csharp 78 | private readonly WebApplicationFactory _factory; 79 | 80 | public SampleControllerIntegrationTests(WebApplicationFactory factory) 81 | { 82 | _factory = factory; 83 | } 84 | ``` 85 | 86 | 12. Add a private constant string called 'JsonContentType' with a value of "application/json; charset=utf-8" to the top of your class. 87 | 88 | ```csharp 89 | private const string JsonContentType = "application/json; charset=utf-8"; 90 | ``` 91 | 92 | 13. Rename the 'Test1' method to 'Hello_ReturnsExpectedContentType' 93 | 94 | 14. Update your test so that it looks like the block below. This will spin up a host and in-memory test web server before using the client to make a request to the 'Hello' endpoint. We are asserting some quite basic checks here but the tests can be far more involved. 95 | 96 | ```csharp 97 | [Fact] 98 | public async Task Hello_ReturnsExpectedContentType() 99 | { 100 | var client = _factory.CreateClient(); 101 | 102 | var response = await client.GetAsync("/Steve"); 103 | 104 | response.EnsureSuccessStatusCode(); // Status Code 200-299 105 | response.Content.Headers.ContentType.ToString().Should().Be(JsonContentType); 106 | } 107 | ``` 108 | 109 | 15. Include any required Using statements. 110 | 111 | 16. Run the unit test via your preferred test runner. 112 | 113 | 17. You can also execute the test via the dotnet CLI. Run the following command from your solution root directory... 114 | 115 | ```dotnet test test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj``` 116 | 117 | ## End of Activity 118 | 119 | The completed example for this activity can be found in the '/steps/14-Integration-Tests' folder. 120 | 121 | [Return to README and activity links](../README.md) -------------------------------------------------------------------------------- /images/0-EditSampleApiProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/0-EditSampleApiProject.png -------------------------------------------------------------------------------- /images/1-PostmanRequest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/1-PostmanRequest.png -------------------------------------------------------------------------------- /images/2-LoggingOutput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/2-LoggingOutput.png -------------------------------------------------------------------------------- /images/3-AddTestProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/3-AddTestProject.png -------------------------------------------------------------------------------- /images/4-TestProjectReferenceViaUi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/4-TestProjectReferenceViaUi.png -------------------------------------------------------------------------------- /images/5-TestProjectMoq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/5-TestProjectMoq.png -------------------------------------------------------------------------------- /images/6-EditTestProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/6-EditTestProject.png -------------------------------------------------------------------------------- /images/7-AppSettingsJson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejgordon/AspNetCoreDockerWorkshop/db6c9dc8523ea255a76d674739ba5096686e89e9/images/7-AppSettingsJson.png -------------------------------------------------------------------------------- /resources/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/02-Create-Solution-And-Project/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/02-Create-Solution-And-Project/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/02-Create-Solution-And-Project/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/02-Create-Solution-And-Project/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/02-Create-Solution-And-Project/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/02-Create-Solution-And-Project/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace SampleApi 11 | { 12 | public class Startup 13 | { 14 | // This method gets called by the runtime. Use this method to add services to the container. 15 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | } 19 | 20 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 21 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 22 | { 23 | if (env.IsDevelopment()) 24 | { 25 | app.UseDeveloperExceptionPage(); 26 | } 27 | 28 | app.Run(async (context) => 29 | { 30 | await context.Response.WriteAsync("Hello World!"); 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace SampleApi.Controllers 4 | { 5 | [ApiController] 6 | public class SampleController : ControllerBase 7 | { 8 | [Route("")] 9 | public string Hello() 10 | { 11 | return "Hello"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/03-Create-Controller-And-Action/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace SampleApi 7 | { 8 | public class Startup 9 | { 10 | // This method gets called by the runtime. Use this method to add services to the container. 11 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 12 | public void ConfigureServices(IServiceCollection services) 13 | { 14 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 15 | } 16 | 17 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 19 | { 20 | if (env.IsDevelopment()) 21 | { 22 | app.UseDeveloperExceptionPage(); 23 | } 24 | 25 | app.UseMvc(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using SampleApi.OutputModels; 3 | 4 | namespace SampleApi.Controllers 5 | { 6 | [ApiController] 7 | public class SampleController : ControllerBase 8 | { 9 | [Route("")] 10 | public ActionResult Hello() 11 | { 12 | var model = new HelloOutputModel(); 13 | return Ok(model); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/04-Returning-JSON-Data/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace SampleApi 7 | { 8 | public class Startup 9 | { 10 | // This method gets called by the runtime. Use this method to add services to the container. 11 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 12 | public void ConfigureServices(IServiceCollection services) 13 | { 14 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 15 | } 16 | 17 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 19 | { 20 | if (env.IsDevelopment()) 21 | { 22 | app.UseDeveloperExceptionPage(); 23 | } 24 | 25 | app.UseMvc(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using SampleApi.OutputModels; 3 | 4 | namespace SampleApi.Controllers 5 | { 6 | [ApiController] 7 | public class SampleController : ControllerBase 8 | { 9 | [Route("")] 10 | public ActionResult Hello(string name) 11 | { 12 | var model = new HelloOutputModel(); 13 | model.WelcomeMessage = $"{model.WelcomeMessage} {name ?? "whoever you are!"}"; 14 | return Ok(model); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/05-Binding-Data-From-Query-String/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace SampleApi 7 | { 8 | public class Startup 9 | { 10 | // This method gets called by the runtime. Use this method to add services to the container. 11 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 12 | public void ConfigureServices(IServiceCollection services) 13 | { 14 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 15 | } 16 | 17 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 19 | { 20 | if (env.IsDevelopment()) 21 | { 22 | app.UseDeveloperExceptionPage(); 23 | } 24 | 25 | app.UseMvc(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using SampleApi.OutputModels; 3 | 4 | namespace SampleApi.Controllers 5 | { 6 | [ApiController] 7 | public class SampleController : ControllerBase 8 | { 9 | [Route("{name?}")] 10 | public ActionResult Hello(string name) 11 | { 12 | var model = new HelloOutputModel(); 13 | model.WelcomeMessage = $"{model.WelcomeMessage} {name ?? "whoever you are!"}"; 14 | return Ok(model); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/06-Binding-From-Route-Parameters/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace SampleApi 7 | { 8 | public class Startup 9 | { 10 | // This method gets called by the runtime. Use this method to add services to the container. 11 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 12 | public void ConfigureServices(IServiceCollection services) 13 | { 14 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 15 | } 16 | 17 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 19 | { 20 | if (env.IsDevelopment()) 21 | { 22 | app.UseDeveloperExceptionPage(); 23 | } 24 | 25 | app.UseMvc(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using SampleApi.InputModels; 3 | using SampleApi.OutputModels; 4 | 5 | namespace SampleApi.Controllers 6 | { 7 | [ApiController] 8 | public class SampleController : ControllerBase 9 | { 10 | [Route("{name?}")] 11 | public ActionResult Hello(string name) 12 | { 13 | var model = new HelloOutputModel(); 14 | model.WelcomeMessage = $"{model.WelcomeMessage} {name ?? "whoever you are!"}"; 15 | return Ok(model); 16 | } 17 | 18 | [HttpPost] 19 | [Route("calculate")] 20 | public ActionResult Calculate(CalculateInputModel input) 21 | { 22 | var result = new CalculateOutputModel { Result = input.Number1 + input.Number2 }; 23 | return Ok(result); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/07-Accepting-Posted-Data/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace SampleApi 7 | { 8 | public class Startup 9 | { 10 | // This method gets called by the runtime. Use this method to add services to the container. 11 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 12 | public void ConfigureServices(IServiceCollection services) 13 | { 14 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 15 | } 16 | 17 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 19 | { 20 | if (env.IsDevelopment()) 21 | { 22 | app.UseDeveloperExceptionPage(); 23 | } 24 | 25 | app.UseMvc(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /steps/08-Configuration/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/08-Configuration/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Options; 3 | using SampleApi.Configuration; 4 | using SampleApi.InputModels; 5 | using SampleApi.OutputModels; 6 | 7 | namespace SampleApi.Controllers 8 | { 9 | [ApiController] 10 | public class SampleController : ControllerBase 11 | { 12 | private readonly IOptions _messagesConfig; 13 | 14 | public SampleController(IOptions messagesConfig) 15 | { 16 | _messagesConfig = messagesConfig; 17 | } 18 | 19 | [Route("{name?}")] 20 | public ActionResult Hello(string name) 21 | { 22 | var model = new HelloOutputModel(); 23 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 24 | return Ok(model); 25 | } 26 | 27 | [HttpPost] 28 | [Route("calculate")] 29 | public ActionResult Calculate(CalculateInputModel input) 30 | { 31 | var result = new CalculateOutputModel { Result = input.Number1 + input.Number2 }; 32 | return Ok(result); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Messages": { 3 | "WelcomeMessage": "Hey from dev" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /steps/08-Configuration/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Messages": { 3 | "WelcomeMessage": "Hey" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /steps/09-Logging/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/09-Logging/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model); 29 | } 30 | 31 | [HttpPost] 32 | [Route("calculate")] 33 | public ActionResult Calculate(CalculateInputModel input) 34 | { 35 | var result = new CalculateOutputModel(); 36 | 37 | try 38 | { 39 | result.Result = input.Number1 + input.Number2; 40 | _logger.LogInformation("Successfully calculated a result"); 41 | } 42 | catch (Exception ex) 43 | { 44 | _logger.LogError(ex, "Unable to calculate a result"); 45 | } 46 | return Ok(result); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/09-Logging/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/dotnet:2.1-sdk 2 | 3 | WORKDIR /app 4 | 5 | COPY src/SampleApi . 6 | 7 | RUN dotnet build 8 | 9 | EXPOSE 80 10 | 11 | ENTRYPOINT ["dotnet", "run"] -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model); 29 | } 30 | 31 | [HttpPost] 32 | [Route("calculate")] 33 | public ActionResult Calculate(CalculateInputModel input) 34 | { 35 | var result = new CalculateOutputModel(); 36 | 37 | try 38 | { 39 | result.Result = input.Number1 + input.Number2; 40 | _logger.LogInformation("Successfully calculated a result"); 41 | } 42 | catch (Exception ex) 43 | { 44 | _logger.LogError(ex, "Unable to calculate a result"); 45 | } 46 | return Ok(result); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/10-Initial-Dockerfile/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/dockerfile: -------------------------------------------------------------------------------- 1 | # Build image 2 | 3 | FROM microsoft/dotnet:2.1-sdk as publish 4 | 5 | WORKDIR /publish 6 | 7 | COPY src/SampleApi/*.csproj ./src/SampleApi/ 8 | 9 | RUN dotnet restore src/SampleApi/SampleApi.csproj --verbosity quiet 10 | 11 | COPY ./src ./src 12 | 13 | RUN dotnet publish src/SampleApi/SampleApi.csproj --output ../../out -c Release --no-restore --verbosity quiet /clp:ErrorsOnly 14 | 15 | # Optimised final image 16 | 17 | FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine 18 | 19 | ENTRYPOINT ["dotnet", "SampleApi.dll"] 20 | 21 | WORKDIR /app 22 | 23 | EXPOSE 80 24 | 25 | COPY --from=publish /publish/out . -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model); 29 | } 30 | 31 | [HttpPost] 32 | [Route("calculate")] 33 | public ActionResult Calculate(CalculateInputModel input) 34 | { 35 | var result = new CalculateOutputModel(); 36 | 37 | try 38 | { 39 | result.Result = input.Number1 + input.Number2; 40 | _logger.LogInformation("Successfully calculated a result"); 41 | } 42 | catch (Exception ex) 43 | { 44 | _logger.LogError(ex, "Unable to calculate a result"); 45 | } 46 | return Ok(result); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/11-Production-Dockerfile/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/12-Docker-Compose/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(NestedProjects) = preSolution 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | api-service: 6 | build: 7 | context: . 8 | ports: 9 | - "10001:80" 10 | environment: 11 | - "Messages__WelcomeMessage=Hello from Docker" 12 | container_name: sample-api-service -------------------------------------------------------------------------------- /steps/12-Docker-Compose/dockerfile: -------------------------------------------------------------------------------- 1 | # Build image 2 | 3 | FROM microsoft/dotnet:2.1-sdk as publish 4 | 5 | WORKDIR /publish 6 | 7 | COPY src/SampleApi/*.csproj ./src/SampleApi/ 8 | 9 | RUN dotnet restore src/SampleApi/SampleApi.csproj --verbosity quiet 10 | 11 | COPY ./src ./src 12 | 13 | RUN dotnet publish src/SampleApi/SampleApi.csproj --output ../../out -c Release --no-restore --verbosity quiet /clp:ErrorsOnly 14 | 15 | # Optimised final image 16 | 17 | FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine 18 | 19 | ENTRYPOINT ["dotnet", "SampleApi.dll"] 20 | 21 | WORKDIR /app 22 | 23 | EXPOSE 80 24 | 25 | COPY --from=publish /publish/out . -------------------------------------------------------------------------------- /steps/12-Docker-Compose/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model); 29 | } 30 | 31 | [HttpPost] 32 | [Route("calculate")] 33 | public ActionResult Calculate(CalculateInputModel input) 34 | { 35 | var result = new CalculateOutputModel(); 36 | 37 | try 38 | { 39 | result.Result = input.Number1 + input.Number2; 40 | _logger.LogInformation("Successfully calculated a result"); 41 | } 42 | catch (Exception ex) 43 | { 44 | _logger.LogError(ex, "Unable to calculate a result"); 45 | } 46 | return Ok(result); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/12-Docker-Compose/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/13-Unit-Tests/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2F277387-CAB6-421C-8757-3F48F313CB78}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi.Test", "test\SampleApi.Test\SampleApi.Test.csproj", "{79216D43-8936-4929-9366-F87686373282}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 35 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 36 | {79216D43-8936-4929-9366-F87686373282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {79216D43-8936-4929-9366-F87686373282}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {79216D43-8936-4929-9366-F87686373282}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {79216D43-8936-4929-9366-F87686373282}.Debug|x64.Build.0 = Debug|Any CPU 40 | {79216D43-8936-4929-9366-F87686373282}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {79216D43-8936-4929-9366-F87686373282}.Debug|x86.Build.0 = Debug|Any CPU 42 | {79216D43-8936-4929-9366-F87686373282}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {79216D43-8936-4929-9366-F87686373282}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {79216D43-8936-4929-9366-F87686373282}.Release|x64.ActiveCfg = Release|Any CPU 45 | {79216D43-8936-4929-9366-F87686373282}.Release|x64.Build.0 = Release|Any CPU 46 | {79216D43-8936-4929-9366-F87686373282}.Release|x86.ActiveCfg = Release|Any CPU 47 | {79216D43-8936-4929-9366-F87686373282}.Release|x86.Build.0 = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(NestedProjects) = preSolution 53 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 54 | {79216D43-8936-4929-9366-F87686373282} = {2F277387-CAB6-421C-8757-3F48F313CB78} 55 | EndGlobalSection 56 | GlobalSection(ExtensibilityGlobals) = postSolution 57 | SolutionGuid = {E1435368-2E74-447C-A722-8BD1CE4E8B22} 58 | EndGlobalSection 59 | EndGlobal 60 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | api-service: 6 | build: 7 | context: . 8 | ports: 9 | - "10001:80" 10 | environment: 11 | - "Messages__WelcomeMessage=Hello from Docker" 12 | container_name: sample-api-service -------------------------------------------------------------------------------- /steps/13-Unit-Tests/dockerfile: -------------------------------------------------------------------------------- 1 | # Build image 2 | 3 | FROM microsoft/dotnet:2.1-sdk as publish 4 | 5 | WORKDIR /publish 6 | 7 | COPY src/SampleApi/*.csproj ./src/SampleApi/ 8 | 9 | RUN dotnet restore src/SampleApi/SampleApi.csproj --verbosity quiet 10 | 11 | COPY ./src ./src 12 | 13 | RUN dotnet publish src/SampleApi/SampleApi.csproj --output ../../out -c Release --no-restore --verbosity quiet /clp:ErrorsOnly 14 | 15 | # Optimised final image 16 | 17 | FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine 18 | 19 | ENTRYPOINT ["dotnet", "SampleApi.dll"] 20 | 21 | WORKDIR /app 22 | 23 | EXPOSE 80 24 | 25 | COPY --from=publish /publish/out . -------------------------------------------------------------------------------- /steps/13-Unit-Tests/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model);} 29 | 30 | [HttpPost] 31 | [Route("calculate")] 32 | public ActionResult Calculate(CalculateInputModel input) 33 | { 34 | var result = new CalculateOutputModel(); 35 | 36 | try 37 | { 38 | result.Result = input.Number1 + input.Number2; 39 | _logger.LogInformation("Successfully calculated a result"); 40 | } 41 | catch (Exception ex) 42 | { 43 | _logger.LogError(ex, "Unable to calculate a result"); 44 | } 45 | return Ok(result); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/test/SampleApi.Test/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:55988/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi.Test": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:55989/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/13-Unit-Tests/test/SampleApi.Test/SampleApi.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /steps/13-Unit-Tests/test/SampleApi.Test/SampleControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging.Abstractions; 5 | using Microsoft.Extensions.Options; 6 | using Moq; 7 | using SampleApi.Configuration; 8 | using SampleApi.Controllers; 9 | using SampleApi.OutputModels; 10 | using Xunit; 11 | 12 | namespace SampleApi.Test 13 | { 14 | public class SampleControllerTests 15 | { 16 | [Fact] 17 | public void Hello_Should_ReturnExpectedWelcomeMessage() 18 | { 19 | const string welcomeMessage = "TEST"; 20 | const string name = "Bob"; 21 | 22 | var expectedWelcome = $"{welcomeMessage} {name}"; 23 | 24 | var mockOptions = new Mock>(); 25 | mockOptions.Setup(x => x.Value).Returns(new MessagesConfiguration { WelcomeMessage = welcomeMessage }); 26 | 27 | var sut = new SampleController(mockOptions.Object, new NullLogger()); 28 | 29 | var actionResult = sut.Hello(name); 30 | 31 | var okResult = actionResult.Result.Should().BeAssignableTo() 32 | .Which.Value.Should().BeAssignableTo() 33 | .Which.WelcomeMessage.Should().Be(expectedWelcome); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/14-Integration-Tests/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2F277387-CAB6-421C-8757-3F48F313CB78}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApi.Test", "test\SampleApi.Test\SampleApi.Test.csproj", "{79216D43-8936-4929-9366-F87686373282}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi.IntegrationTest", "test\SampleApi.IntegrationTest\SampleApi.IntegrationTest.csproj", "{09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x64 = Release|x64 23 | Release|x86 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 35 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 36 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 38 | {79216D43-8936-4929-9366-F87686373282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {79216D43-8936-4929-9366-F87686373282}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {79216D43-8936-4929-9366-F87686373282}.Debug|x64.ActiveCfg = Debug|Any CPU 41 | {79216D43-8936-4929-9366-F87686373282}.Debug|x64.Build.0 = Debug|Any CPU 42 | {79216D43-8936-4929-9366-F87686373282}.Debug|x86.ActiveCfg = Debug|Any CPU 43 | {79216D43-8936-4929-9366-F87686373282}.Debug|x86.Build.0 = Debug|Any CPU 44 | {79216D43-8936-4929-9366-F87686373282}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {79216D43-8936-4929-9366-F87686373282}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {79216D43-8936-4929-9366-F87686373282}.Release|x64.ActiveCfg = Release|Any CPU 47 | {79216D43-8936-4929-9366-F87686373282}.Release|x64.Build.0 = Release|Any CPU 48 | {79216D43-8936-4929-9366-F87686373282}.Release|x86.ActiveCfg = Release|Any CPU 49 | {79216D43-8936-4929-9366-F87686373282}.Release|x86.Build.0 = Release|Any CPU 50 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x64.Build.0 = Debug|Any CPU 54 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x86.ActiveCfg = Debug|Any CPU 55 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x86.Build.0 = Debug|Any CPU 56 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x64.ActiveCfg = Release|Any CPU 59 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x64.Build.0 = Release|Any CPU 60 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x86.ActiveCfg = Release|Any CPU 61 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x86.Build.0 = Release|Any CPU 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(NestedProjects) = preSolution 67 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 68 | {79216D43-8936-4929-9366-F87686373282} = {2F277387-CAB6-421C-8757-3F48F313CB78} 69 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547} = {2F277387-CAB6-421C-8757-3F48F313CB78} 70 | EndGlobalSection 71 | GlobalSection(ExtensibilityGlobals) = postSolution 72 | SolutionGuid = {E1435368-2E74-447C-A722-8BD1CE4E8B22} 73 | EndGlobalSection 74 | EndGlobal 75 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | api-service: 6 | build: 7 | context: . 8 | ports: 9 | - "10001:80" 10 | environment: 11 | - "Messages__WelcomeMessage=Hello from Docker" 12 | container_name: sample-api-service -------------------------------------------------------------------------------- /steps/14-Integration-Tests/dockerfile: -------------------------------------------------------------------------------- 1 | # Build image 2 | 3 | FROM microsoft/dotnet:2.1-sdk as publish 4 | 5 | WORKDIR /publish 6 | 7 | COPY src/SampleApi/*.csproj ./src/SampleApi/ 8 | 9 | RUN dotnet restore src/SampleApi/SampleApi.csproj --verbosity quiet 10 | 11 | COPY ./src ./src 12 | 13 | RUN dotnet publish src/SampleApi/SampleApi.csproj --output ../../out -c Release --no-restore --verbosity quiet /clp:ErrorsOnly 14 | 15 | # Optimised final image 16 | 17 | FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine 18 | 19 | ENTRYPOINT ["dotnet", "SampleApi.dll"] 20 | 21 | WORKDIR /app 22 | 23 | EXPOSE 80 24 | 25 | COPY --from=publish /publish/out . -------------------------------------------------------------------------------- /steps/14-Integration-Tests/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model);} 29 | 30 | [HttpPost] 31 | [Route("calculate")] 32 | public ActionResult Calculate(CalculateInputModel input) 33 | { 34 | var result = new CalculateOutputModel(); 35 | 36 | try 37 | { 38 | result.Result = input.Number1 + input.Number2; 39 | _logger.LogInformation("Successfully calculated a result"); 40 | } 41 | catch (Exception ex) 42 | { 43 | _logger.LogError(ex, "Unable to calculate a result"); 44 | } 45 | return Ok(result); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/appsettings.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.IntegrationTest/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59827/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi.IntegrationTest": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:59828/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.IntegrationTest/SampleControllerIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Microsoft.AspNetCore.Mvc.Testing; 4 | using Xunit; 5 | 6 | namespace SampleApi.IntegrationTest 7 | { 8 | public class SampleControllerIntegrationTests : IClassFixture> 9 | { 10 | private const string JsonContentType = "application/json; charset=utf-8"; 11 | 12 | private readonly WebApplicationFactory _factory; 13 | 14 | public SampleControllerIntegrationTests(WebApplicationFactory factory) 15 | { 16 | _factory = factory; 17 | } 18 | 19 | [Fact] 20 | public async Task Hello_ReturnsExpectedContentType() 21 | { 22 | var client = _factory.CreateClient(); 23 | 24 | var response = await client.GetAsync("/Steve"); 25 | 26 | response.EnsureSuccessStatusCode(); // Status Code 200-299 27 | response.Content.Headers.ContentType.ToString().Should().Be(JsonContentType); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.IntegrationTest/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "shadowCopy": false 3 | } -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.Test/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:55988/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi.Test": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:55989/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.Test/SampleApi.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /steps/14-Integration-Tests/test/SampleApi.Test/SampleControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging.Abstractions; 5 | using Microsoft.Extensions.Options; 6 | using Moq; 7 | using SampleApi.Configuration; 8 | using SampleApi.Controllers; 9 | using SampleApi.OutputModels; 10 | using Xunit; 11 | 12 | namespace SampleApi.Test 13 | { 14 | public class SampleControllerTests 15 | { 16 | [Fact] 17 | public void Hello_Should_ReturnExpectedWelcomeMessage() 18 | { 19 | const string welcomeMessage = "TEST"; 20 | const string name = "Bob"; 21 | 22 | var expectedWelcome = $"{welcomeMessage} {name}"; 23 | 24 | var mockOptions = new Mock>(); 25 | mockOptions.Setup(x => x.Value).Returns(new MessagesConfiguration { WelcomeMessage = welcomeMessage }); 26 | 27 | var sut = new SampleController(mockOptions.Object, new NullLogger()); 28 | 29 | var actionResult = sut.Hello(name); 30 | 31 | var okResult = actionResult.Result.Should().BeAssignableTo() 32 | .Which.Value.Should().BeAssignableTo() 33 | .Which.WelcomeMessage.Should().Be(expectedWelcome); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/.dockerignore: -------------------------------------------------------------------------------- 1 | **/obj 2 | **/bin 3 | .vs 4 | *.sh 5 | .git 6 | .gitignore 7 | **/launchSettings.json -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/SampleApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{61C50D69-0377-4212-9E88-E329C797284C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApi", "src\SampleApi\SampleApi.csproj", "{D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2F277387-CAB6-421C-8757-3F48F313CB78}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApi.Test", "test\SampleApi.Test\SampleApi.Test.csproj", "{79216D43-8936-4929-9366-F87686373282}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApi.IntegrationTest", "test\SampleApi.IntegrationTest\SampleApi.IntegrationTest.csproj", "{09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x64 = Release|x64 23 | Release|x86 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.ActiveCfg = Debug|Any CPU 29 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x64.Build.0 = Debug|Any CPU 30 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.ActiveCfg = Debug|Any CPU 31 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Debug|x86.Build.0 = Debug|Any CPU 32 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.ActiveCfg = Release|Any CPU 35 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x64.Build.0 = Release|Any CPU 36 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.ActiveCfg = Release|Any CPU 37 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C}.Release|x86.Build.0 = Release|Any CPU 38 | {79216D43-8936-4929-9366-F87686373282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {79216D43-8936-4929-9366-F87686373282}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {79216D43-8936-4929-9366-F87686373282}.Debug|x64.ActiveCfg = Debug|Any CPU 41 | {79216D43-8936-4929-9366-F87686373282}.Debug|x64.Build.0 = Debug|Any CPU 42 | {79216D43-8936-4929-9366-F87686373282}.Debug|x86.ActiveCfg = Debug|Any CPU 43 | {79216D43-8936-4929-9366-F87686373282}.Debug|x86.Build.0 = Debug|Any CPU 44 | {79216D43-8936-4929-9366-F87686373282}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {79216D43-8936-4929-9366-F87686373282}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {79216D43-8936-4929-9366-F87686373282}.Release|x64.ActiveCfg = Release|Any CPU 47 | {79216D43-8936-4929-9366-F87686373282}.Release|x64.Build.0 = Release|Any CPU 48 | {79216D43-8936-4929-9366-F87686373282}.Release|x86.ActiveCfg = Release|Any CPU 49 | {79216D43-8936-4929-9366-F87686373282}.Release|x86.Build.0 = Release|Any CPU 50 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x64.Build.0 = Debug|Any CPU 54 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x86.ActiveCfg = Debug|Any CPU 55 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Debug|x86.Build.0 = Debug|Any CPU 56 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x64.ActiveCfg = Release|Any CPU 59 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x64.Build.0 = Release|Any CPU 60 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x86.ActiveCfg = Release|Any CPU 61 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547}.Release|x86.Build.0 = Release|Any CPU 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(NestedProjects) = preSolution 67 | {D18B7B47-6C35-4A99-9B6F-9A904A2F7A9C} = {61C50D69-0377-4212-9E88-E329C797284C} 68 | {79216D43-8936-4929-9366-F87686373282} = {2F277387-CAB6-421C-8757-3F48F313CB78} 69 | {09CDBB7A-4EF9-4EEF-A8D9-6446CFE1A547} = {2F277387-CAB6-421C-8757-3F48F313CB78} 70 | EndGlobalSection 71 | GlobalSection(ExtensibilityGlobals) = postSolution 72 | SolutionGuid = {E1435368-2E74-447C-A722-8BD1CE4E8B22} 73 | EndGlobalSection 74 | EndGlobal 75 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | api-service: 6 | build: 7 | context: . 8 | ports: 9 | - "10001:80" 10 | environment: 11 | - "Messages__WelcomeMessage=Hello from Docker" 12 | container_name: sample-api-service -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/dockerfile: -------------------------------------------------------------------------------- 1 | # Build image 2 | 3 | FROM microsoft/dotnet:2.1-sdk as publish 4 | 5 | WORKDIR /publish 6 | 7 | COPY SampleApi.sln . 8 | COPY src/SampleApi/*.csproj ./src/SampleApi/ 9 | COPY test/SampleApi.Test/*.csproj ./test/SampleApi.Test/ 10 | COPY test/SampleApi.IntegrationTest/*.csproj ./test/SampleApi.IntegrationTest/ 11 | 12 | RUN dotnet restore --verbosity quiet 13 | 14 | COPY ./src ./src 15 | COPY ./test ./test 16 | 17 | RUN dotnet build -c Release --no-restore /clp:ErrorsOnly 18 | RUN dotnet test test/SampleApi.Test/SampleApi.Test.csproj -c Release --no-build --no-restore --verbosity minimal 19 | RUN dotnet test test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj -c Release --no-build --no-restore --verbosity minimal 20 | RUN dotnet publish src/SampleApi/SampleApi.csproj --output ../../out -c Release --no-restore --verbosity quiet /clp:ErrorsOnly 21 | 22 | # Optimised final image 23 | 24 | FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine 25 | 26 | ENTRYPOINT ["dotnet", "SampleApi.dll"] 27 | 28 | WORKDIR /app 29 | 30 | EXPOSE 80 31 | 32 | COPY --from=publish /publish/out . -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.403" 4 | } 5 | } -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/Configuration/MessagesConfiguration.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.Configuration 3 | { 4 | public class MessagesConfiguration 5 | { 6 | public string WelcomeMessage { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/Controllers/SampleController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using SampleApi.Configuration; 6 | using SampleApi.InputModels; 7 | using SampleApi.OutputModels; 8 | 9 | namespace SampleApi.Controllers 10 | { 11 | [ApiController] 12 | public class SampleController : ControllerBase 13 | { 14 | private readonly IOptions _messagesConfig; 15 | private readonly ILogger _logger; 16 | 17 | public SampleController(IOptions messagesConfig, ILogger logger) 18 | { 19 | _messagesConfig = messagesConfig; 20 | _logger = logger; 21 | } 22 | 23 | [Route("{name?}")] 24 | public ActionResult Hello(string name) 25 | { 26 | var model = new HelloOutputModel(); 27 | model.WelcomeMessage = $"{_messagesConfig.Value.WelcomeMessage} {name ?? "whoever you are!"}"; 28 | return Ok(model);} 29 | 30 | [HttpPost] 31 | [Route("calculate")] 32 | public ActionResult Calculate(CalculateInputModel input) 33 | { 34 | var result = new CalculateOutputModel(); 35 | 36 | try 37 | { 38 | result.Result = input.Number1 + input.Number2; 39 | _logger.LogInformation("Successfully calculated a result"); 40 | } 41 | catch (Exception ex) 42 | { 43 | _logger.LogError(ex, "Unable to calculate a result"); 44 | } 45 | return Ok(result); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/InputModels/CalculateInputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.InputModels 7 | { 8 | public class CalculateInputModel 9 | { 10 | public int Number1 { get; set; } 11 | public int Number2 { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/OutputModels/CalculateOutputModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace SampleApi.OutputModels 7 | { 8 | public class CalculateOutputModel 9 | { 10 | public int Result { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/OutputModels/HelloOutputModel.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace SampleApi.OutputModels 3 | { 4 | public class HelloOutputModel 5 | { 6 | public string WelcomeMessage { get; set; } = "Hello"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleApi 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65244", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/SampleApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using SampleApi.Configuration; 7 | 8 | namespace SampleApi 9 | { 10 | public class Startup 11 | { 12 | public IConfiguration Configuration { get; } 13 | 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.Configure(Configuration.GetSection("Messages")); 24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | } 34 | 35 | app.UseMvc(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/appsettings.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | }, 9 | "Messages": { 10 | "WelcomeMessage": "Hey from dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/src/SampleApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "Messages": { 8 | "WelcomeMessage": "Hey" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.IntegrationTest/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59827/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi.IntegrationTest": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:59828/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.IntegrationTest/SampleApi.IntegrationTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.IntegrationTest/SampleControllerIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Microsoft.AspNetCore.Mvc.Testing; 4 | using Xunit; 5 | 6 | namespace SampleApi.IntegrationTest 7 | { 8 | public class SampleControllerIntegrationTests : IClassFixture> 9 | { 10 | private const string JsonContentType = "application/json; charset=utf-8"; 11 | 12 | private readonly WebApplicationFactory _factory; 13 | 14 | public SampleControllerIntegrationTests(WebApplicationFactory factory) 15 | { 16 | _factory = factory; 17 | } 18 | 19 | [Fact] 20 | public async Task Hello_ReturnsExpectedContentType() 21 | { 22 | var client = _factory.CreateClient(); 23 | 24 | var response = await client.GetAsync("/Steve"); 25 | 26 | response.EnsureSuccessStatusCode(); // Status Code 200-299 27 | response.Content.Headers.ContentType.ToString().Should().Be(JsonContentType); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.IntegrationTest/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "shadowCopy": false 3 | } -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.Test/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:55988/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SampleApi.Test": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:55989/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.Test/SampleApi.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /steps/15-Testing-In-Docker/test/SampleApi.Test/SampleControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging.Abstractions; 5 | using Microsoft.Extensions.Options; 6 | using Moq; 7 | using SampleApi.Configuration; 8 | using SampleApi.Controllers; 9 | using SampleApi.OutputModels; 10 | using Xunit; 11 | 12 | namespace SampleApi.Test 13 | { 14 | public class SampleControllerTests 15 | { 16 | [Fact] 17 | public void Hello_Should_ReturnExpectedWelcomeMessage() 18 | { 19 | const string welcomeMessage = "TEST"; 20 | const string name = "Bob"; 21 | 22 | var expectedWelcome = $"{welcomeMessage} {name}"; 23 | 24 | var mockOptions = new Mock>(); 25 | mockOptions.Setup(x => x.Value).Returns(new MessagesConfiguration { WelcomeMessage = welcomeMessage }); 26 | 27 | var sut = new SampleController(mockOptions.Object, new NullLogger()); 28 | 29 | var actionResult = sut.Hello(name); 30 | 31 | var okResult = actionResult.Result.Should().BeAssignableTo() 32 | .Which.Value.Should().BeAssignableTo() 33 | .Which.WelcomeMessage.Should().Be(expectedWelcome); 34 | } 35 | } 36 | } 37 | --------------------------------------------------------------------------------