<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arjav Dave</title>
    <description>The latest articles on DEV Community by Arjav Dave (@arjavdave).</description>
    <link>https://dev.to/arjavdave</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F597384%2F27651164-173e-4cf7-adfe-974e3662ec99.png</url>
      <title>DEV Community: Arjav Dave</title>
      <link>https://dev.to/arjavdave</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arjavdave"/>
    <language>en</language>
    <item>
      <title>How To Debug Node JS Inside Docker?</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Thu, 05 May 2022 05:35:00 +0000</pubDate>
      <link>https://dev.to/arjavdave/how-to-debug-node-js-inside-docker-31b0</link>
      <guid>https://dev.to/arjavdave/how-to-debug-node-js-inside-docker-31b0</guid>
      <description>&lt;h2&gt;
  
  
  What is a Debugger?
&lt;/h2&gt;

&lt;p&gt;For any developer, the debugger is the best friend. One can easily find bugs in software with a debugger.&lt;/p&gt;

&lt;p&gt;One can add a breakpoint to pause execution. Secondly, one can also add logic to a breakpoint to halt the execution. As an example, consider a &lt;code&gt;for&lt;/code&gt; loop having 1,000 iterations. The execution should stop when the iteration count reaches above 100. To do so, put a breakpoint on the &lt;code&gt;for&lt;/code&gt; loop. Next, add the logic to halt the execution when the iteration goes above 100.&lt;/p&gt;

&lt;p&gt;Besides halting a program, debuggers show memory allocations. For example, halting the execution will show memory consumed at any given point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Remote Debugger?
&lt;/h2&gt;

&lt;p&gt;Debugging is usually done on a localhost. Doing it remotely is called remote debugging :). That is, if you debug software running on a remote host, its called remote debugging. It is helpful for multiple reasons.&lt;/p&gt;

&lt;p&gt;For one, one can debug software locally. Consider a scenario where software is on the cloud. It might be deployed either for dev, UAT, or production. Now an issue happens on the cloud but not on the localhost. In this case, it would be very helpful to connect to the cloud and attach the debugger to the process. One can execute the software line by line to evaluate the issue and fix it.&lt;/p&gt;

&lt;p&gt;Secondly, remote debugging is also useful when the software is running inside a container. Let’s say a project is running inside Docker. One won’t be directly able to run the project and connect to it via the debugger. Instead, the docker container should expose its container port. Secondly, the remote debugger needs configuration to connect the project inside the docker container.&lt;/p&gt;

&lt;p&gt;Docker helps create portable containers that are fast and easy to deploy on various machines. These containers can be run locally on your Windows, Mac &amp;amp; Linux. Also, major cloud systems like AWS or Azure do support them out of the box. If you want to learn more Docker basics and need a cheat sheet for Docker CLI, &lt;a href="https://betterprogramming.pub/a-beginners-cheat-sheet-for-docker-f5024fd6c17f"&gt;here&lt;/a&gt; is an introductory article about it.&lt;/p&gt;

&lt;p&gt;In this article, we will set up a NodeJS project to run inside a docker container. We will also set up a remote debugging for the project.&lt;/p&gt;

&lt;p&gt;If you love this article so far, please &lt;a href="https://blog.royalecheese.com/"&gt;follow me&lt;/a&gt; and do check out other such awesome articles on my profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before we move further, the system should have docker desktop and VS Code installed. Other than that, no other requirements are there.&lt;/p&gt;

&lt;p&gt;For the hasty ones, I have made the source code available as a repository. You can check it out &lt;a href="https://github.com/shenanigan/docker-node-debug"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Project Files
&lt;/h3&gt;

&lt;p&gt;We are going to create a very simple express Node JS project. It will simply return a static JSON string on opening a specific URL. For this, we will create a file named &lt;code&gt;server.js&lt;/code&gt;, which is the entry point to our project.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;server.js&lt;/code&gt; file with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const server = require("express")();
server.listen(3000, async () =&amp;gt; { });
server.get("/node-app", async (_, response) =&amp;gt; {
    response.json({ "node": "app" });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;server.js&lt;/code&gt; file states that display &lt;code&gt;{“node”: “app”}&lt;/code&gt; on opening &lt;code&gt;http://localhost:3000/node-app&lt;/code&gt; URL in the browser.&lt;/p&gt;

&lt;p&gt;Secondly, we will need a &lt;code&gt;package.json&lt;/code&gt; file to configure the project and add dependencies. For that, create a &lt;code&gt;package.json&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "node-app",
    "dependencies": {
        "express": "^4.17.1"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the &lt;code&gt;npm install&lt;/code&gt; command to install the dependencies locally. This will create a &lt;code&gt;node_modules&lt;/code&gt; in the project directory.&lt;/p&gt;

&lt;p&gt;Even though we will be running the project inside a container, the dependencies need to be installed. It is needed since we will be mapping our current project directory to a container project directory. It is explained below how to do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running as Docker Container
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;Dockerfile&lt;/code&gt; is needed to run the project as a docker container. Create a &lt;code&gt;Dockerfile&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Download the slim version of node
FROM node:17-slim
# Needed for monitoring any file changes
RUN npm install -g nodemon
# Set the work directory to app folder. 
# We will be copying our code here
WORKDIR /node
#Copy all files from current directory to the container
COPY . .
# Needed for production. Check comments below
RUN npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the project is set up to run as a simple node server without allowing any breakpoints. The container will be running the project out of a node directory inside the container. nodemon is installed globally in the container. It’s needed for watching any file change in the directory. It is explained in detail below.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;RUN npm install&lt;/code&gt; command is needed only when deploying to production. We will map the &lt;code&gt;/node&lt;/code&gt; directory of our container to the current project directory on localhost using Docker Compose (next section). But when the app is deployed on the container, it needs to install the dependencies on its own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Ignore
&lt;/h3&gt;

&lt;p&gt;The Docker ignore feature is very much similar to git ignore. &lt;code&gt;.gitignore&lt;/code&gt; doesn’t track the files or folders mentioned in it. Similarly, we don’t want to copy unnecessary files in the container, which takes up space.&lt;/p&gt;

&lt;p&gt;In our case, we don’t want to copy the node_modules folder to the container. To do so, create a &lt;code&gt;.dockerignore&lt;/code&gt; file in the project directory with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;

&lt;p&gt;Docker Compose is a really helpful way to build and run docker containers with a single command. It is also helpful for running multiple containers at the same time. It is one of the reasons we use docker compose instead of plain docker. To know more about docker compose and how to run multiple containers, please visit the article Run Multiple Containers With Docker Compose.&lt;/p&gt;

&lt;p&gt;Now, let’s create a &lt;code&gt;docker-compose.yml&lt;/code&gt; file to add some more configurations. Add the below contents to &lt;code&gt;docker-compose.yml&lt;/code&gt; file once created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.4'
services:
  node-app:
    # 1. build the current directory
    build: .
    # 2. Run the project using nodemon, for monitoring file changes
    # Run the debugger on 9229 port
    command: nodemon --inspect=0.0.0.0:9229 /node/server.js 3000
    volumes:
      # 3. Bind the current directory on local machine with /node inside the container.
      - .:/node
    ports:
      # 4. map the 3000 and 9229 ports of container and host
      - "3000:3000"
      - "9229:9229"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker-compose.yml&lt;/code&gt; file is explained point-wise below.&lt;/p&gt;

&lt;p&gt;Point to our current directory for building the project.&lt;br&gt;
Run the project using nodemon, since if there are any changes in the local directory, we want to restart the project in the docker with the changes. Nodemon is a utility that will monitor for any changes in your source and automatically restart your server.&lt;br&gt;
Bind our current directory to the &lt;code&gt;/node&lt;/code&gt; directory using volumes.&lt;/p&gt;

&lt;p&gt;In addition to exposing and binding the 3000 port for the server, expose the 9229 for attaching the debugger.&lt;/p&gt;

&lt;p&gt;Use the above &lt;code&gt;docker-compose.yml&lt;/code&gt; file only for debugging.&lt;/p&gt;

&lt;p&gt;The above &lt;code&gt;docker-compose.yml&lt;/code&gt; exposes the debug port. In addition, it also monitors for any file changes inside the container (which are not going to happen). Lastly, it maps the volumes of the container to the project directory.&lt;/p&gt;

&lt;p&gt;For production, create a new file &lt;code&gt;docker-compose-prod.yml&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.4'
services:
  node-app:
    build: .
    command: node /node/server.js 3000
    ports:
      - "3000:3000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It simply runs the project and exposes the 3000 port. We are using multiple docker compose files to manage separate environments. Check the Running a Project section below to understand how to run a project based on different docker compose files.&lt;/p&gt;

&lt;p&gt;Before we can run the project, we still need to configure the debugger to connect to the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure a Remote Debugger
&lt;/h3&gt;

&lt;p&gt;First, check if you have &lt;code&gt;launch.json&lt;/code&gt; file created in your project. &lt;code&gt;launch.json&lt;/code&gt; defines different types of configurations we can run for debugging. If it is not created, visit the &lt;code&gt;RUN AND DEBUG&lt;/code&gt; tab on the left in your VS Code, as seen in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5QZwfAWa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1651728153089/qyvgaJ_ng.png%2520align%3D%2522left%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5QZwfAWa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1651728153089/qyvgaJ_ng.png%2520align%3D%2522left%2522" alt="Screenshot 2022-05-02 at 4.57.08 PM.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the text that says create a &lt;code&gt;launch.json&lt;/code&gt; file. Before you can proceed, it will ask the type of application to proceed. Select &lt;code&gt;Node.js&lt;/code&gt;. It will create a new &lt;code&gt;launch.json&lt;/code&gt; file in your project with a default Node.js configuration added.&lt;/p&gt;

&lt;p&gt;Since we are not going to run the node application locally, go ahead and delete that configuration. Instead, replace the launch.json file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "version": "0.2.0",
    "configurations": [
        {
            // 1. Type of application to attach to
            "type": "node",

            // 2. Type of request. In this case 'attach'
            "request": "attach",
            // 3. Restart the debugger whenever it gets disconnected
            "restart": true,
            // 4. Port to connect to 
            "port": 9229,
            // 5. Name of the configuration
            "name": "Docker: Attach to Node",
            // 6. Connect to /node directory of docker
            "remoteRoot": "/node"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration added is pretty self-explanatory. Basically, we are asking the debugger to connect to a remote host with port number 9229. We are also requesting the debugger to restart whenever it gets disconnected to the host. By default, the debugger tries to connect on &lt;code&gt;http://localhost:9229/&lt;/code&gt;. But project is hosted inside the &lt;code&gt;/node&lt;/code&gt; directory in docker. To map &lt;code&gt;/node&lt;/code&gt;, the remoteRoot attribute is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Project
&lt;/h2&gt;

&lt;p&gt;That’s about it! Now, if you run docker compose up, your project will start running. For the first run, it will download some layers of the node slim SDK and then install nodemon inside the docker container. But, subsequent runs would be much faster. Running docker compose up will show the following output in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to attach the debugger, run the Docker: Attach to Node task from the &lt;code&gt;RUN AND DEBUG&lt;/code&gt; tab. The debugger will now attach to the &lt;code&gt;/node&lt;/code&gt; directory of your docker container. Next, put a breakpoint on line 4 of your &lt;code&gt;server.js&lt;/code&gt; file, i.e., &lt;code&gt;response.json({ “super”: “app1” });&lt;/code&gt;. Finally, open your browser and hit &lt;code&gt;http://localhost:3000&lt;/code&gt;. The breakpoint will be hit, and the execution will halt.&lt;/p&gt;

&lt;p&gt;For production, we need to use the &lt;code&gt;docker-compose-prod.yml&lt;/code&gt; file. To do so, we need to mention the file name in the docker command. Execute the following command to run the project as if in a production environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose -f docker-compose-prod.yml up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above command, a debugger cannot be attached to the container since we are not exposing any debugging point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/shenanigan/docker-node-debug"&gt;link&lt;/a&gt; to the final source code of the project we have created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Debugging is one of the best things for development. It’s the cherry on top when we are able to debug remotely. Remote debugging enables us to connect to code running not only on the cloud but also to a docker container running locally.&lt;/p&gt;

&lt;p&gt;I hope you have enjoyed this article. Feel free to check out some of my other articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://betterprogramming.pub/a-beginners-cheat-sheet-for-docker-f5024fd6c17f"&gt;Docker: An introduction and cheat sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://betterprogramming.pub/run-multiple-containers-with-docker-compose-9297957f7a3c"&gt;Running multiple containers with Docker Compose&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops"&gt;Setup CI/CD for Android with Azure Pipelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>docker</category>
      <category>node</category>
    </item>
    <item>
      <title>Lazy Loading in Angular - A Beginner’s Guide</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Wed, 28 Apr 2021 07:17:00 +0000</pubDate>
      <link>https://dev.to/itnext/lazy-loading-in-angular-a-beginner-s-guide-57ln</link>
      <guid>https://dev.to/itnext/lazy-loading-in-angular-a-beginner-s-guide-57ln</guid>
      <description>&lt;h2&gt;What is Lazy Loading?&lt;/h2&gt;

&lt;p&gt;Lazy loading is a process of loading components, modules or other assets of a website as required. Since, Angular creates a &lt;a href="https://en.wikipedia.org/wiki/Single-page_application#:~:text=From%20Wikipedia%2C%20the%20free%20encyclopedia,browser%20loading%20entire%20new%20pages." rel="noreferrer noopener"&gt;SPA (Single Page Application)&lt;/a&gt;, all of its components are loaded, at once. Secondly, a lot of unnecessary libraries or modules might be loaded as well.&lt;/p&gt;

&lt;p&gt;For a small application it would be okay, but as the application grows the load time will increase, if everything is loaded at once. Lazy loading allows Angular to load components and modules as and when needed.&lt;/p&gt;

&lt;p&gt;First of all, to understand how lazy loading works in Angular, we need to understand the basic building blocks of the framework: NgModules.&lt;/p&gt;

&lt;p&gt;In order to understand how Lazy Loading works we first need to understand the building block of Angular: NgModules.&lt;/p&gt;

&lt;h2&gt;What are NgModules?&lt;/h2&gt;

&lt;p&gt;Angular libraries like RouterModule, BrowserModule, FormsModule are NgModules. Angular Material, which is a 3rd party, is also a type of NgModule. NgModule consists of files &amp;amp; code related to a specific domain or having a similar set of functionalities.&lt;/p&gt;

&lt;p&gt;A typical NgModule file declares components, directives, pipes, and services. It can also import other modules that are needed in the current module.&lt;/p&gt;

&lt;p&gt;One of the important advantage of NgModules is they can be lazy loaded.  Let's have a look at how we can configure lazy loading.&lt;/p&gt;

&lt;h2&gt;How to Create NgModules&lt;/h2&gt;

&lt;p&gt;In this tutorial, we will create two modules &lt;em&gt;Module&lt;/em&gt; &lt;em&gt;A&lt;/em&gt; and &lt;em&gt;Module B&lt;/em&gt; which will be lazy loaded. On the main screen we will have two buttons for loading each module lazily.&lt;/p&gt;

&lt;h4&gt;Create a Project&lt;/h4&gt;

&lt;p&gt;Create a new Angular project &lt;em&gt;lazy-load-demo&lt;/em&gt; by executing the below command.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new lazy-load-demo --routing --strict --style css
code lazy-load-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are creating a new project with routing. Secondly, the &lt;a href="https://angular.io/guide/strict-mode" rel="noreferrer noopener"&gt;strict mode&lt;/a&gt; is enabled. Lastly, we are mentioning the stylesheet format to css. The second command opens your project in VS Code. &lt;/p&gt;

&lt;h4&gt;Root Module&lt;/h4&gt;

&lt;p&gt;By default, a root module or app module is created under &lt;em&gt;/src/app&lt;/em&gt;. Below is the NgModule file created:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we are importing all the required modules and components.&lt;/p&gt;

&lt;p&gt;After that, &lt;em&gt;&lt;strong&gt;@NgModule&lt;/strong&gt;&lt;/em&gt; decorator states that AppModule class is a type of NgModule. The decorator accepts &lt;em&gt;declarations, imports, providers, and bootstrap. &lt;/em&gt;Here are the descriptions for each of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;declarations&lt;/em&gt;&lt;/strong&gt;: The components in this module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;imports&lt;/em&gt;&lt;/strong&gt;: The modules that are required by the current module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;providers&lt;/em&gt;&lt;/strong&gt;: The service providers if any.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;bootstrap&lt;/em&gt;&lt;/strong&gt;: The &lt;em&gt;root&lt;/em&gt; component that Angular creates and inserts into the &lt;code&gt;index.html&lt;/code&gt; host web page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Main screen&lt;/h4&gt;

&lt;p&gt;The main screen will have 2 buttons, namely, &lt;strong&gt;&lt;em&gt;Load Module A &lt;/em&gt;&lt;/strong&gt;&amp;amp; &lt;strong&gt;&lt;em&gt;Load Module B.&lt;/em&gt;&lt;/strong&gt; As the name suggests, clicking these buttons will lazily load each module. &lt;/p&gt;

&lt;p&gt;For that, replace your &lt;em&gt;app.component.html&lt;/em&gt; file with the contents below:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button style="padding: 20px; color: white; background-color: green;" routerLink="a"&amp;gt;Load Module A&amp;lt;/button&amp;gt;
&amp;lt;button style="padding: 20px; color: white; background-color: blue;" routerLink="b"&amp;gt;Load Module B&amp;lt;/button&amp;gt;
&amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's define the modules for routes &lt;em&gt;a &lt;/em&gt;&amp;amp; &lt;em&gt;b&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;Lazy Loaded Modules&lt;/h4&gt;

&lt;p&gt;In order to create lazy loaded modules execute the below commands.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng generate module modulea --route a --module app.module
ng generate module moduleb --route b --module app.module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commands will generate two folders &lt;em&gt;&lt;strong&gt;modulea&lt;/strong&gt;&lt;/em&gt; &amp;amp; &lt;strong&gt;&lt;em&gt;moduleb&lt;/em&gt;&lt;/strong&gt;. Subsequently, each folder will contain their own &lt;em&gt;module.ts&lt;/em&gt;,&lt;em&gt; routing.ts &lt;/em&gt;and &lt;em&gt;component&lt;/em&gt; files.&lt;/p&gt;

&lt;p&gt;If you check your &lt;em&gt;app-routing.module.ts&lt;/em&gt; you will see the below code for routes.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
  { path: 'a', loadChildren: () =&amp;gt; import('./modulea/modulea.module').then(m =&amp;gt; m.ModuleaModule) },
  { path: 'b', loadChildren: () =&amp;gt; import('./moduleb/moduleb.module').then(m =&amp;gt; m.ModulebModule) }
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It implies that when route &lt;em&gt;a&lt;/em&gt; or &lt;em&gt;b &lt;/em&gt;is visited load their respective modules lazily. &lt;/p&gt;

&lt;p&gt;On running the project with &lt;strong&gt;&lt;em&gt;ng serve&lt;/em&gt;&lt;/strong&gt;, you will see the below screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LIbbEMrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.18.55-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LIbbEMrT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.18.55-PM.png" alt="" width="578" height="280"&gt;&lt;/a&gt;Home Page &lt;/p&gt;

&lt;p&gt;On clicking &lt;em&gt;Load Module A&lt;/em&gt; button, you will be routed to page &lt;em&gt;a&lt;/em&gt;. This is how your screen should look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1FzThyon--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.18.14-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1FzThyon--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.18.14-PM.png" alt="" width="644" height="326"&gt;&lt;/a&gt;Lazily loaded Module A&lt;/p&gt;

&lt;p&gt;You should see a similar screen that says &lt;strong&gt;&lt;em&gt;moduleb works! &lt;/em&gt;&lt;/strong&gt;when clicked on &lt;em&gt;Load Module B.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;How to Verify that the Lazy Loading Worked&lt;/h2&gt;

&lt;p&gt;In order to verify the files loaded, open the developer tools by pressing F12. After that, visit the &lt;em&gt;Network&lt;/em&gt; tab as you can see in the screenshot below. When you refresh the page it will show few files requested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T5kLG29_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Network-Tab-1024x601.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T5kLG29_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Network-Tab-1024x601.jpg" alt="" width="880" height="516"&gt;&lt;/a&gt;Network Tab&lt;/p&gt;

&lt;p&gt;Go ahead and clear your list of requests by hitting the clear button as shown in the image on the right&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1OORoIBb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.42.21-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1OORoIBb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.42.21-PM.png" alt="" width="320" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click on the &lt;em&gt;Load Module A&lt;/em&gt;, you will see a request for &lt;em&gt;modulea-modulea-module.js&lt;/em&gt; as in the screenshot below. This verifies that the &lt;em&gt;Module A &lt;/em&gt;was lazily loaded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gd7g6C3n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.46.50-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gd7g6C3n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.46.50-PM.png" alt="" width="558" height="228"&gt;&lt;/a&gt;Module A Loaded&lt;/p&gt;

&lt;p&gt;Similarly, when you click &lt;em&gt;Load Module B&lt;/em&gt;, the &lt;em&gt;moduleb-moduleb-module.js&lt;/em&gt; file is loaded. Hence, verifying that Module B was loaded lazily.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6uMhJWXg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.47.10-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6uMhJWXg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/Screenshot-2021-04-25-at-11.47.10-PM.png" alt="" width="602" height="268"&gt;&lt;/a&gt;Module B Loaded&lt;/p&gt;

&lt;h2&gt;Use Cases&lt;/h2&gt;

&lt;p&gt;As we have seen, it’s very easy to create lazy loading modules. There are lots of use cases where they are useful, such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a separate module for pre-login vs post-login screens.&lt;/li&gt;
&lt;li&gt;For an e-commerce website, vendor facing vs customer facing screens can belong to separate modules. You can also create a separate module for payment.&lt;/li&gt;
&lt;li&gt;A separate CommonModule which contains shared components, directives, or pipelines is usually created. Directives like &lt;em&gt;Copy Code&lt;/em&gt; button, components like &lt;em&gt;up vote/down vote &lt;/em&gt;are usually included in a common module.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;For smaller websites, it might not matter much that all the modules are loaded at once. But, as the site grows it's very effective to have separate modules which are loaded as needed.&lt;/p&gt;

&lt;p&gt;Because of lazy loading, load time for the websites can be reduced drastically. This is specially helpful when you are trying to rank higher in SEO's. Even if not, less loading times means better user experience.&lt;/p&gt;

&lt;p&gt;Are you interested in more articles? Check these out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/learn-test-driven-development-with-integration-tests-in-net-5-0"&gt;Learn TDD with Integration Tests in .NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/net-5-how-to-authenticate-authorise-api-s-correctly"&gt;How to authenticate &amp;amp; authorise API’s correctly in .NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/azure-functions-wkhtmltopdf-convert-html-to-pdf"&gt;Azure Functions &amp;amp; wkhtmltopdf: Convert HTML to PDF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>performance</category>
    </item>
    <item>
      <title>5 Ways to Increase Your Efficiency as a Developer</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Thu, 22 Apr 2021 13:38:00 +0000</pubDate>
      <link>https://dev.to/itnext/5-ways-to-increase-your-efficiency-as-a-developer-265e</link>
      <guid>https://dev.to/itnext/5-ways-to-increase-your-efficiency-as-a-developer-265e</guid>
      <description>&lt;p&gt;Like any other field, the efficiency of a developer varies based on several factors: work culture, management, personal life, skills, and so on. Some we can control, and some we cannot.&lt;/p&gt;

&lt;p&gt;In this article, we will discuss how to make oneself better by working on things we control. There are some clichéd tips like &lt;em&gt;Stay Focused, Avoid Distractions &lt;/em&gt;and&lt;em&gt; Be in the FLOW&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But we are going to get a bit more technical on increasing the efficiency.&lt;/p&gt;

&lt;h2&gt;Journey of a Software Developer&lt;/h2&gt;

&lt;p&gt;The usual stages of a developer are shown below:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Coder -&amp;gt; Programmer -&amp;gt; Architect&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Coder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many people use the words “coder” and “programmer” interchangeably. But, there is a difference between the two. A coder is someone who knows how to write the code or a script. Optimisation and architecture are not their priority. Rather, they focus on writing code that just works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Programmer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A programmer is a superset of a coder. They know how to write code, but in addition, they also write it with great optimisation and robustness. Secondly, time and memory complexities are kept in mind when writing code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As you might have guessed, the architect or the software architect, to be precise, is a superset of a programmer. The architect is responsible for not only a piece of code but also how these pieces fit together.&lt;/p&gt;

&lt;h2&gt;Coder vs. Programmer vs. Architect: An Example&lt;/h2&gt;

&lt;p&gt;A great example would be to ask these three people to “Create tic-tac-toe.”&lt;/p&gt;

&lt;p&gt;When asking the coders, they will start coding, assuming it's a 3x3 tic-tac-toe with two players. Secondly, they might write nested &lt;em&gt;for &lt;/em&gt;loops as a brute force approach to figure out who won the match. There might also be no design patterns in place like MVC, MVVM, or others.&lt;/p&gt;

&lt;p&gt;However, the programmer will first understand the requirements completely and then start on the coding part. They will create optimised algorithms for maintaining the state of the players and a scalable way to change the grid dimensions.&lt;/p&gt;

&lt;p&gt;The architect will select which technology is best for designing the game. A few other questions come into play: Is it going to be a web-based or mobile app? Will an API be required or just played locally? In addition, they will also decide on which design patterns to implement. Once the architecture is defined, only then will the actual implementation start.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The goal of each developer should be to become a great architect along with having great technical skills.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Here are a few ways to improve your efficiency and skills:&lt;/p&gt;

&lt;h2&gt;1. Pen and Paper&lt;/h2&gt;

&lt;p&gt;Many people find it offensive to give a pen and paper coding interview. In my opinion, it is one of the best tools to get clarity. Jot down the things that you want to achieve and how you will achieve them.&lt;/p&gt;

&lt;p&gt;In the tic-tac-toe example above, you can write down that there will be models like Player and Board. Secondly, a game engine will be required to calculate the result after each move. Also, write down what each class will contain, e.g., Player will have a name and symbol property.&lt;/p&gt;

&lt;p&gt;You get the idea. Figure out your design patterns. What you are trying to achieve in code, achieve it with pen and paper first.&lt;/p&gt;

&lt;h2&gt;2. Become Your Own Manager&lt;/h2&gt;

&lt;p&gt;At the start of the day, create a TODO list of your tasks, e.g., you will complete the game engine today, or you will complete the board UI today, etc.&lt;/p&gt;

&lt;p&gt;Try to break it down into smaller tasks of 3–4 hours each. Having clear goals for the day keeps you motivated. Secondly, it gives a sense of satisfaction when the tasks are completed; it boosts morale.&lt;/p&gt;

&lt;p&gt;Essentially, it’s a variant of the &lt;a href="https://www.atlassian.com/agile/kanban#:~:text=Kanban%20is%20a%20popular%20framework,of%20work%20at%20any%20time." rel="noopener noreferrer"&gt;Kanban&lt;/a&gt; management style. But, I have found it to be quite effective.&lt;/p&gt;

&lt;h2&gt;3. Documentation&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F0%2Aic31iXHxVYtLUjWo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1400%2F0%2Aic31iXHxVYtLUjWo.png" alt=""&gt;&lt;/a&gt;&lt;em&gt;&lt;span&gt;Reference: &lt;a href="https://www.reddit.com/r/ProgrammerHumor/comments/ijoxq6/why_read_documentation/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/ProgrammerHumor/comments/ijoxq6/why_read_documentation/&lt;/a&gt;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the reasons for taking time to get things right is a lack of knowledge.&lt;/p&gt;

&lt;p&gt;Astonishingly, there are many developers who just assume how the system or library works. They spend an enormous amount of time figuring out by themselves how it works without reading the documentation. This leads to a loss in efficiency.&lt;/p&gt;

&lt;p&gt;A simple example I personally encountered was writing blogs using the Markdown syntax. I know most of the basic syntax, but I was unaware of some of the advanced ones. I wasted around 5–10 minutes trying to figure it out. If I had gone to the documentation, instead, it would have saved me time.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet" rel="noopener noreferrer"&gt;Markdown&lt;/a&gt; cheat sheet in case you are wondering.&lt;/p&gt;

&lt;h2&gt;4. Don’t Reinvent the Wheel&lt;/h2&gt;

&lt;p&gt;If something is already available in the market, with great reliability, use it. Usually speaking the pros will outweigh the cons.&lt;/p&gt;

&lt;p&gt;As an example, you should never write your own cryptographic function or library. There are pretty decent libraries available in almost all languages, and they will save you lots of time. More importantly, it will have the correct implementation, and it will be well optimised&lt;/p&gt;

&lt;p&gt;But don’t overdo it. You shouldn’t import &lt;a href="https://underscorejs.org/" rel="noopener noreferrer"&gt;underscore&lt;/a&gt; or &lt;a href="https://lodash.com/" rel="noopener noreferrer"&gt;lodash&lt;/a&gt; just to loop through an array. It only increases your package size and hurts the user experience.&lt;/p&gt;

&lt;h2&gt;5. Testing&lt;/h2&gt;

&lt;p&gt;Test-Driven Development (TDD) is another methodology that is going strong. Writing tests is as important as writing code. Though manual testing is required, automated tests provide confidence that your system will not break logically.&lt;/p&gt;

&lt;p&gt;Initially, it might feel that you are spending more time writing tests. But as the project grows, it would be worth it. Fewer bugs and more robustness.&lt;/p&gt;

&lt;p&gt;Here is an article on &lt;a href="https://www.daveops.co.in/post/learn-test-driven-development-with-integration-tests-in-net-5-0" rel="noopener noreferrer"&gt;how to do TDD&lt;/a&gt; in .NET Core.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I, personally, felt a lot of different emotions when following the above tips, but they have improved my efficiency a lot, especially creating a to-do list for the day.&lt;/p&gt;

&lt;p&gt;Read more interesting articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/xcode-vs-android-studio-which-is-worse" rel="noopener noreferrer"&gt;XCode or Android Studio: Which is Better?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/continuous-integration-ci-cd-for-ios-part-1" rel="noopener noreferrer"&gt;Setup CI-CD for iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops" rel="noopener noreferrer"&gt;Setup CI-CD for Android&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.daveops.co.in/post/learn-test-driven-development-with-integration-tests-in-net-5-0" rel="noopener noreferrer"&gt;Learning Integration Tests in .NET with .TDD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devjournal</category>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>XCode vs Android Studio: Which is better?</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Tue, 20 Apr 2021 05:04:44 +0000</pubDate>
      <link>https://dev.to/itnext/xcode-vs-android-studio-which-is-worse-251m</link>
      <guid>https://dev.to/itnext/xcode-vs-android-studio-which-is-worse-251m</guid>
      <description>&lt;h2&gt;What makes me eligible to review!&lt;/h2&gt;

&lt;p&gt;I have been in the industry for more than 11+ years now. I started my career with BlackBerry (BB) Development. I feel old already!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Eclipse&lt;/em&gt;&lt;/strong&gt; was our best friend back then for mobile development. I did my internship working on the famous &lt;a href="https://zagat.com/" rel="noreferrer noopener"&gt;Zagat&lt;/a&gt; app for BlackBerry. It was overall a great learning experience.&lt;/p&gt;

&lt;p&gt;For my full-time job I switched to a start up named &lt;a href="https://www.linkedin.com/company/spinlet/" rel="noreferrer noopener"&gt;Spinlet&lt;/a&gt; which I hope is still going strong. I worked as BlackBerry developer in the beginning but switched to iOS development eventually.&lt;/p&gt;

&lt;p&gt;My iOS experience was nothing but exciting in those initial years. Eventually I started my own firm &lt;a href="https://royalecheese.com" rel="noreferrer noopener"&gt;Royale Cheese&lt;/a&gt; with a friend that provides mobile design &amp;amp; development. &lt;/p&gt;

&lt;p&gt;After around 4 years of iOS development I found my way into Android development. I have got a fair share of experience with Android development as well, around 3 years to be precise.&lt;/p&gt;

&lt;p&gt;We have since then been working on full stack. That's when I realised how horrible the mobile development tools are.&lt;/p&gt;

&lt;p&gt;Enough with the chit chat. Here's an honest review of the mobile development tools and technologies. &lt;/p&gt;

&lt;h2&gt;XCode&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---MUSt2J7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/xcode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---MUSt2J7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/xcode.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It used to be a good tool in the past. But it has become terrible lately. Here is a list of all the issues even after 15 years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-completion&lt;/strong&gt;: Firstly, who in the right mind would set Esc as the suggestions key. Secondly, the autocompletion doesn't work many a times or gives weird suggestions that are out of context. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Times&lt;/strong&gt;: It takes a lot of time to create an archive or to run on a device for the first time. It's best to &lt;a href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/" rel="noreferrer noopener"&gt;setup a CI/CD&lt;/a&gt; to archive and upload builds. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signing &amp;amp; Deployment:&lt;/strong&gt; With the latest version's it's getting easier. But, it's still confusing with the signing certificates and the provision profiles for someone who is a beginner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Hog&lt;/strong&gt;: Somehow XCode keeps on hogging memory. For every new device on which the build needs to run it occupies 3 GB. Archives take a huge chunk and so as the simulators. Overall it occupies around 50GB if I don't clean up regularly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updates&lt;/strong&gt;: Each update is around 10-12GB even the minor upgrades. What's worse is it requires more than 40GB of free space to get installed. Last but not the least, XCode takes around 12GB of the space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design&lt;/strong&gt;: Initially to design UI there was struts &amp;amp; springs, then came the Autolayout and now the SwiftUI. It is worrisome that the methodology keeps on changing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cocoapods&lt;/strong&gt;: is getting worse day by day because it's repo is getting so big. Secondly, it increases the build times by a lot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Camera&lt;/strong&gt;: Possibly due to hardware limitations camera was not supported in simulators. But, now it's been a while. If the location can be simulated why not the Camera?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Android Studio&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MaYra0EV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/android-studio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MaYra0EV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/04/android-studio.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I haven't got the chance to use it in the early days. But from what I have been seeing it wouldn't have been pretty. Here are some of the frustrating issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fragmentation&lt;/strong&gt;: I feel this might be on top of every ones list. Supporting the staggering amount of devices to support might just overwhelm anyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradle&lt;/strong&gt;: Oh My God! Gradle takes forever to run builds. There are optimisation's which can help alleviate the problem, but it still remains the problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAM Hogger&lt;/strong&gt;: With emulators and IDE running together, the combo requires around 10-12GB of RAM. That's way more than what their official documentation says: &lt;strong&gt;&lt;em&gt;4GB&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signing Keys&lt;/strong&gt;: You lose your signing keys and you can't upload to the same app again. You will have to create a new app and get the reviews and downloads again. There is some improvement in this area recently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDE: &lt;/strong&gt;I always felt Java based IDE's to be clumsy. That includes IntelliJ (on which Android Studio is based), Eclipse or NetBeans. It's responsiveness is not like other tools like XCode or VS Code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;XCode vs Android Studio Review&lt;/h2&gt;

&lt;p&gt;Even though it might feel Android has less issues it does have some serious issues. &lt;strong&gt;Fragmentation&lt;/strong&gt; and &lt;strong&gt;Gradle&lt;/strong&gt; might alone be enough to make Android look bad. &lt;/p&gt;

&lt;p&gt;Personally I prefer to work in XCode compared to Android since I own a Mac and probably I am more used to it. &lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Overall both platforms have a huge user base. But, I feel that these tools still have a long way to go. &lt;/p&gt;

&lt;p&gt;As an example I am absolutely in love how Microsoft has revamped dotnet to &lt;a href="https://dotnet.microsoft.com/download" rel="noreferrer noopener"&gt;dotnet core&lt;/a&gt; and their IDE to &lt;a href="https://code.visualstudio.com/" rel="noreferrer noopener"&gt;VS Code&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Jot down your frustrations in the comments below. &lt;/p&gt;

&lt;p&gt;Read some of my articles here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arjavdave.com/2021/03/11/continuous-integration-cicd-for-ios-on-azure-devops-part-1/" rel="noreferrer noopener"&gt;Setup CI-CD for iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arjavdave.com/2021/03/16/how-to-setup-ci-cd-pipelines-for-android-with-azure-devops/" rel="noreferrer noopener"&gt;Setup CI-CD for Android&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arjavdave.com/2021/04/14/learn-test-driven-development-with-integration-tests-in-net-5-0/" rel="noreferrer noopener"&gt;Learning Integration Tests in .NET with .TDD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arjavdave.com/2021/04/05/going-password-less-with-dotnet/" rel="noreferrer noopener"&gt;Provide a password-less to login&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>ios</category>
      <category>discuss</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Learn Test-Driven Development with Integration Tests in .NET 5.0</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Wed, 14 Apr 2021 18:00:20 +0000</pubDate>
      <link>https://dev.to/itnext/learn-tdd-with-integration-tests-in-net-5-0-31fg</link>
      <guid>https://dev.to/itnext/learn-tdd-with-integration-tests-in-net-5-0-31fg</guid>
      <description>&lt;p&gt;TDD (Test Driven Development) is a much debated word in the tech industry. Debates like &lt;em&gt;Whether you should do TDD or not?&lt;/em&gt; or &lt;em&gt;How advantageous is it?&lt;/em&gt;  are quite popular. Simply said, TDD is write tests before you develop. &lt;/p&gt;

&lt;p&gt;Now, there are a lot of school of thoughts regarding what type of test's are included and what are not in TDD. As an example, should it include Unit Test, Integration Test, System Test or even UAT? &lt;/p&gt;

&lt;p&gt;In this article, we will go through a real-world example on how to write integration tests in .NET 5.0 with TDD methodology.&lt;/p&gt;

&lt;h2&gt;Project Requirements&lt;/h2&gt;

&lt;p&gt;TDD requires a very clear understanding of scope of work. Without clarity, all the test cases might not be covered.&lt;/p&gt;

&lt;p&gt;Let's define the scope of work. We will be developing a &lt;em&gt;patient admission system&lt;/em&gt; for a Hospital.&lt;/p&gt;

&lt;h4&gt;Business Requirements&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A hospital has X ICU rooms, Y Premium rooms &amp;amp; Z General rooms.&lt;/li&gt;
&lt;li&gt;ICU &amp;amp; Premium rooms can have a single patient at a time, while General rooms can have 2 patients. Each room has a room number.&lt;/li&gt;
&lt;li&gt;On admitting, the patient has to provide name, age, gender &amp;amp; phone number. &lt;/li&gt;
&lt;li&gt;It is possible to search a patient via name or phone number.&lt;/li&gt;
&lt;li&gt;Same patient cannot be admitted to multiple beds while he is still checked in.&lt;/li&gt;
&lt;li&gt;A patient cannot be admitted if all the rooms are occupied.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Model Validation Rules&lt;/h4&gt;

&lt;p&gt;Based on the above requirements, there are 2 models namely Patient &amp;amp; Room. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A patient's age is between 0 &amp;amp; 150. The length of name should be between 2 and 40. Gender can be male, female &amp;amp; other. Phone Number's length should be between 7 and 12 and it should all be digits. &lt;/li&gt;
&lt;li&gt;Room type can be either "ICU", "Premium" or "General".&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Test Cases&lt;/h4&gt;

&lt;p&gt;Now, that we have defined rules &amp;amp; requirements, lets start creating test cases. Since it's a basic CRUD application we mostly have integration tests. &lt;/p&gt;

&lt;h6&gt;Patient&lt;/h6&gt;

&lt;ul&gt;
&lt;li&gt;Do all the model validation tests.&lt;/li&gt;
&lt;li&gt;Admit the same patient twice&lt;/li&gt;
&lt;li&gt;Check out the same patient twice. &lt;/li&gt;
&lt;li&gt;Admit the same patient to multiple rooms at the same time.&lt;/li&gt;
&lt;li&gt;Search a patient with phone number and name.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;TDD Setup&lt;/h2&gt;

&lt;p&gt;In the above section we gathered requirements. Secondly, we defined the models. Finally, we created the list of test cases which we will implement. &lt;/p&gt;

&lt;p&gt;Open your terminal and run the below script to create and setup a new project.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir TDD
cd TDD
dotnet new sln
dotnet new webapi --name TDD
dotnet new xunit --name TDD.Tests
cd TDD
dotnet add package Microsoft.EntityFrameworkCore --version 5.0.5
cd ../TDD.Tests
dotnet add reference ../TDD/TDD.csproj
dotnet add package Microsoft.EntityFrameworkCore --version 5.0.5
dotnet add package Microsoft.AspNetCore.Hosting --version 2.2.7
dotnet add package Microsoft.AspNetCore.Mvc.Testing --version 5.0.5
dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 5.0.5
cd ..
dotnet sln add TDD/TDD.csproj
dotnet sln add TDD.Tests/TDD.Tests.csproj
code .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above script creates a solution file named &lt;em&gt;TDD.sln&lt;/em&gt;. Secondly, we create 2 projects for TDD &amp;amp; TDD.Tests. Then we add the dependencies for each project. Lastly, we add the projects to the solution and open the project in VS Code. &lt;/p&gt;

&lt;p&gt;Before we start testing, some more setup is required. Basically, integration tests test the a specific module without mocking. So we will be mimicking our application via TestServer.&lt;/p&gt;

&lt;h4&gt;Custom WAF&lt;/h4&gt;

&lt;p&gt;In order to mimic the TestServer there is a class called &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactory-1?view=aspnetcore-5.0" rel="noreferrer noopener"&gt;WebApplicationFactory&lt;/a&gt; (WAF) which bootstraps the application in memory.&lt;/p&gt;

&lt;p&gt;In your &lt;strong&gt;&lt;em&gt;TDD.Tests&lt;/em&gt;&lt;/strong&gt; project create a file named &lt;em&gt;PatientTestsDbWAF.cs&lt;/em&gt; with the following code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore;


namespace TDD.Tests
{
    public class PatientTestsDbWAF&amp;lt;TStartup&amp;gt; : WebApplicationFactory&amp;lt;TStartup&amp;gt; where TStartup : class
    {

        protected override IWebHostBuilder CreateWebHostBuilder()
        {
            return WebHost.CreateDefaultBuilder()
                .UseStartup&amp;lt;TStartup&amp;gt;();
        }
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(async services =&amp;gt;
           {
               // Remove the app's DbContext registration.
               var descriptor = services.SingleOrDefault(
                      d =&amp;gt; d.ServiceType ==
                          typeof(DbContextOptions&amp;lt;DataContext&amp;gt;));

               if (descriptor != null)
               {
                   services.Remove(descriptor);
               }

               // Add DbContext using an in-memory database for testing.
               services.AddDbContext&amp;lt;DataContext&amp;gt;(options =&amp;gt;
                  {
                      // Use in memory db to not interfere with the original db.
                      options.UseInMemoryDatabase("PatientTestsTDD.db");
                  });
           });
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are removing the applications DbContext and adding an &lt;strong&gt;in memory&lt;/strong&gt; DbContext. It is a necessary step since we don't want to interfere with the original database. &lt;/p&gt;

&lt;p&gt;Secondly, we are initialising the database with some dummy data. &lt;/p&gt;

&lt;p&gt;Since, DataContext is a custom class, it will give compiler error. So, we need to create it.&lt;/p&gt;

&lt;h4&gt;Data Context&lt;/h4&gt;

&lt;p&gt;Therefore, in your &lt;strong&gt;TDD project&lt;/strong&gt;, create a file named &lt;em&gt;DataContext.cs&lt;/em&gt; with the following code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore;

namespace TDD
{
    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions options) : base(options) { }

        // For storing the list of patients and their state
        public DbSet&amp;lt;Patient&amp;gt; Patient { get; set; }

        // For the storying the rooms along with their types and capacity
        public DbSet&amp;lt;Room&amp;gt; Room { get; set; }

        // For logging which patients are currently admitted to which room
        public DbSet&amp;lt;RoomPatient&amp;gt; RoomPatient { get; set; }

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here Patient, Room &amp;amp; RoomPatient are Entity classes with the required properties, which we will create next.&lt;/p&gt;

&lt;h4&gt;Patient&lt;/h4&gt;

&lt;p&gt;Again, in your &lt;strong&gt;TDD project&lt;/strong&gt;, create a file named &lt;em&gt;Patient.cs&lt;/em&gt; and paste in the code below.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace TDD
{
    public class Patient
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public String Name { get; set; }

        public String PhoneNumber { get; set; }

        public int Age { get; set; }

        public String Gender { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;Room&lt;/h4&gt;

&lt;p&gt;Create another file named &lt;em&gt;Room.cs&lt;/em&gt; with the following code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace TDD
{
    public class Room
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public String RoomType { get; set; }

        public int CurrentCapacity { get; set; }

        public int MaxCapacity { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;RoomPatient&lt;/h4&gt;

&lt;p&gt;Create the last model file &lt;em&gt;RoomPatient.cs&lt;/em&gt; with the following code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace TDD
{
    public class RoomPatient
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required]
        public int RoomId { get; set; }

        [ForeignKey("RoomId")]
        public Room Room { get; set; }

        [Required]
        public int PatientId { get; set; }

        [ForeignKey("PatientId")]
        public Patient Patient { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you shouldn't be getting any compiler error.&lt;/p&gt;

&lt;p&gt;Lastly, remove the WeatherForecast.cs and WeatherForecastController.cs files. &lt;/p&gt;

&lt;p&gt;Go to your terminal in VS Code and run the below command.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd TDD.Tests
dotnet test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a nice green result which says 1 test passed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farjavdave.com%2Fwp-content%2Fuploads%2F2021%2F04%2FTest_Success_1-1024x211.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farjavdave.com%2Fwp-content%2Fuploads%2F2021%2F04%2FTest_Success_1-1024x211.png" alt=""&gt;&lt;/a&gt;Test Success&lt;/p&gt;

&lt;h4&gt;Patient Controller&lt;/h4&gt;

&lt;p&gt;Unfortunately dotnet doesn't provide a way to directly test the model's in itself. So, we will have to create a controller to test it. &lt;/p&gt;

&lt;p&gt;Go ahead and create &lt;em&gt;PatientController.cs&lt;/em&gt; in the Controllers folder in &lt;strong&gt;TDD project&lt;/strong&gt; with the below code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc;

namespace TDD.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class PatientController : Controller
    {
        [HttpPost]
        public IActionResult AddPatient([FromBody] Patient Patient)
        {
            // TODO: Insert the patient into db
            return Created("/patient/1", Patient);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created an api to add a patient. In order to test our model we will call this api.&lt;/p&gt;

&lt;p&gt;That is all the things required to start testing.&lt;/p&gt;

&lt;h2&gt;Model Validation Tests&lt;/h2&gt;

&lt;p&gt;Since, we have setup the basic code for testing, let's write a test that fails. We will start our testing with the model validation tests.&lt;/p&gt;

&lt;h4&gt;Failing (Red) State&lt;/h4&gt;

&lt;p&gt;Let's create a new file named &lt;em&gt;PatientTests.cs&lt;/em&gt; in your &lt;strong&gt;TDD.Tests project&lt;/strong&gt; and delete the file named &lt;em&gt;UnitTest1.cs&lt;/em&gt;. Copy the below code in your file.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc.Testing;
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace TDD.Tests
{
    public class PatientTests : IClassFixture&amp;lt;PatientTestsDbWAF&amp;lt;Startup&amp;gt;&amp;gt;
    {
        // HttpClient to call our api's
        private readonly HttpClient httpClient;
        public WebApplicationFactory&amp;lt;Startup&amp;gt; _factory;

        public PatientTests(PatientTestsDbWAF&amp;lt;Startup&amp;gt; factory)
        {
            _factory = factory;

            // Initiate the HttpClient
            httpClient = _factory.CreateClient();
        }

        [Theory]
        [InlineData("Test Name 2", "1234567891", 20, "Male", HttpStatusCode.Created)]
        [InlineData("T", "1234567891", 20, "Male", HttpStatusCode.BadRequest)]
        [InlineData("A very very very very very very loooooooooong name", "1234567891", 20, "Male", HttpStatusCode.BadRequest)]
        [InlineData(null, "1234567890", 20, "Invalid Gender", HttpStatusCode.BadRequest)]
        [InlineData("Test Name", "InvalidNumber", 20, "Male", HttpStatusCode.BadRequest)]
        [InlineData("Test Name", "1234567890", -10, "Male", HttpStatusCode.BadRequest)]
        [InlineData("Test Name", "1234567890", 20, "Invalid Gender", HttpStatusCode.BadRequest)]
        [InlineData("Test Name", "12345678901234444", 20, "Invalid Gender", HttpStatusCode.BadRequest)]
        public async Task PatientTestsAsync(String Name, String PhoneNumber, int Age, String Gender, HttpStatusCode ResponseCode)
        {
            var scopeFactory = _factory.Services;
            using (var scope = scopeFactory.CreateScope())
            {
                var context = scope.ServiceProvider.GetService&amp;lt;DataContext&amp;gt;();

                // Initialize the database, so that 
                // changes made by other tests are reset. 
                await DBUtilities.InitializeDbForTestsAsync(context);

                // Arrange
                var request = new HttpRequestMessage(HttpMethod.Post, "api/patient");

                request.Content = new StringContent(JsonSerializer.Serialize(new Patient
                {
                    Name = Name,
                    PhoneNumber = PhoneNumber,
                    Age = Age,
                    Gender = Gender
                }), Encoding.UTF8, "application/json");

                // Act
                var response = await httpClient.SendAsync(request);

                // Assert
                var StatusCode = response.StatusCode;
                Assert.Equal(ResponseCode, StatusCode);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[Theory] attribute allows us to mention different parameters for our tests. Consequently, we don't have to write different tests for all the combinations.&lt;/p&gt;

&lt;p&gt;Also, DBUtilities is a utility class to reinitialise the database to it's initial state. This might seem trivial when we have 1 or 2 tests but, gets critical as we add more tests. &lt;/p&gt;

&lt;h4&gt;DBUtilities&lt;/h4&gt;

&lt;p&gt;The DBUtilities class will initialise your database with 1 patient and 3 different type of rooms. &lt;/p&gt;

&lt;p&gt;Create a file named &lt;em&gt;DBUtilities.cs&lt;/em&gt; in your &lt;strong&gt;&lt;em&gt;TDD.Tests&lt;/em&gt;&lt;/strong&gt; project with the below code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Threading.Tasks;

namespace TDD.Tests
{
    // Helps to initialise the database either from the WAF for the first time
    // Or before running each test.
    public class DBUtilities
    {

        // Clears the database and then,
        //Adds 1 Patient and 3 different types of rooms to the database
        public static async Task InitializeDbForTestsAsync(DataContext context)
        {
            context.RoomPatient.RemoveRange(context.RoomPatient);
            context.Patient.RemoveRange(context.Patient);
            context.Room.RemoveRange(context.Room);

            // Arrange
            var Patient = new Patient
            {
                Name = "Test Patient",
                PhoneNumber = "1234567890",
                Age = 20,
                Gender = "Male"
            };
            context.Patient.Add(Patient);

            var ICURoom = new Room
            {
                RoomType = "ICU",
                MaxCapacity = 1,
                CurrentCapacity = 1
            };
            context.Room.Add(ICURoom);

            var GeneralRoom = new Room
            {
                RoomType = "General",
                MaxCapacity = 2,
                CurrentCapacity = 2
            };
            context.Room.Add(GeneralRoom);

            var PremiumRoom = new Room
            {
                RoomType = "Premium",
                MaxCapacity = 1,
                CurrentCapacity = 1
            };
            context.Room.Add(PremiumRoom);

            await context.SaveChangesAsync();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and run the &lt;em&gt;dotnet test&lt;/em&gt; command again and you will see 1 passed and 4 failed tests. This is because the 4 tests were expecting BadRequest but getting a Created result. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farjavdave.com%2Fwp-content%2Fuploads%2F2021%2F04%2Ffailed_tests-1024x195.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farjavdave.com%2Fwp-content%2Fuploads%2F2021%2F04%2Ffailed_tests-1024x195.png" alt=""&gt;&lt;/a&gt;Failing (Red) State&lt;/p&gt;

&lt;p&gt;Let's fix it!&lt;/p&gt;

&lt;h4&gt;Success (Green) State&lt;/h4&gt;

&lt;p&gt;In order to fix these we need to add attributes to our &lt;em&gt;Patient.cs&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;Update the &lt;em&gt;Patient.cs&lt;/em&gt; file as below.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace TDD
{
    public class Patient : IValidatableObject
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required]
        [StringLength(40, MinimumLength = 2, ErrorMessage = "The name should be between 2 &amp;amp; 40 characters.")]
        public String Name { get; set; }

        [Required]
        [DataType(DataType.PhoneNumber)]
        [RegularExpression(@"^(\d{7,12})$", ErrorMessage = "Not a valid phone number")]
        public String PhoneNumber { get; set; }

        [Required]
        [Range(1, 150)]
        public int Age { get; set; }

        [Required]
        public String Gender { get; set; }

        public Boolean IsAdmitted { get; set; }

        public IEnumerable&amp;lt;ValidationResult&amp;gt; Validate(ValidationContext validationContext)
        {
            // Only Male, Female or Other gender are allowed
            if (Gender.Equals("Male", System.StringComparison.CurrentCultureIgnoreCase) == false &amp;amp;&amp;amp;
                Gender.Equals("Female", System.StringComparison.CurrentCultureIgnoreCase) == false &amp;amp;&amp;amp;
                Gender.Equals("Other", System.StringComparison.CurrentCultureIgnoreCase) == false)
            {
                yield return new ValidationResult("The gender can either be Male, Female or Other");
            }

            yield return ValidationResult.Success;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have added the required attributes. We have also implemented the &lt;em&gt;IValidatableObject&lt;/em&gt; interface so that we can verify the &lt;em&gt;Gender&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Time to run the &lt;em&gt;dotnet test&lt;/em&gt; command. You will see a nice green line saying 5 tests passed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farjavdave.com%2Fwp-content%2Fuploads%2F2021%2F04%2FTests_passed_2-1024x244.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farjavdave.com%2Fwp-content%2Fuploads%2F2021%2F04%2FTests_passed_2-1024x244.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add more edge case scenarios in the &lt;em&gt;InlineData&lt;/em&gt; to test the Patient model validation tests thoroughly.&lt;/p&gt;

&lt;h2&gt;Duplicate Patient Test&lt;/h2&gt;

&lt;p&gt;We shall now create a test which fails when we try to add a duplicate patient.&lt;/p&gt;

&lt;h4&gt;Failing (Red) Test&lt;/h4&gt;

&lt;p&gt;Create another test in your class &lt;em&gt;PatientTests. &lt;/em&gt;Add the below code.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Fact]
public async Task PatientDuplicationTestsAsync()
{
    var scopeFactory = _factory.Services;
    using (var scope = scopeFactory.CreateScope())
    {
        var context = scope.ServiceProvider.GetService&amp;lt;DataContext&amp;gt;();
        await DBUtilities.InitializeDbForTestsAsync(context);

        // Arrange
        var Patient = await context.Patient.FirstOrDefaultAsync();

        var Request = new HttpRequestMessage(HttpMethod.Post, "api/patient");
        Request.Content = new StringContent(JsonSerializer.Serialize(Patient), Encoding.UTF8, "application/json");

        // Act
        var Response = await httpClient.SendAsync(Request);

        // Assert
        var StatusCode = Response.StatusCode;
        Assert.Equal(HttpStatusCode.BadRequest, StatusCode);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have used a [Fact] attribute instead of [Theory] attribute here since we don't want to test the same method with different parameters. Instead, we want to make the same request twice.&lt;/p&gt;

&lt;p&gt;Run &lt;em&gt;&lt;strong&gt;dotnet test &lt;/strong&gt;&lt;/em&gt;to run our newly created test. The test will fail with message &lt;em&gt;Assert.Equal() Failure&lt;/em&gt;. Time to fix it.&lt;/p&gt;

&lt;h4&gt;Success (Green) Test&lt;/h4&gt;

&lt;p&gt;To fix the failing test we need to add the implementation for the AddPatient method in &lt;em&gt;PatientController.cs&lt;/em&gt;. Update the file's code as below.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace TDD.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class PatientController : Controller
    {
        private readonly DataContext _context;

        public PatientController(DataContext context)
        {
            _context = context;
        }
        [HttpPost]
        public async Task&amp;lt;IActionResult&amp;gt; AddPatientAsync([FromBody] Patient Patient)
        {
            var FetchedPatient = await _context.Patient.FirstOrDefaultAsync(x =&amp;gt; x.PhoneNumber == Patient.PhoneNumber);
            // If the patient doesn't exist create a new one
            if (FetchedPatient == null)
            {
                _context.Patient.Add(Patient);
                await _context.SaveChangesAsync();
                return Created($"/patient/{Patient.Id}", Patient);
            }
            // Else throw a bad request
            else
            {
                return BadRequest();
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the &lt;strong&gt;&lt;em&gt;dotnet test &lt;/em&gt;&lt;/strong&gt;again and you will see that the test has passed.&lt;/p&gt;

&lt;p&gt;You can run all the tests by calling &lt;strong&gt;&lt;em&gt;dotnet test.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;Important Notes&lt;/h2&gt;

&lt;p&gt;As you add more models/domains like Doctors, Staff, Instruments etc. You will have to create more tests. Make sure to have a different WAF, utility wrappers and different Test files for each of them.&lt;/p&gt;

&lt;p&gt;Secondly, the tests in the same file do not run in parallel. But, the tests from different files do run in parallel. Therefore, each WAF should have a different database name so that data is not misconfigured. &lt;/p&gt;

&lt;p&gt;Lastly, the connections to the original database still needs to be setup in the main project. &lt;/p&gt;

&lt;h2&gt;Thought Process&lt;/h2&gt;

&lt;p&gt;The thought process for creating tests for all scenarios are similar. &lt;/p&gt;

&lt;p&gt;That is, you should first identify the requirements. Then, set up a skeleton of methods and classes without implementation. Write tests to verify the implementation. Finally, refactor as needed and rerun the tests.&lt;/p&gt;

&lt;p&gt;This tutorial didn't include authentication and authorisation for api's. You can &lt;a href="https://arjavdave.com/2021/03/31/net-5-setup-authentication-and-authorisation/" rel="noreferrer noopener"&gt;read here&lt;/a&gt; on how to set it up.&lt;/p&gt;

&lt;p&gt;Since, it is not possible to cover all the test cases, I have created a &lt;a href="https://github.com/shenanigan/tdd-demo" rel="noreferrer noopener"&gt;repository on Github&lt;/a&gt;. It covers the implementation for all the test cases and the implementation as well. &lt;/p&gt;

&lt;p&gt;You can find the &lt;a href="https://github.com/shenanigan/tdd-demo" rel="noreferrer noopener"&gt;project here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In order for TDD to be effective you really need to have a clear idea of what the requirements are. If the requirements keep on changing it would get very tough to maintain the tests as well as the project. &lt;/p&gt;

&lt;p&gt;TDD mainly covers unit, integration &amp;amp; functional tests. You will still have to do UAT, Configuration &amp;amp; Production testing before you go live. &lt;/p&gt;

&lt;p&gt;Having said that, TDD is really helpful in making your project bug free. Secondly, it boosts your confidence for the implementation. You will be able to change bits &amp;amp; pieces of your code as long as the tests pass. Lastly, it provides a better architecture for your project. &lt;/p&gt;

&lt;p&gt;Hope you like the article. Let me know your thoughts or feedback. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://arjavdave.com" rel="noopener noreferrer"&gt;Check more tutorials on .NET here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Just let me login! Going Password less with .NET</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Tue, 06 Apr 2021 06:40:00 +0000</pubDate>
      <link>https://dev.to/itnext/just-let-me-login-going-password-less-with-net-24i6</link>
      <guid>https://dev.to/itnext/just-let-me-login-going-password-less-with-net-24i6</guid>
      <description>&lt;h2&gt;
  
  
  Passwords are a pain in the a..
&lt;/h2&gt;

&lt;p&gt;With thousand's of softwares and app's launching everyday, you would like to make your software stand out. Most importantly, it should have some unique USP, but in addition it should provide convenience and ease of use. &lt;/p&gt;

&lt;p&gt;For instance, one of the pain points for many apps is they require a username and a password to login. I personally have to remember 10-15 passwords for apps like Gmail, Facebook, Instagram, etc. You get the idea. &lt;/p&gt;

&lt;p&gt;In today's article we are going to create a solution for your API's that will allow your users to login without a password.&lt;/p&gt;

&lt;h2&gt;
  
  
  The How of Going Password less
&lt;/h2&gt;

&lt;p&gt;In order to omit the password there needs to be some type of token generated for a user. &lt;/p&gt;

&lt;p&gt;This token will then be sent to a user where only the user can access it e.g. on email or phone number. Here is an overview of the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eu-DInEa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njjypscirkr5stsiqexu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eu-DInEa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njjypscirkr5stsiqexu.png" alt="No Password Login Flow" width="880" height="502"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Dotnet Identity provides ways to generate tokens for email confirmation or changing email or phone. We will see more about it below.&lt;/p&gt;

&lt;p&gt;There are mainly two token providers available&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TotpSecurityStampBasedTokenProvider (Time-based One Time Password).&lt;/li&gt;
&lt;li&gt;DataProtectionTokenProvider&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  TotpSecurityStampBasedTokenProvider
&lt;/h4&gt;

&lt;p&gt;It generates time based tokens which are valid for around 3 minutes (Reference: &lt;a href="https://github.com/aspnet/AspNetIdentity/blob/b7826741279450c58b230ece98bd04b4815beabf/src/Microsoft.AspNet.Identity.Core/Rfc6238AuthenticationService.cs#L75"&gt;Source Code&lt;/a&gt;). Based on the token provider the tokens are generated from the Email, PhoneNumber or user's id as well as the user's security stamp. &lt;/p&gt;

&lt;p&gt;Dotnet Identity provides utility classes EmailTokenProvider and PhoneNumberTokenProvider that are subclasses of TotpSecurityStampBasedTokenProvider.&lt;/p&gt;

&lt;h4&gt;
  
  
  DataProtectorTokenProvider
&lt;/h4&gt;

&lt;p&gt;If you want to generate a token that doesn't expire for a long duration DataProtectorTokenProvider is the way to go. &lt;/p&gt;

&lt;p&gt;DataProtectorTokenProvider generates tokens using a DataProtector and cryptographic algorithms. You can check out the implementation for more details &lt;a href="https://github.com/aspnet/AspNetIdentity/blob/main/src/Microsoft.AspNet.Identity.Owin/DataProtectorTokenProvider.cs"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article we are going to subclass &lt;em&gt;DataProtectorTokenProvider&lt;/em&gt; so that our token is valid for 10 minutes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Identity
&lt;/h2&gt;

&lt;p&gt;The .NET Identity provides ways to manage users, passwords, profile data, roles, claims, tokens, email confirmation, and more. &lt;/p&gt;

&lt;p&gt;Let's start with a scratch project. Create a new project by executing the command &lt;strong&gt;dotnet new webapi –-name NoPasswordProject&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 5.0.4
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 5.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to create an in memory database for this tutorial. But you can use a database of your choice and accordingly change the package above. &lt;/p&gt;

&lt;p&gt;Note: The in memory database will clear the users every time the server restarts. &lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Token Provider
&lt;/h2&gt;

&lt;p&gt;Let's create a custom token provider that generates token that are valid for 10 minutes.&lt;/p&gt;

&lt;h4&gt;
  
  
  NPTokenProvider
&lt;/h4&gt;

&lt;p&gt;Create a new file called NPTokenProvider.cs. NP prefix stands for No Password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

public class NPTokenProvider&amp;lt;TUser&amp;gt; : DataProtectorTokenProvider&amp;lt;TUser&amp;gt;
where TUser : IdentityUser
{
    public NPTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions&amp;lt;NPTokenProviderOptions&amp;gt; options, ILogger&amp;lt;NPTokenProvider&amp;lt;TUser&amp;gt;&amp;gt; logger)
        : base(dataProtectionProvider, options, logger)
    { }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are here subclassing the DataProtectorTokenProvider. Nothing out of the ordinary except in the constructor we are passing NPTokenProviderOptions. The options need to be subclass of DataProtectionTokenProviderOptions.&lt;/p&gt;

&lt;h4&gt;
  
  
  NPTokenProviderOptions
&lt;/h4&gt;

&lt;p&gt;Create a new file NPTokenProviderOptions.cs and paste in the below code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using Microsoft.AspNetCore.Identity;

public class NPTokenProviderOptions : DataProtectionTokenProviderOptions
{
    public NPTokenProviderOptions()
    {
        Name = "NPTokenProvider";
        TokenLifespan = TimeSpan.FromMinutes(10);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are setting options for the tokens to be created. You can change the Name and TokenLifeSpan to your liking. &lt;/p&gt;

&lt;h4&gt;
  
  
  DbContext
&lt;/h4&gt;

&lt;p&gt;Almost every project needs database to store it's users and other data related to the project. Dotnet EF Framework provides a nice helper &lt;em&gt;DbContext&lt;/em&gt; to handle sessions with the database and query and save entites. So create a subclass of &lt;em&gt;IdentityDbContext&lt;/em&gt; which is in turn a subclass of &lt;em&gt;DbContext&lt;/em&gt;. Name the file &lt;em&gt;NPDataContext.cs&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

public class NPDataContext : IdentityDbContext
{
    public NPDataContext(DbContextOptions&amp;lt;NPDataContext&amp;gt; options)
        : base(options)
    { }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Startup.cs
&lt;/h4&gt;

&lt;p&gt;We have created the classes; now time to configure them in our &lt;em&gt;Startup.cs&lt;/em&gt; files. In &lt;em&gt;ConfigureServices&lt;/em&gt; add the below code at the start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var builder = services
.AddIdentityCore&amp;lt;IdentityUser&amp;gt;()
.AddEntityFrameworkStores&amp;lt;NPDataContext&amp;gt;();

var UserType = builder.UserType;
var provider = typeof(NPTokenProvider&amp;lt;&amp;gt;).MakeGenericType(UserType);
builder.AddTokenProvider("NPTokenProvider", provider);

services.AddDbContext&amp;lt;NPDataContext&amp;gt;(options =&amp;gt;
    options.UseInMemoryDatabase(Guid.NewGuid().ToString()));

services.AddAuthentication(options =&amp;gt;
{
    options.DefaultScheme = IdentityConstants.ExternalScheme;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also add &lt;strong&gt;app.UseAuthentication();&lt;/strong&gt; above &lt;em&gt;app.UseAuthorization();&lt;/em&gt; in &lt;em&gt;Configure&lt;/em&gt; method.&lt;/p&gt;

&lt;h4&gt;
  
  
  NoPasswordController.cs
&lt;/h4&gt;

&lt;p&gt;Let's create a controller for our login and verify API's. Create a &lt;em&gt;NoPasswordController.cs&lt;/em&gt; file in your &lt;em&gt;Controllers&lt;/em&gt; folder. Add the below content to the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace NoPasswordProject.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class NoPasswordController : ControllerBase
    {
        private readonly UserManager&amp;lt;IdentityUser&amp;gt; _userManager;

        public NoPasswordController(UserManager&amp;lt;IdentityUser&amp;gt; userManager)
        {
            _userManager = userManager;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are injecting an instance of UserManager in our controller. UserManager is used for CRUD operations for a user as well as generating tokens and validating it. &lt;/p&gt;

&lt;h4&gt;
  
  
  Login API
&lt;/h4&gt;

&lt;p&gt;Let's add a &lt;em&gt;Login&lt;/em&gt; api which accepts an Email as input. The Email is the unique identifier for a user i.e. there should be a one-to-one relation ship between user and email.&lt;/p&gt;

&lt;p&gt;Create a new function in your controller as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[HttpGet]
public async Task&amp;lt;ActionResult&amp;lt;String&amp;gt;&amp;gt; Login([FromQuery] string Email)
{
    // Create or Fetch your user from the database
    var User = await _userManager.FindByNameAsync(Email);
    if (User == null)
    {
        User = new IdentityUser();
        User.Email = Email;
        User.UserName = Email;
        var IdentityResult = await _userManager.CreateAsync(User);
        if (IdentityResult.Succeeded == false)
        {
            return BadRequest();
        }
    }

    var Token = await _userManager.GenerateUserTokenAsync(User, "NPTokenProvider", "nopassword-for-the-win");

    // DON'T RETURN THE TOKEN.
    // SEND IT TO THE USER VIA EMAIL.
    return NoContent();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are fetching a User from the database. If the user doesn't exist then we create a user. Make sure to set the UserName as well or it will give runtime error.&lt;/p&gt;

&lt;p&gt;Then based on the user, we generate a UserToken. The &lt;em&gt;GenerateUserTokenAsync&lt;/em&gt; takes the user, token provider and the purpose for generating a token. &lt;/p&gt;

&lt;p&gt;The token provider string should be the one you have used in &lt;em&gt;NPTokenProviderOptions&lt;/em&gt;. The purpose can be anything you want.&lt;/p&gt;

&lt;p&gt;Send out the token to the user via a link in a nicely designed email. When the user will click on the link in the email it will open your front-end page. Consequently this page will request the Verify api.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verify API
&lt;/h4&gt;

&lt;p&gt;Let's add another api &lt;em&gt;Verify&lt;/em&gt; that takes the &lt;em&gt;Email&lt;/em&gt; and &lt;em&gt;Token&lt;/em&gt; as query parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[HttpGet]
public async Task&amp;lt;ActionResult&amp;lt;String&amp;gt;&amp;gt; Verify([FromQuery] string Token, [FromQuery] string Email)
{
    // Fetch your user from the database
    var User = await _userManager.FindByNameAsync(Email);
    if (User == null)
    {
        return NotFound();
    }

    var IsValid = await _userManager.VerifyUserTokenAsync(User, "NPTokenProvider", "nopassword-for-the-win", Token);
    if (IsValid)
    {
        // TODO: Generate a bearer token
        var BearerToken = "";
        return BearerToken;
    }
    return Unauthorized();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are again fetching the user based on email. As a result if we are not able to find the user return 404 Not Found. &lt;/p&gt;

&lt;p&gt;We then continue to verify the user. &lt;em&gt;VerifyUserTokenAsync&lt;/em&gt; takes user, token provider, purpose and token as input parameters. The purpose should be same as the one used while generating token. &lt;/p&gt;

&lt;p&gt;If the token is not valid, return 401 Unauthorised. Otherwise return the bearer token. &lt;a href="https://www.daveops.co.in/post/net-5-how-to-authenticate-authorise-api-s-correctly"&gt;This is a good article&lt;/a&gt; on how to generate bearer token for the user.&lt;/p&gt;

&lt;p&gt;In conclusion our article explains on how to allow users to login without a password. You can find the whole project &lt;a href="https://github.com/shenanigan/dotnet-passwordless"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Providing features was supposed to be the most important thing in the '90s. But today besides having great features, the convenience for the users is a priority.&lt;/p&gt;

&lt;p&gt;We looked at one of the ways of providing convenience. That said, let us know in the comments below for more ways to do it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://daveops.co.in"&gt;Check here&lt;/a&gt; for more tutorials like this.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>security</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>.NET 5: How to authenticate &amp; authorise API's correctly</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Thu, 01 Apr 2021 05:21:53 +0000</pubDate>
      <link>https://dev.to/itnext/net-5-how-to-authenticate-authorise-api-s-correctly-4db8</link>
      <guid>https://dev.to/itnext/net-5-how-to-authenticate-authorise-api-s-correctly-4db8</guid>
      <description>&lt;p&gt;In over 11 years of my experience I have seen so many API's that have major security flaw. They either lack a proper setup of Authentication or Authorisation or both. The developers might feel okay since these endpoints are usually not public. But it is a huge security loop hole which anyone can easily target. &lt;/p&gt;

&lt;p&gt;To better understand security for API's let's create a demo project for FBI. There will be an Admin who can enrol FBI Agents and change their clearance levels. Secondly FBI Agents with &lt;em&gt;Clearance Level 1&lt;/em&gt; will be able to access public files and agents with &lt;em&gt;Clearance Level 2&lt;/em&gt; will be able to access pubic &amp;amp; classified files.&lt;/p&gt;

&lt;p&gt;First some theory! Not Interested? Take me to the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;Our Agent has successfully cleared all his exams; time to enrol him. In order to do that he will provide his documents and in return will get his badge. &lt;/p&gt;

&lt;p&gt;In the above scenario &lt;em&gt;providing documents&lt;/em&gt; is like login where once verified he will be provided with a token (badge). This process is called &lt;em&gt;Authentication&lt;/em&gt;. It determines whether agents are who they claim to be. &lt;/p&gt;

&lt;p&gt;We are going to use Json Web Tokens (JWT) Bearer tokens for authentication. &lt;em&gt;Bearer tokens&lt;/em&gt; are a type of tokens generated by servers which contain details of the claims/roles of a user trying to login. Bearer tokens are mostly structured tokens like &lt;em&gt;JWT&lt;/em&gt;. &lt;a href="https://jwt.io/introduction" rel="noopener noreferrer"&gt;Read here&lt;/a&gt; to know more about JWT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorisation
&lt;/h2&gt;

&lt;p&gt;Now since the FBI Agent has got his badge he can enter the FBI building. He is also able to access public files, but when trying to access classified files he gets &lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401" rel="noopener noreferrer"&gt;401&lt;/a&gt;&lt;/em&gt; error. &lt;/p&gt;

&lt;p&gt;This is because FBI Agent is not &lt;em&gt;authorised&lt;/em&gt; to access classified files. &lt;em&gt;Authorisation&lt;/em&gt; determines what agents can and cannot access.&lt;/p&gt;

&lt;p&gt;As mentioned above the JWT Bearer token contains claims/roles. Based on it, our server decides whether to give access to a private resource or not. &lt;/p&gt;

&lt;h2&gt;
  
  
  Access Flow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy1pcf6w2cjhtj9hgzm8v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy1pcf6w2cjhtj9hgzm8v.jpg" alt="Access Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the above diagram on successful login the server returns a Bearer token. The client uses the bearer token in subsequent calls to access a private resource. &lt;/p&gt;

&lt;p&gt;These are the two main concepts that we are going to implement in our article. &lt;/p&gt;

&lt;p&gt;Enough with the theory, show me some code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Create a new project by executing the command &lt;strong&gt;dotnet new webapi --name FBI&lt;/strong&gt; from your cli. It will create a project with a sample WeatherForecast api. &lt;/p&gt;

&lt;p&gt;Why work on WeatherForecast when we can work on FBI. Go ahead and delete &lt;em&gt;WeatherForecast.cs&lt;/em&gt; file. &lt;/p&gt;

&lt;p&gt;Add dependencies by executing the commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package Microsoft.IdentityModel.Tokens --version 6.9.0
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 5.0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;em&gt;ConfigureServices&lt;/em&gt; function in your &lt;em&gt;Startup.cs&lt;/em&gt; file add the below code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var TokenValidationParameters = new TokenValidationParameters
{
    ValidIssuer = "https://fbi-demo.com",
    ValidAudience = "https://fbi-demo.com",
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ")),
    ClockSkew = TimeSpan.Zero // remove delay of token when expire
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are defining the parameters for validating a token. Make sure that the length of the string for generating SymmetricSecurityKey is 32.&lt;/p&gt;

&lt;p&gt;Next, setup the services to add authentication for API's.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services
    .AddAuthentication(options =&amp;gt;
    {
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(cfg =&amp;gt;
    {
        cfg.TokenValidationParameters = TokenValidationParameters;
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;AddAuthentication&lt;/em&gt; method registers services required by authentication services. It also configures JWT Bearer Authentication as the default scheme.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;AddJwtBearer&lt;/em&gt; enables JWT-bearer authentication and setting the TokenValidationParameters defined above.&lt;/p&gt;

&lt;p&gt;Now let's add some Authorisation claims for our &lt;em&gt;Agent&lt;/em&gt; &amp;amp; &lt;em&gt;Admin&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.AddAuthorization(cfg =&amp;gt;
    {
        cfg.AddPolicy("Admin", policy =&amp;gt; policy.RequireClaim("type", "Admin"));
        cfg.AddPolicy("Agent", policy =&amp;gt; policy.RequireClaim("type", "Agent"));
        cfg.AddPolicy("ClearanceLevel1", policy =&amp;gt; policy.RequireClaim("ClearanceLevel", "1", "2"));
        cfg.AddPolicy("ClearanceLevel2", policy =&amp;gt; policy.RequireClaim("ClearanceLevel", "2"));
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;AddAuthorization&lt;/em&gt; method registers services required for authorisation. We are also adding claims for &lt;em&gt;Admin&lt;/em&gt;, &lt;em&gt;Agent&lt;/em&gt;, &lt;em&gt;ClearanceLevel1&lt;/em&gt; and &lt;em&gt;ClearanceLevel2&lt;/em&gt; by calling &lt;em&gt;AddPolicy&lt;/em&gt;. A claim is a name value pair that represents what the subject is. Since clearance level 2 can also access clearance level 1 we have put &lt;em&gt;"1", "2"&lt;/em&gt; in ClearanceLevel1. You can read more about claims &lt;em&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-5.0" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Lastly in the &lt;em&gt;Configure&lt;/em&gt; method add the below line just above &lt;em&gt;app.UseAuthorization();&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.UseAuthentication();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Admin Controller
&lt;/h2&gt;

&lt;p&gt;Rename your file &lt;em&gt;WeatherForecastController.cs&lt;/em&gt; to &lt;em&gt;AdminController.cs&lt;/em&gt;. Do change the class name and constructor names as well. Finally, remove everything except the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc;

namespace FBI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class AdminController : ControllerBase
    {
        public AdminController() { }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Login API
&lt;/h3&gt;

&lt;p&gt;Let's create a login API for Admin so that she can get a token to perform other tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[HttpPost]
[Route("[action]")]
public IActionResult Login([FromBody] User User)
{
    // TODO: Authenticate Admin with Database
    // If not authenticate return 401 Unauthorized
    // Else continue with below flow

    var Claims = new List&amp;lt;Claim&amp;gt;
            {
                new Claim("type", "Admin"),
            };

    var Key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ"));

    var Token = new JwtSecurityToken(
        "https://fbi-demo.com",
        "https://fbi-demo.com",
        Claims,
        expires: DateTime.Now.AddDays(30.0),
        signingCredentials: new SigningCredentials(Key, SecurityAlgorithms.HmacSha256)
    );

    return new OkObjectResult(new JwtSecurityTokenHandler().WriteToken(Token));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code &lt;em&gt;User&lt;/em&gt; is a model with properties &lt;em&gt;Username&lt;/em&gt; &amp;amp; &lt;em&gt;Password&lt;/em&gt;. We are also creating an object of &lt;em&gt;JwtSecurityToken&lt;/em&gt; using configurations that we have used in &lt;em&gt;Startup.cs&lt;/em&gt; file. The token is then converted to string and returned in an OkObjectResult.&lt;/p&gt;

&lt;p&gt;You can now open Swagger and execute the API to see a bearer token. A bearer token will be returned as you can see below. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faedozuzics30gunpp3b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faedozuzics30gunpp3b5.png" alt="Bearer Token Response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep the token handy since we are going to use it in the next section. You can also visit &lt;a href="https://jwt.io" rel="noopener noreferrer"&gt;https://jwt.io&lt;/a&gt; to analyse your token.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate Badge API
&lt;/h3&gt;

&lt;p&gt;Generating badge for an Agent is a sensitive task and should only be Authorised by an &lt;em&gt;Admin&lt;/em&gt;. We are going to add an &lt;em&gt;Authorize&lt;/em&gt; attribute for the &lt;em&gt;GenerateBadge&lt;/em&gt; api.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[HttpPost]
[Route("[action]")]
[Authorize(Policy = "Admin")]
public IActionResult GenerateBadge([FromBody] Agent Agent)
{
    var Claims = new List&amp;lt;Claim&amp;gt;
    {
        new Claim("type", "Agent"),
        new Claim("ClearanceLevel", Agent.ClearanceLevel.ToString()),
    };

    var Key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SXkSqsKyNUyvGbnHs7ke2NCq8zQzNLW7mPmHbnZZ"));

    var Token = new JwtSecurityToken(
        "https://fbi-demo.com",
        "https://fbi-demo.com",
        Claims,
        expires: DateTime.Now.AddDays(30.0),
        signingCredentials: new SigningCredentials(Key, SecurityAlgorithms.HmacSha256)
    );

    return new OkObjectResult(new JwtSecurityTokenHandler().WriteToken(Token));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here Agent is a model with properties &lt;em&gt;Name&lt;/em&gt; as string and &lt;em&gt;ClearanceLevel&lt;/em&gt; as int.&lt;/p&gt;

&lt;p&gt;Now when you go back to swagger and try to execute &lt;em&gt;GenerateBadge&lt;/em&gt; api it will give you 401 Unauthorised response. Since we have not passed the bearer token we are getting this error. &lt;/p&gt;

&lt;p&gt;To be able to add the Authorize header in Swagger change the &lt;em&gt;services.AddSwaggerGen&lt;/em&gt; as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.AddSwaggerGen(c =&amp;gt;
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "FBI", Version = "v1" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        In = ParameterLocation.Header,
        Description = "Please enter JWT with Bearer into field",
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement {
    { new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer"}
            },
        new string[] {}
    }
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you refresh Swagger in your browser you will notice an &lt;em&gt;Authorize&lt;/em&gt; button on the right side above the list of apis.&lt;/p&gt;

&lt;p&gt;Click on the newly added &lt;em&gt;Authorize&lt;/em&gt; button in Swagger which will open up a dialog. We need to mention what type of token it is. So first enter &lt;em&gt;Bearer&lt;/em&gt; in the field then a space and then the token generated from the &lt;em&gt;/Admin/Login&lt;/em&gt; api from previous section. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ks0jtoy39ogcpa61s71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ks0jtoy39ogcpa61s71.png" alt="Authorization Header"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the header to lock in the token. Now you are all set. When you execute the &lt;em&gt;GenerateBadge&lt;/em&gt; api again you will get a token (analogous to badge). Keep this token handy, since we require in next section. Also make sure to &lt;strong&gt;pass ClearanceLevel as 1&lt;/strong&gt; for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Controller
&lt;/h2&gt;

&lt;p&gt;Create a new file &lt;em&gt;AgentController.cs&lt;/em&gt; with below content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc;

namespace FBI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    [Authorize(Policy = "Agent")]
    public class AgentController : ControllerBase
    {
        public AgentController() { }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see above we are authorising the whole controller for Agent's access only. So even Admin won't be able to access the API's we are going to create.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Records API's
&lt;/h3&gt;

&lt;p&gt;Let's add the api's to access both public and classified files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[HttpGet]
[Route("[action]")]
[Authorize(Policy = "ClearanceLevel1")]
public ActionResult&amp;lt;String&amp;gt; AccessPublicFiles()
{
    return new OkObjectResult("Public Files Accessed");
}

[HttpGet]
[Route("[action]")]
[Authorize(Policy = "ClearanceLevel2")]
public ActionResult&amp;lt;String&amp;gt; AccessClassifiedFiles()
{
    return new OkObjectResult("Classified Files Accessed");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added &lt;em&gt;Authorize&lt;/em&gt; attribute's for both API's such that public files can be accessed by &lt;em&gt;ClearanceLevel1&lt;/em&gt; and classified files can be accessed by &lt;em&gt;ClearanceLevel2&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you try to access these API's with the Admin token you will get 403 Forbidden error. So go ahead and click on the &lt;em&gt;Authorize&lt;/em&gt; button again and click on &lt;em&gt;logout&lt;/em&gt;. Then, get the token from the above step and paste in the field with &lt;em&gt;Bearer&lt;/em&gt; as a prefix i.e. &lt;em&gt;Bearer &lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Now when you access &lt;em&gt;/Agent/AccessPublicFiles&lt;/em&gt; api you will see response 200 with message &lt;em&gt;Public Files Accessed&lt;/em&gt;. But when you try the classified api you get 403 Forbidden error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing Clearance Level
&lt;/h2&gt;

&lt;p&gt;Fast forward 3 years and our &lt;em&gt;Agent's&lt;/em&gt; performance has been mind bogglingly good. Management has now decided to promote him to ClearanceLevel2. &lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Agent&lt;/em&gt; goes to the &lt;em&gt;Admin&lt;/em&gt; and asks her to provide a token/badge with Clearance Level 2.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Admin&lt;/em&gt; calls the &lt;em&gt;/Admin/Login&lt;/em&gt; api to generate his own token first. She then enters it in the &lt;em&gt;Authorize&lt;/em&gt; dialog. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;/Admin/GenerageBadge&lt;/em&gt; api is then called by Admin with value 2 in the ClearanceLevel. This generates a new token/badge which she then hands over to &lt;em&gt;Agent&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Agent&lt;/em&gt; enters this token/badge in the &lt;em&gt;Authorize&lt;/em&gt; dialog and when he now calls &lt;em&gt;/Agent/AccessClassifiedFiles&lt;/em&gt; he is pleased to see the result &lt;em&gt;Classified Files Accessed&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You can find the whole project &lt;a href="https://github.com/shenanigan/fbi-demo" rel="noopener noreferrer"&gt;here&lt;/a&gt; on github.&lt;/p&gt;

&lt;p&gt;API security is extremely important and shouldn't be taken lightly even if it's for internal use only. Setup Authentication and Authorisation and you are halfway there. &lt;/p&gt;

&lt;p&gt;There are other other security measures you can security against DDoS attacks, accepting API's from a particular IP or domain only etc.&lt;/p&gt;

&lt;p&gt;How did you like the article? What are the other security measures do you usually take? Any feedbacks or comments?&lt;/p&gt;

&lt;p&gt;You can checkout out more tutorials &lt;a href="https://arjavdave.com" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>dotnet</category>
      <category>cybersecurity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Speed Up Your Website with Azure CDN</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Mon, 29 Mar 2021 05:01:22 +0000</pubDate>
      <link>https://dev.to/itnext/how-to-speed-up-your-website-with-azure-cdn-3e63</link>
      <guid>https://dev.to/itnext/how-to-speed-up-your-website-with-azure-cdn-3e63</guid>
      <description>&lt;h2&gt;
  
  
  What is CDN? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A Content Delivery Network (CDN) helps you deliver your content more quickly. You can serve any type of content that remains unchanged over a period of time, like images, videos, CSS, JavaScript, HTML files, PDFs, and more.&lt;/p&gt;

&lt;p&gt;A CDN is a group of servers that are spread across the world to deliver the content from the &lt;em&gt;Edge servers&lt;/em&gt;. Edge servers are servers located closest to the place from where the request is being made.&lt;/p&gt;

&lt;p&gt;Depending on the request, edge servers may either return the content from its cache or they can fetch it from the &lt;em&gt;Origin Server&lt;/em&gt;. The servers that serve the actual content are called Origin servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--obYQoF2g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i7t9e95tryv3mi44ghc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--obYQoF2g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i7t9e95tryv3mi44ghc.png" alt="CDN Overview" width="880" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above image, the Edge Servers are located around the world and the Origin Server is located in California, USA. When a request is made, the Edge Server located at Mumbai, India may contact the Origin Server if it's not able to serve the content.&lt;/p&gt;

&lt;h2&gt;
  
  
  How CDN works? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;CDNs have four main parts: A &lt;em&gt;Consumer&lt;/em&gt;, &lt;em&gt;DNS&lt;/em&gt;, &lt;em&gt;Edge Server&lt;/em&gt; &amp;amp; &lt;em&gt;Origin Server&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xw4WiEW_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gni65528kxlmciivhnyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xw4WiEW_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gni65528kxlmciivhnyw.png" alt="CDN Detail" width="872" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the Consumer makes a request, it is at first accepted by its Internet Service Provider (ISP). The ISP will then hit the content provider's &lt;em&gt;Authoritative DNS&lt;/em&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An Authoritative DNS converts the DNS request to an IP request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the Authoritative DNS is made, it returns the IP address of the closest Edge Server. The Edge Server will then check in its own cache to see if the requested content is available.&lt;/p&gt;

&lt;p&gt;If it is, then it returns the content. If the content is not available, it requests the content from the Origin Server and on retrieval caches it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of CDN &lt;a&gt;
&lt;/a&gt;
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Low Bandwidth Consumption
&lt;/h4&gt;

&lt;p&gt;Many web hosts have a limited bandwidth allocation per month. If you go beyond that you will be charged extra.&lt;/p&gt;

&lt;p&gt;With a CDN most of your bandwidth will be saved since the content will be served by the edge servers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Low Latency
&lt;/h4&gt;

&lt;p&gt;The Edge Servers cache the content. So anytime cached content is requested the latency is reduced drastically. This is because the request doesn't go all the way to the Origin Server.&lt;/p&gt;

&lt;h4&gt;
  
  
  Security against DDoS
&lt;/h4&gt;

&lt;p&gt;Almost all the popular CDN's have the capability to protect your webserver against Distributed denial of service (DDos) attacks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Improves SEO
&lt;/h4&gt;

&lt;p&gt;Loading time is one of the factors that can affect your site's SEO rankings. If you are serving most of your content via CDN, the loading times are drastically reduced and can help improve your SEO.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive into Azure CDN &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Let's say you have created an Azure Storage Account and hosted a very simple site that displays Hello World as h1. Now that you know the benefits of CDNs, you want to serve your simple site over CDN.&lt;/p&gt;

&lt;p&gt;You will have an endpoint something like &lt;em&gt;&lt;a href="https://demostorageaccountarjav.z29.web.core.windows.net/"&gt;https://demostorageaccountarjav.z29.web.core.windows.net/&lt;/a&gt;&lt;/em&gt;(where instead of demostorageaccountarjav it would be your storage account's name). Here are more details on how to &lt;a href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website"&gt;setup a static website&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Login to your Azure Portal and click on &lt;em&gt;Create a resource&lt;/em&gt; from you dashboard. Search for &lt;em&gt;CDN&lt;/em&gt; which will open the resource in the marketplace as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vijjvTJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ravgtt3j0xmeu5osd9d2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vijjvTJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ravgtt3j0xmeu5osd9d2.png" alt="CDN Create Resource" width="880" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open up a form to create a CDN profile. A CDN profile is a set of CDN endpoints. There is not much to fill in here except the name, resource group, and the pricing tier.&lt;/p&gt;

&lt;p&gt;Next, select the checkbox to create a CDN endpoint. An endpoint is where the Consumer will be requesting content. So if you have multiple sites you can create multiple endpoints as well.&lt;/p&gt;

&lt;p&gt;I have attached a screenshot for your reference on what values to put in. Since CDN is a global service the region selection will be disabled. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NhFFnKR0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z35xu3l7ox243ea6ecfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NhFFnKR0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z35xu3l7ox243ea6ecfj.png" alt="CDN Details" width="880" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now click on &lt;em&gt;Create&lt;/em&gt; to generate the profile and endpoint. It will take a couple of minutes to create. After it is created and when you go to the home screen, you will have these 4 resources:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8L45ta1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o6bxldg59sqod68o4kq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8L45ta1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o6bxldg59sqod68o4kq1.png" alt="Azure Resources" width="880" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As discussed earlier the CDN Profile is a group of &lt;em&gt;Endpoints&lt;/em&gt;. To view the details click the &lt;em&gt;Endpoint&lt;/em&gt; resource. You will see an overview with a link to the &lt;em&gt;Endpoint hostname&lt;/em&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BEIZghUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vt4t5uxyv6u0i5llicwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BEIZghUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vt4t5uxyv6u0i5llicwp.png" alt="Endpoint Overview" width="880" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you open the endpoint hostname it might show "404 not found" initially. You might have to wait another 10-15 minutes before your actual site is visible. &lt;/p&gt;

&lt;p&gt;As discussed in the benefits section you can configure the Endpoint for security, caching, routing &amp;amp; a lot of other things. You can explore more concepts &lt;a href="https://docs.microsoft.com/en-us/azure/cdn/cdn-how-caching-works"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Access via SAS Token &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You may be wondering what if my resource is in a private container and can only be accessed via a &lt;em&gt;Shared Access Signature&lt;/em&gt; (SAS) Token. Well you are in luck! The query strings are passed on as they are and since SAS is as a query string you are good. &lt;/p&gt;

&lt;p&gt;Go ahead and create a new storage account (with static website disabled). Add a new Endpoint in the CDN profile that points to the newly created storage account.&lt;/p&gt;

&lt;p&gt;For demo purposes I have created a container named &lt;em&gt;site&lt;/em&gt; with private access level and uploaded a Blob named &lt;em&gt;Photo.jpeg&lt;/em&gt; in a Storage Account with URL &lt;a href="https://demostorageaccountarjav.blob.core.windows.net"&gt;https://demostorageaccountarjav.blob.core.windows.net&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can of course get a SAS token from the Azure portal directly for testing, but that's not how you would usually do in real-world. For that find below a simple snippet to create SAS token in Node.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const azureSasToken = require('azure-sas-token');

// default token validity is 7 days
let sasToken = azureSasToken.createSharedAccessToken('https://&amp;lt;service namespace&amp;gt;.servicebus.windows.net/&amp;lt;topic name or queue&amp;gt;',
                                '&amp;lt;signature key name&amp;gt;',
                                '&amp;lt;signature hash&amp;gt;');
console.log(`sasToken: ${sasToken}`);

// Specify your own validity in secs, two hours in this example
sasToken = azureSasToken.createSharedAccessToken('https://&amp;lt;service namespace&amp;gt;.servicebus.windows.net/&amp;lt;topic name or queue&amp;gt;',
                                '&amp;lt;signature key name&amp;gt;',
                                '&amp;lt;signature hash&amp;gt;', 
                                60 * 60 * 2);
console.log(`sasToken: ${sasToken}`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have used a simple npm package named &lt;a href="https://www.npmjs.com/package/azure-sas-token"&gt;azure-sas-token&lt;/a&gt;. Once the SAS is generated your URL will look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://demostorageaccountarjav.blob.core.windows.net/site/Photo.jpeg?sp=r&amp;amp;st=2021-03-25T07:28:45Z&amp;amp;se=2022-02-02T15:28:45Z&amp;amp;spr=https&amp;amp;sv=2020-02-10&amp;amp;sr=b&amp;amp;sig=PD4HlRI8bDEirMevpYQgpx6drwh%2BE5EpILfXkQOMlvw%3D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above URL is pointing directly to the storage account. So go ahead and change the origin so that it uses the origin endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://demowebsitearjav.azureedge.net/site/Photo.jpeg?sp=r&amp;amp;st=2021-03-25T07:28:45Z&amp;amp;se=2022-02-02T15:28:45Z&amp;amp;spr=https&amp;amp;sv=2020-02-10&amp;amp;sr=b&amp;amp;sig=PD4HlRI8bDEirMevpYQgpx6drwh%2BE5EpILfXkQOMlvw%3D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you visit this site you will now be able to view the protected resource via CDN.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In my opinion everyone should be using a Content Delivery Network. There are lots of other providers like Cloudflare, S3 etc. but Microsoft is one of the major players which is emerging with a wide variety of services. &lt;/p&gt;

&lt;p&gt;If you are an Azure fan like I am you should definitely give Azure CDN a try. &lt;/p&gt;

&lt;p&gt;For any feedback or questions you can &lt;a href="https://arjavdave.com/contact/"&gt;get in touch&lt;/a&gt; with me.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://arjavdave.com"&gt;here&lt;/a&gt; for more tutorials like this.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>cdn</category>
      <category>security</category>
      <category>seo</category>
    </item>
    <item>
      <title>Azure Functions &amp; wkhtmltopdf: Convert HTML to PDF</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Mon, 22 Mar 2021 09:12:25 +0000</pubDate>
      <link>https://dev.to/arjavdave/azure-functions-wkhtmltopdf-convert-html-to-pdf-16ne</link>
      <guid>https://dev.to/arjavdave/azure-functions-wkhtmltopdf-convert-html-to-pdf-16ne</guid>
      <description>&lt;p&gt;We are going to use Azure Functions &amp;amp; &lt;a href="https://wkhtmltopdf.org/" rel="noreferrer noopener"&gt;wkhtmltopdf&lt;/a&gt; tool to generate a PDF file from an HTML file. You might want to create a PDF file for a great many reasons e.g. generate invoices for sales, medical reports for your patients, insurance forms for your clients etc. There are a few ways to do this.&lt;/p&gt;

&lt;p&gt;Firstly, you can use Adobe‘s fill and sign tool to fill out forms, but this mostly requires a human interaction and hence it’s not scalable and not convenient.&lt;/p&gt;

&lt;p&gt;Second option is you directly create a pdf file. Based on the platform you are working on you will have tools to directly create a pdf file. If it’s a very simple pdf you can take this approach.&lt;/p&gt;

&lt;p&gt;This brings us to our final and most convenient option. wkhtmltopdf is a really great tool to convert your HTML to PDF. Since it is free, open source and can be compiled for almost all platforms it is our best choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft VS Code&lt;/li&gt;
&lt;li&gt;An account on &lt;a href="https://portal.azure.com" rel="noreferrer noopener nofollow"&gt;Azure Portal&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Linux Basic (B1) App Service Plan. If you already have a Windows Basic (B1) App Service Plan you can use that.&lt;/li&gt;
&lt;li&gt;Azure Storage Account.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Azure Functions
&lt;/h2&gt;

&lt;p&gt;Since converting a HTML to PDF is a time consuming task we shouldn’t run it on our main web server. Otherwise it may start blocking other important requests. Azure Functions are the best way to delegate such tasks.&lt;/p&gt;

&lt;p&gt;In order to create a function you will first need to install Azure Functions on your machine. Based on your OS install the &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=macos%2Ccsharp%2Cbash#install-the-azure-functions-core-tools" rel="noreferrer noopener nofollow"&gt;Azure Functions Core Tools&lt;/a&gt;. Once installed open your command line tool to fire the below command. html2pdf is your project's name. You can replace it with any name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func init html2pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On executing the command it will ask for a worker runtime. Here select &lt;em&gt;1. dotnet&lt;/em&gt; since being a Microsoft’s product it provides great support for dotnet. This will generate a folder named &lt;em&gt;html2pdf&lt;/em&gt; in your current directory. Since Visual Studio Code allows to directly publish to Azure Functions we will use it to code and deploy.&lt;/p&gt;

&lt;p&gt;After you open your project in VS Code create a file named &lt;em&gt;Html2Pdf.cs&lt;/em&gt;. Azure Functions provide a wide variety of &lt;a href="https://www.serverless360.com/blog/azure-functions-triggers-and-bindings" rel="noreferrer noopener nofollow"&gt;triggers&lt;/a&gt; to execute the function. For now we will start with HTTP trigger i.e. the function can be called directly via http protocol. In our newly created file paste the below content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
namespace Html2Pdf
{
    public class Html2Pdf
    {
        // The name of the function
        [FunctionName("Html2Pdf")]

        // The first arugment tells that the functions can be triggerd by a POST HTTP request. 
        // The second argument is mainly used for logging information, warnings or errors
        public void Run([HttpTrigger(AuthorizationLevel.Function, "POST")] Html2PdfRequest Request, ILogger Log)
        {
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a skeleton in which we will now fill in the details. As you might have noticed the type of request variable is &lt;em&gt;Html2PdfRequest&lt;/em&gt;. So let’s create a model &lt;em&gt;Html2PdfRequest.cs&lt;/em&gt; class as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace Html2Pdf
{
    public class Html2PdfRequest
    {
        // The HTML content that needs to be converted.
        public string HtmlContent { get; set; }

        // The name of the PDF file to be generated
        public string PDFFileName { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  DinkToPdf
&lt;/h2&gt;

&lt;p&gt;In order to invoke wkhtmltopdf from our managed code a technology called P/Invoke is used. In short P/Invoke allows us to access structs, callbacks and functions in unmanaged libraries. There is a nice P/Invoke wrapper named DinkToPdf to allow us to abstract away the technicalities.&lt;br&gt;
You can add DinkToPdf to your project via nuget. Simply run the command from your root folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package DinkToPdf --version 1.0.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Time to add some code at the top of our class &lt;em&gt;Html2Pdf&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Read more about converter on: https://github.com/rdvojmoc/DinkToPdf
// For our purposes we are going to use SynchronizedConverter
IPdfConverter pdfConverter = new SynchronizedConverter(new PdfTools());
// A function to convert html content to pdf based on the configuration pased as arguments
// Arguments:
// HtmlContent: the html content to be converted
// Width: the width of the pdf to be created. e.g. "8.5in", "21.59cm" etc.
// Height: the height of the pdf to be created. e.g. "11in", "27.94cm" etc.
// Margins: the margis around the content
// DPI: The dpi is very important when you want to print the pdf.
// Returns a byte array of the pdf which can be stored as a file
private byte[] BuildPdf(string HtmlContent, string Width, string Height, MarginSettings Margins, int? DPI = 180)
{
  // Call the Convert method of SynchronizedConverter "pdfConverter"
  return pdfConverter.Convert(new HtmlToPdfDocument()
            {
                // Set the html content
                Objects =
                {
                    new ObjectSettings
                    {
                        HtmlContent = HtmlContent
                    }
                },
                // Set the configurations
                GlobalSettings = new GlobalSettings
                {
                    // PaperKind.A4 can also be used instead PechkinPaperSize
                    PaperSize = new PechkinPaperSize(Width, Height),
                    DPI = DPI,
                    Margins = Margins
                }
            });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have added inline comments so as to be self explanatory. If you have any questions you can ask me in the comments section below. Let’s call the above created function from our &lt;em&gt;Run&lt;/em&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// PDFByteArray is a byte array of pdf generated from the HtmlContent 
var PDFByteArray = BuildPdf(Request.HtmlContent, "8.5in", "11in", new MarginSettings(0, 0, 0,0));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the byte array is generated let’s store that as a blob in Azure Storage. Before you upload the blob, do create a container. Once you do that add the below code after &lt;em&gt;PDFByteArray&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The connection string of the Storage Account to which our PDF file will be uploaded

// Make sure to replace with your connection string.
var StorageConnectionString = "DefaultEndpointsProtocol=https;AccountName=&amp;lt;YOUR ACCOUNT NAME&amp;gt;;AccountKey=&amp;lt;YOUR ACCOUNT KEY&amp;gt;;EndpointSuffix=core.windows.net";

// Generate an instance of CloudStorageAccount by parsing the connection string
var StorageAccount = CloudStorageAccount.Parse(StorageConnectionString);

// Create an instance of CloudBlobClient to connect to our storage account
CloudBlobClient BlobClient = StorageAccount.CreateCloudBlobClient();

// Get the instance of CloudBlobContainer which points to a container name "pdf"
// Replace your own container name
CloudBlobContainer BlobContainer = BlobClient.GetContainerReference("pdf");

// Get the instance of the CloudBlockBlob to which the PDFByteArray will be uploaded
CloudBlockBlob Blob = BlobContainer.GetBlockBlobReference(Request.PDFFileName);

// Upload the pdf blob
await Blob.UploadFromByteArrayAsync(PDFByteArray, 0, PDFByteArray.Length);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see some errors and warning after you add this code. For that firstly, add the missing import statements. Secondly, change the return type from void to async Task for the Run function. Here is what the final &lt;em&gt;Html2Pdf.cs&lt;/em&gt; file will look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using DinkToPdf;
using IPdfConverter = DinkToPdf.Contracts.IConverter;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System.Threading.Tasks;
namespace Html2Pdf
{
    public class Html2Pdf
    {
        // Read more about converter on: https://github.com/rdvojmoc/DinkToPdf
        // For our purposes we are going to use SynchronizedConverter
        IPdfConverter pdfConverter = new SynchronizedConverter(new PdfTools());

        // A function to convert html content to pdf based on the configuration pased as arguments
        // Arguments:
        // HtmlContent: the html content to be converted
        // Width: the width of the pdf to be created. e.g. "8.5in", "21.59cm" etc.
        // Height: the height of the pdf to be created. e.g. "11in", "27.94cm" etc.
        // Margins: the margis around the content
        // DPI: The dpi is very important when you want to print the pdf.
        // Returns a byte array of the pdf which can be stored as a file
        private byte[] BuildPdf(string HtmlContent, string Width, string Height, MarginSettings Margins, int? DPI = 180)
        {
            // Call the Convert method of SynchronizedConverter "pdfConverter"
            return pdfConverter.Convert(new HtmlToPdfDocument()
            {
                // Set the html content
                Objects =
                {
                    new ObjectSettings
                    {
                        HtmlContent = HtmlContent
                    }
                },
                // Set the configurations
                GlobalSettings = new GlobalSettings
                {
                    // PaperKind.A4 can also be used instead of width &amp;amp; height
                    PaperSize = new PechkinPaperSize(Width, Height),
                    DPI = DPI,
                    Margins = Margins
                }
            });
        }
        // The name of the function
        [FunctionName("Html2Pdf")]
        // The first arugment tells that the functions can be triggerd by a POST HTTP request. 
        // The second argument is mainly used for logging information, warnings or errors
        public async Task Run([HttpTrigger(AuthorizationLevel.Function, "POST")] Html2PdfRequest Request, ILogger Log)
        {
            // PDFByteArray is a byte array of pdf generated from the HtmlContent 
            var PDFByteArray = BuildPdf(Request.HtmlContent, "8.5in", "11in", new MarginSettings(0, 0, 0, 0));
           // The connection string of the Storage Account to which our PDF file will be uploaded

            // The connection string of the Storage Account to which our PDF file will be uploaded
            var StorageConnectionString = "DefaultEndpointsProtocol=https;AccountName=&amp;lt;YOUR ACCOUNT NAME&amp;gt;;AccountKey=&amp;lt;YOUR ACCOUNT KEY&amp;gt;;EndpointSuffix=core.windows.net";

            // Generate an instance of CloudStorageAccount by parsing the connection string
            var StorageAccount = CloudStorageAccount.Parse(StorageConnectionString);

            // Create an instance of CloudBlobClient to connect to our storage account
            CloudBlobClient BlobClient = StorageAccount.CreateCloudBlobClient();

            // Get the instance of CloudBlobContainer which points to a container name "pdf"
            // Replace your own container name
            CloudBlobContainer BlobContainer = BlobClient.GetContainerReference("pdf");

            // Get the instance of the CloudBlockBlob to which the PDFByteArray will be uploaded
            CloudBlockBlob Blob = BlobContainer.GetBlockBlobReference(Request.PDFFileName);

            // Upload the pdf blob
            await Blob.UploadFromByteArrayAsync(PDFByteArray, 0, PDFByteArray.Length);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This concludes the coding part.&lt;/p&gt;

&lt;h2&gt;
  
  
  wkhtmltopdf
&lt;/h2&gt;

&lt;p&gt;We will still need to add wkhtmltopdf library in our project. There are a few caveats when selecting a particular Azure App Plan. Based on the Plan, we will have to get the wkhtmltopdf library. For our purposes we have selected Linux Basic (B1) App Service Plan since Windows Basic (B1) App Service Plan is 5 times costlier.&lt;/p&gt;

&lt;p&gt;At the time of writing this blog Azure App Service Plan was using Debian 10 with amd64 architecture. Good for us, DinkToPdf provides precompiled libraries for Linux, Windows &amp;amp; MacOS. Download the .so library for Linux and put it in your project’s root folder. I am working on MacOS so I downloaded libwkhtmltox.dylib as well. If you are using Windows or if you have hosted the Azure Functions on Windows App Service Plan you must download the libwkhtmltox.dll. Here is how our project structure will look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rcH3K9V0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-21-at-4.41.20-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rcH3K9V0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-21-at-4.41.20-PM.png" alt="project structure" width="676" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we create a build we need to include the .so library. In order to do that open your csproj file and add the below content to the ItemGroup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;None Update="./libwkhtmltox.so"&amp;gt;
      &amp;lt;CopyToOutputDirectory&amp;gt;PreserveNewest&amp;lt;/CopyToOutputDirectory&amp;gt;
    &amp;lt;CopyToPublishDirectory&amp;gt;Always&amp;lt;/CopyToPublishDirectory&amp;gt;
&amp;lt;/None&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the whole csproj file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Project Sdk="Microsoft.NET.Sdk"&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;TargetFramework&amp;gt;netcoreapp3.1&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;AzureFunctionsVersion&amp;gt;v3&amp;lt;/AzureFunctionsVersion&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include="DinkToPdf" Version="1.0.8" /&amp;gt;
    &amp;lt;PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.11" /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;None Update="host.json"&amp;gt;
      &amp;lt;CopyToOutputDirectory&amp;gt;PreserveNewest&amp;lt;/CopyToOutputDirectory&amp;gt;
    &amp;lt;/None&amp;gt;
    &amp;lt;None Update="local.settings.json"&amp;gt;
      &amp;lt;CopyToOutputDirectory&amp;gt;PreserveNewest&amp;lt;/CopyToOutputDirectory&amp;gt;
      &amp;lt;CopyToPublishDirectory&amp;gt;Never&amp;lt;/CopyToPublishDirectory&amp;gt;
    &amp;lt;/None&amp;gt;
    &amp;lt;None Update="./libwkhtmltox.so"&amp;gt;
      &amp;lt;CopyToOutputDirectory&amp;gt;PreserveNewest&amp;lt;/CopyToOutputDirectory&amp;gt;
      &amp;lt;CopyToPublishDirectory&amp;gt;Always&amp;lt;/CopyToPublishDirectory&amp;gt;
    &amp;lt;/None&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Azure Functions App
&lt;/h2&gt;

&lt;p&gt;Before we deploy to Azure Functions we will have to create the Azure Functions in Azure Portal. You can go to Azure Portal and start creating the Azure Functions resource. You can follow the below screenshots for clarity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZvdKmltJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Untitled-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZvdKmltJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Untitled-1.jpg" alt="" width="880" height="849"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the below screenshot make sure to select or create at least Basic Plan here. Secondly, in the Operating System select Linux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CfkataIR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.30.48-AM-979x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CfkataIR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.30.48-AM-979x1024.png" alt="" width="880" height="920"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s good to have Application Insights since you will be able to see logs and monitor functions. Besides, it hardly costs anything. As shown in the screenshot below select Yes if you want to enable it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dHPy8O6N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.31.11-AM-962x1024.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dHPy8O6N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.31.11-AM-962x1024.png" alt="" width="880" height="937"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Next: Tags and again click Next and click Create to create your resource. It might take a few minutes to create the Azure Functions resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Azure Functions
&lt;/h2&gt;

&lt;p&gt;Once created we will deploy our code directly to Azure Functions via VS Code. For that you will have to go to the extensions and install the Azure Functions extension. With its help we will be able to login and manage Azure Functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fTevnPEi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.03.00-AM-1024x591.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fTevnPEi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.03.00-AM-1024x591.png" alt="" width="880" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once installed you will see Azure icon on the side bar. When clicked, it will open a panel with an option to Sign In to Azure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i9lQRldx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.19.08-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i9lQRldx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.19.08-AM.png" alt="" width="796" height="898"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Sign in to Azure which will open a browser where you can login with your account. Once logged in you can go back to VS Code and see the list of Azure Functions in your side panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XfwFI6ty--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.43.07-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XfwFI6ty--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-10.43.07-AM.png" alt="" width="670" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me there are 4 function apps. Since you might have created just one it will show one. Time to deploy the app.&lt;/p&gt;

&lt;p&gt;Press F1 to open a menu with a list of actions. Select Azure Functions: Deploy to Function App… which will open a list of Azure Functions to which you can deploy. Select our newly created Azure Funtions App. This will ask for a confirmation pop-up, so go ahead and deploy it. It will take a few minutes to deploy your App.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring wkhtmltopdf
&lt;/h2&gt;

&lt;p&gt;Once you have deployed to Azure Functions there is still one last thing to do. We will need to add libwkhtmltox.so to a proper location on our Azure Functions App. Login to Azure portal and navigate to our Azure Functions App. On the side panel search for SSH and click the Go button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8iFcKs4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.14.03-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8iFcKs4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.14.03-PM.png" alt="" width="880" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open a SSH console in new tab. Our site is located at /home/site/wwwroot. So navigate to it's bin folder by typing in the below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /home/site/wwwroot/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you execute &lt;em&gt;ls&lt;/em&gt; command to view the contents of the file you won’t see the &lt;em&gt;libwkhtmltox.so&lt;/em&gt; file. It is actually located at &lt;em&gt;/home/site/wwwroot&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That is not the correct position. We need to copy it in the bin folder. For that execute the below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp ../libwkhtmltox.so libwkhtmltox.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you know a better way on how to include the file in the bin folder please suggest in the comment below.&lt;/p&gt;

&lt;p&gt;That’s it!!! You have got a fully functional Azure Functions App. Time to call it from our demo dotnet project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invoking the Azure Function
&lt;/h2&gt;

&lt;p&gt;All said and done we still need to test and call our function. Before we do that we need to get hold of &lt;em&gt;Code&lt;/em&gt; which is required to call the Function. The &lt;em&gt;Code&lt;/em&gt; is a secret that needs to be included to call the Function securely. To get the &lt;em&gt;Code&lt;/em&gt; navigate to Azure Portal and open your Function App. In the side panel search for &lt;em&gt;Functions&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lQj9G2gw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.28.21-PM-1024x571.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lQj9G2gw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.28.21-PM-1024x571.png" alt="" width="880" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will see &lt;em&gt;Html2Pdf&lt;/em&gt; in the list. Click on that function which will open the details view. In the side panel there will be an option for Function Keys. Select that option to view a hidden default Code already added for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KeZ0Ra5G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.29.55-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KeZ0Ra5G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://arjavdave.com/wp-content/uploads/2021/03/Screenshot-2021-03-22-at-12.29.55-PM.png" alt="" width="880" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the code and keep that handy since it will be needed in the code. In order to test the function I have created a sample console app for you. Replace the base url and the &lt;em&gt;Code&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Demo.ConsoleApp
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            string AzureFunctionsUrl = "https://&amp;lt;Your Base Url&amp;gt;/api/Html2Pdf?code=&amp;lt;Replace with your Code&amp;gt;";
using (HttpClient client = new HttpClient())
            {
                var Request = new Html2PdfRequest
                {
                    HtmlContent = "&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;",
                    PDFFileName = "hello-world.pdf"
                };
                string json = JsonConvert.SerializeObject(Request);
                var buffer = System.Text.Encoding.UTF8.GetBytes(json);
                var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (HttpResponseMessage res = await client.PostAsync(AzureFunctionsUrl, byteContent))
                {
                    if (res.StatusCode != HttpStatusCode.NoContent)
                    {
                        throw new Exception("There was an error uploading the pdf");
                    }
                }
            }
        }
    }
public class Html2PdfRequest
    {
        // The HTML content that needs to be converted.
        public string HtmlContent { get; set; }
        // The name of the PDF file to be generated
        public string PDFFileName { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again the code should be pretty self explanatory. If you have any feedback or questions please ask in the comment section below. Once you run the above console app, it will create a &lt;em&gt;hello-world.pdf&lt;/em&gt; file in your pdf container in Azure Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That concludes our tutorial on how to convert HTML to PDF using Azure Functions. For any feedback, questions or blog topics you can leave a comment below. Subscribe to the newsletter for upcoming and exciting new tutorials.&lt;/p&gt;

&lt;p&gt;You can also follow me on &lt;a href="https://arjav-dave.medium.com/" rel="noreferrer noopener nofollow"&gt;medium&lt;/a&gt;, &lt;a href="https://dev.to/arjavdave" rel="noreferrer noopener nofollow"&gt;dev.to&lt;/a&gt; &amp;amp; &lt;a href="https://blog.royalecheese.com/" rel="noreferrer noopener nofollow"&gt;hashnode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://arjavdave.com"&gt;my blogs&lt;/a&gt; for more such tutorials.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>serverless</category>
      <category>htmltopdf</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Self-Signed SSL: NGINX on MAC</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Fri, 19 Mar 2021 11:09:13 +0000</pubDate>
      <link>https://dev.to/arjavdave/self-signed-ssl-nginx-on-mac-93</link>
      <guid>https://dev.to/arjavdave/self-signed-ssl-nginx-on-mac-93</guid>
      <description>&lt;p&gt;Till now, we have  &lt;a href="https://arjavdave.com/2021/03/08/installing-nginx-on-mac-part-1/"&gt;installed Nginx&lt;/a&gt; and did a  &lt;a href="https://arjavdave.com/2021/03/08/simple-configuration-of-nginx-on-mac-part-2/"&gt;simple configuration&lt;/a&gt; to host an html file locally.&lt;/p&gt;

&lt;p&gt;In this part we will be configuring Nginx with a self-signed certificate. We will be creating a self signed certificate using openssl and make Nginx use it for serving content over https. Let's get our hands dirty. Open our pal, Terminal and lets create a couple of folders to store our key and certificate. Fire the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir -p /usr/local/etc/ssl/private
mkdir -p /usr/local/etc/ssl/certs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally you can create these folders anywhere but it's a good practice to have them at the above given path. We will now create key and certificate by running the below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo openssl req \
  -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /usr/local/etc/ssl/private/self-signed.key \
  -out /usr/local/etc/ssl/certs/self-signed.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's alter our server context from previous tutorial. The updated file is as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events {

}


http {
    server {
        # Listen on port 80 which is the default http port
        listen 80;

        # Set a permanent redirection from http to https
        return 301 https://localhost:443;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add another server context inside http context with configuration and locations relating to SSL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; server {
       listen       443 ssl;

       # location of ssl certificate
       ssl_certificate /usr/local/etc/ssl/certs/self-signed.crt;

       # location of ssl key
       ssl_certificate_key /usr/local/etc/ssl/private/self-signed.key;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add location context inside the ssl server context&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location / {
    root   /Users/arjav/Desktop/www;
    index  index.html index.htm;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the whole configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events {

}

http {
    # HTTP server
    server {
        listen       80;
        return 301 https://localhost:443;
    }


    # HTTPS server
    server {
       listen       443 ssl;

       ssl_certificate /usr/local/etc/ssl/certs/self-signed.crt;
       ssl_certificate_key /usr/local/etc/ssl/private/self-signed.key;

       location / {
           root   /Users/arjav/Desktop/www;
           index  index.html index.htm;
       }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a last step we will need to add the self-signed certificate to the system keychain. Run the below command in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo security add-trusted-cert \
  -d -r trustRoot \
  -k /Library/Keychains/System.keychain /usr/local/etc/ssl/certs/self-signed.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila! That's it. In your terminal verify your configuration file by running&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;nginx -t&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 and if everything looks okay reload your Nginx server by running&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;nginx -s reload&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Visit  &lt;a href="https://127.0.0.1"&gt;https://127.0.0.1&lt;/a&gt;. You will still see a red flag or "Not secure" sign in your browser saying that your certificate is invalid, but that it's because not signed by a third-part authority. Rest assured the content is served over secure channels.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://arjavdave.com/2021/03/09/ssl-advanced-configuration-nginx-on-mac-part-4/"&gt;next chapter&lt;/a&gt; we will look at some advanced ssl configuration options for better security, caching and optimisation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>computerscience</category>
      <category>macos</category>
    </item>
    <item>
      <title>Simple Configuration of NGINX on Mac</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Wed, 17 Mar 2021 19:29:00 +0000</pubDate>
      <link>https://dev.to/arjavdave/simple-configuration-of-nginx-on-mac-3cg3</link>
      <guid>https://dev.to/arjavdave/simple-configuration-of-nginx-on-mac-3cg3</guid>
      <description>&lt;p&gt;This is the 2nd part in the series of NGINX on Mac. You can visit the 1st part  &lt;a href="https://www.daveops.co.in/post/installing-nginx-on-mac-part-1"&gt;here&lt;/a&gt;. In this part we are going to understand the configuration file of Nginx and tweak as per our requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration file
&lt;/h3&gt;

&lt;p&gt;Your Nginx configuration file will be located at&lt;br&gt;
&lt;em&gt;/usr/local/etc/nginx/nginx.conf&lt;/em&gt;. You can fire&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;nginx -t&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;to run a test configuration just to verify the location. Open the nginx.conf file to see it's contents. I usually use Microsoft's VS Code to edit any text files, so I ran the below command to open the file in VS Code.  You can use any text editor you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code /usr/local/etc/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once open you will see a the config file already has content in it most of which is commented out. You can clear everything so that we can take one step at a time. Run&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;nginx -t&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 to test the configuration file we just cleared out. It will give an error saying&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;no "events" section in configuration&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 which simply means there is no events context. Add the events context as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The events context helps you with configuration of the server like how many worker connections can be open at the same time and how polling happens among them. &lt;/p&gt;

&lt;p&gt;Now when you run&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;nginx -t&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 you will not get any error but when you open  &lt;a href="http://127.0.0.1:8080"&gt;http://127.0.0.1:8080&lt;/a&gt; you will be faced with&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;This site can’t be reached&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 error since we have not set our server locations yet. Time to do that! &lt;/p&gt;

&lt;p&gt;Below your events context add another context with name &lt;em&gt;http&lt;/em&gt;. Inside http context add context with name &lt;em&gt;server&lt;/em&gt;. Finally, inside server context add context named &lt;em&gt;location&lt;/em&gt;. It should look like below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events {
}

http {
    server {
        # We are configuring for the / location
        location / {

        }
    }    
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a skeleton inside which we will now put some flesh and bones. Add a &lt;em&gt;root&lt;/em&gt; directive inside the location context with a path pointing to your html directory. For demo purposes I have created a &lt;em&gt;www&lt;/em&gt; folder on my Desktop. You can place your folder where ever you like. In the folder I have created an index.html with content&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;&amp;lt;h1&amp;gt;HELLO WORLD&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
  Here is the overall config file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;events {
}

http {
    server {
        # listen on the port 8080
        listen 8080;

        # When 127.0.0.1:8080 is visited, serve content from www on Desktop
        location / {
            root /Users/arjav/Desktop/www;
        }
    }    
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it!!! This is the simplest configuration you can have in nginx. From your terminal run&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;nginx -s reload&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 to reload nginx and visit &lt;a href="http://127.0.0.1:8080"&gt;http://127.0.0.1:8080&lt;/a&gt; in your browser and you will see a nice big HELLO WORLD. Press Cmd+Shift+R to hard refresh your browser in case you are seeing some cached content.&lt;/p&gt;

&lt;p&gt;Next -&amp;gt;  &lt;a href="https://www.daveops.co.in/post/self-signed-ssl-nginx-on-mac-part-3"&gt;Configure self-signed SSL&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>nginx</category>
      <category>webdev</category>
      <category>codenewbie</category>
      <category>html</category>
    </item>
    <item>
      <title>Installing NGINX on MAC</title>
      <dc:creator>Arjav Dave</dc:creator>
      <pubDate>Wed, 17 Mar 2021 05:56:00 +0000</pubDate>
      <link>https://dev.to/arjavdave/installing-nginx-on-mac-46ac</link>
      <guid>https://dev.to/arjavdave/installing-nginx-on-mac-46ac</guid>
      <description>&lt;p&gt;Nginx is one of the most widely used web servers in the world. In addition to being a web server it has also become very popular for reverse proxy, HTTP Caching &amp;amp; Load Balancing. &lt;/p&gt;

&lt;p&gt;This is a multipart part series where we will be installing, configuring and deploying Nginx on a local machine with Mac OS installed as it were a production machine. &lt;/p&gt;

&lt;p&gt;Let's start with Part 1: Installing Nginx. &lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Homebrew
&lt;/h3&gt;

&lt;p&gt;We will be using Homebrew to install Nginx. To install Homebrew open your terminal and fire the below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can visit &lt;a href="https://brew.sh/"&gt;https://brew.sh/&lt;/a&gt; for more details&lt;/p&gt;

&lt;p&gt;If Homebrew is already installed you can use the below command to update the repository index&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing Nginx
&lt;/h3&gt;

&lt;p&gt;Once Homebrew is installed, installing Nginx is as easy as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed you can see the version of the Nginx that is installed in the Summary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting and Stopping Nginx
&lt;/h3&gt;

&lt;p&gt;To start the Nginx server fire the below command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will start the web server. You can then visit &lt;a href="http://127.0.0.1:8080"&gt;http://127.0.0.1:8080&lt;/a&gt; in your browser to see a welcome message by Nginx.&lt;/p&gt;

&lt;p&gt;To stop the Nginx server add the stop signal as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nginx -s stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next -&amp;gt;  &lt;a href="https://www.daveops.co.in/post/simple-configuration-of-nginx-on-mac-part-2"&gt;Part 2: Simple Configuration Nginx on Mac&lt;/a&gt; &lt;/p&gt;

</description>
      <category>nginx</category>
      <category>macos</category>
      <category>homebrew</category>
      <category>webserver</category>
    </item>
  </channel>
</rss>
