{"id":21857577,"url":"https://github.com/hjerpbakk/aspnetcoreworkshop","last_synced_at":"2026-04-07T08:32:17.954Z","repository":{"id":149283407,"uuid":"148107396","full_name":"hjerpbakk/AspNetCoreWorkshop","owner":"hjerpbakk","description":"Simple workshop introducing ASP.Net Core, Docker and Docker Compose.","archived":false,"fork":false,"pushed_at":"2018-09-20T08:30:16.000Z","size":1893,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-21T19:32:45.394Z","etag":null,"topics":["asp-net-core","docker","docker-compose","docker-image","dotnet","workshop"],"latest_commit_sha":null,"homepage":"https://hjerpbakk.com/","language":"CSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hjerpbakk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-09-10T06:12:20.000Z","updated_at":"2021-04-17T08:25:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"7ec08e37-7e48-4fe6-a1ee-3450f45c3140","html_url":"https://github.com/hjerpbakk/AspNetCoreWorkshop","commit_stats":null,"previous_names":["hjerpbakk/aspnetcoreworkshop"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hjerpbakk/AspNetCoreWorkshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjerpbakk%2FAspNetCoreWorkshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjerpbakk%2FAspNetCoreWorkshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjerpbakk%2FAspNetCoreWorkshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjerpbakk%2FAspNetCoreWorkshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hjerpbakk","download_url":"https://codeload.github.com/hjerpbakk/AspNetCoreWorkshop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hjerpbakk%2FAspNetCoreWorkshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31506562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","docker","docker-compose","docker-image","dotnet","workshop"],"created_at":"2024-11-28T02:30:04.778Z","updated_at":"2026-04-07T08:32:17.933Z","avatar_url":"https://github.com/hjerpbakk.png","language":"CSS","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Workshop Tromsø 20.09.2018\n\nAn ASP.net Web app running in containers orchestrated by Docker Compose. In this workshop, all commands are run from the root folder of project unless otherwise noted.\n\nWe have prepared Git branches with solutions for each part - they are written out in bold text, e.g.\n#### `git checkout start`\n\n## Prerequisites\n\n- Install `git`\n- Install the latest version of `.Net Core`.\n- Install the latest version of `Docker`.\n- Install `Visual Studio Code`.\n- Run `docker run --rm microsoft/dotnet:2.1-sdk-alpine`.\n- Run `docker run --rm microsoft/dotnet:2.1-aspnetcore-runtime-alpine`.\n- Install postman.\n\n## Part 1: Anatomy of an ASP.Net Web App\n#### `git checkout start`\n\nIn this part you will create a simple ASP.Net web app and web API, familiarising yourself with their architectures.\n\n### Create app from template\n\n- `mkdir Workshop`\n- `cd Workshop`\n- `dotnet new mvc --no-https`\n\n### Use Visual Studio Code to develop and debug\n\n- `code .`\n\n![](doc/vscode.png)\n\n- When Visual Studio Code asks if you want to add \"required assets to build and debug\", press *yes*.\n- `Ctrl + Shift + B` to build (`Shift + ⌘ + B` on a Mac)\n- `F5` to debug\n- You should see the default ASP.Net website template running in your browser.\n\n### Get to know ASP.Net\n\n- [Static resources](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-2.1\u0026tabs=aspnetcore2x)\n- [Model View Controller](https://docs.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-2.1)\n- [Dependency Injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1)\n- [Configuration](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1)\n- [Routes](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1)\n- [Request pipeline](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1)\n\n#### Solution: `git checkout part1-mvc-template`\n\n### Implement an actual app\nNow you'll add a simple 2048 game and high score services. \n\n#### Create HTML5 game\n\n- Add the content of the `Game` folder to your `wwwroot` folder.\n- Verify that the game runs on [http://localhost:5000/Game.html](http://localhost:5000/Game.html).\n\n![](doc/game.png)\n\n#### Link game from example site\n\n- Add `\u003cli\u003e\u003ca href=\"Game.html\"\u003eGame\u003c/a\u003e\u003c/li\u003e` to `views/Shared/_Layout.cshtml`.\n- Verify that the game can be opened from the menu \n\n#### Solution: `git checkout part1-add-game`\n\n#### Create High Score service\n\n- Create a directory called `Services`\n- In it, create the interface of a simple high score service, `IHighScoreService.cs`.\n- Implement it in a new file called `HighScoreService.cs` in the same directory.\n- Add it as a singleton to the service collection in `Startup.cs`\n- Create `HighScoreController`. Check that it works by visiting [http://localhost:5000/api/highscore](http://localhost:5000/api/highscore) (You might need to restart the server).\n- Verify the value is 0, posting a high score of 1 using _Postman_ and content type JSON, and verifying that the new high score is indeed one.\n\n![](doc/PostmanGet.PNG)\n![](doc/PostmanPost.png)\n\n#### Solution: `git checkout part1-highscore-service`\n\n#### Integrate High Score service\n\n- Create a new JS class in `high_score_manager.js` in `wwwroot/js`\n- The class should have a `setHighScore` and a `getHighScore` method which should use your highscore service to get and update the highscore\n- Add this file to `Game.html`'s script imports.\n- Add `HighScoreManager` to `application.js`.\n- Use the `HighScoreManager` in `game_manager.js`.\n- Verify that `scores/highScore.json` is written to disk in the _bin-folder_.\n\n#### Solution: `git checkout part1-highscore-integration`\n\n## Part 2: Local machine ain't good enough\n\nNow we'll host the webapp in a Docker container\n\n### First Docker try\n\n- Update `Program.cs` and add `.UseUrls(\"http://*:5000\")`.\n- Create a `Dockerfile` to build and then contain the application\n- Create a `.dockerignore` file to copy the minimum needed files to the build context\n- Run `docker build -t dips/workshop .` to build the Docker image. The `-t` switch tells Docker to associate the *tag* `dips/workshop` with the newly built image. \n- Run `docker run -p 5000:5000 dips/workshop` to start the new container with our app. The `-p` switch tells Docker to connect one of our computers's (the *host*) network ports to one on our new container.\n- Verify that the app works in the browser.\n- By default, Docker containers will run in the background even if we interrupt them with Ctrl+C - `docker ps` gives us a list of the currently running containers, and `docker kill \u003cid\u003e` can shut down a container. \n\n### Adding mounting to preserve high score\n\n- Run `docker run -p 5000:5000 -v \u003cPath-To-Project\u003e/scores:/app/scores dips/workshop`\n- For Windows, you need to enable drive sharing in Docker settings. If you're using Docker Toolbox on Windows, this path has to be inside `C:\\Users`.\n\n#### Solution: `git checkout part2-containerize`\n\n## Part 3: Divide and conquer\n\nWe don't want the website and API in the same app. Let's fix this.\n\n### Create a new folder for the web project\n\n- Create a new folder called `web` in the root folder.\n- Move all files and folders to `web`, except `LICENSE` and `readme.md`.\n- Recreate `.dockerignore` if it got lost in the moving process\n\n#### Solution: `git checkout part3-move-app`\n\n### Move high score to own app\n\n- Create a new folder called `api` in the root folder.\n- Navigate into the `api` folder and create a new app `dotnet new webapi --no-https`\n- Open the project in VS Code `code .`\n- Update `Program.cs` and add `.UseUrls(\"http://*:5000\")`.\n- Move `IHighScoreService`, `HighScoreService` and `HighScoreController` to the new app.\n- Move `services.AddSingleton\u003cIHighScoreService, HighScoreService\u003e();` to the new `Startup`.\n- Remove `using Workshop.Services;` from the original `Startup`.\n- Verify that the new service works using Postman.\n\n#### Solution: `git checkout part3-add-api`\n\n### Create another Docker container\n\nAll commands here are called from the `api`-folder.\n\n- Create a `Dockerfile` to build and then contain the application\n- Create a `.dockerignore` file to copy the minimum needed files to the build context\n- In `Startup.cs`, add `services.AddCors();` to `public void ConfigureServices(IServiceCollection services)` and `app.UseCors(builder =\u003e builder.WithOrigins(\"http://localhost\").AllowAnyMethod().AllowAnyHeader().AllowCredentials());` to `public void Configure(IApplicationBuilder app, IHostingEnvironment env)` before `app.UseMvc();`.\n- Run `docker build -t dips/api .` to build the docker image\n- Run `docker run -p 5000:5000 -v \u003cPath-To-Project\u003e/scores:/app/scores dips/api` to run the app through the container\n- Verify it works using Postman.\n\n#### Solution: `git checkout part3-containerize`\n\n### Make them work together\n\n- Create a new file, `docker-compose.yml`, in the root folder.\n- Navigate to the `web` folder and run `docker build -t dips/workshop .`\n- Navigate back to the root folder and run `docker-compose up`.\n- Enjoy your apps working together.\n\n#### Solution: `git checkout part4-compose`\n\nCongratulations! You now have a multi-container web application orchestrated by Docker Compose. Note how the app's concerns have been separated across the containers: so long as the API is available, our game doesn't care about *how* scores are stored. If we wanted, we could implement new high-score service backed by a proper database, and transparently replace the old service on-the-fly.\n\n### Extra credit\n\n- Right now, we have to manually set up CORS headers to allow the user to submit high scores, due to the [same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy). A far better way of handling this is to set up a *reverse proxy* - like [Traefik](https://traefik.io/) or [Nginx](https://www.nginx.com/) - to handle user requests and pass them on to the correct container. Add a reverse proxy to your application to intercept requests to `api/highscore` and pass them to the `api` container.\n- While Docker gives you command-line tools to manage running containers, they can get a bit hairy if you're running lots of applications with multiple containers. To make your life a bit easier, there are services which provide GUIs for container management, such as [Portainer](https://github.com/portainer/portainer). Extend your `docker-compose.yml` file with a Portainer service to manage the containers from your browser.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhjerpbakk%2Faspnetcoreworkshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhjerpbakk%2Faspnetcoreworkshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhjerpbakk%2Faspnetcoreworkshop/lists"}