banner



How To Create A Web Api In Visual Studio 2015

Even with all the hype around ASP.NET Core, many .NET developers continue to develop applications with ASP.NET 4.x. The ASP.NET 4.X framework is still being developed, and will be supported for a long time to come. It's a battle-tested web framework that has existed for over 15 years and is supported by a mature ecosystem.

On the client side, many developers prefer Angular, and it is outstanding for building enterprise-level, feature rich, applications.

The application we will be creating is a simple CRUD Sugar Level Tracker. First, we will learn how to build a REST service with our framework of choice, ASP.NET Web API 2. After that, we will implement SPA that will consume our API. Also, let's not forget about security! We will learn how to easily secure our application with an external provider like Okta. In the end, we will have fully functional, and secure application for measuring sugar level.

We will be using .NET Framework 4.7.1 and Visual Studio 2017. Also, you should have Node and npm installed.

Get Started with ASP.NET

First, let's create an API using a built-in template within Visual Studio. Let's start from a scratch.

In Visual Studio, select File -> New Project

New Project Dialog

Dialog to choose Web API as project type.

At this moment, we have a lot of boilerplate code that we really don't need in our application.

Let's clean up the boilerplate code and remove all redundant files and NuGet packages.

Since we are building an API, we don't need UI related files or folders. We can remove all the following:

Project file tree with items to be removed highlighted.

After you have done this, go to your Global.asax file and remove the using statement:

using System.Web.Optimization;

And also remove the Bundles registration part, since we will not be needing these for API:

BundleConfig.RegisterBundles(BundleTable.Bundles);

We will also remove some of the NuGet packages: Microsoft.ApplicationInsights related packages, Modernizr, WebGrease, Antlr, bootstrap, jQuery.

You can do it simply by running the following script into your Package Manager Console

            Uninstall-Package Microsoft.ApplicationInsights.Web Uninstall-Package Microsoft.ApplicationInsights.WindowsServer Uninstall-Package Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel Uninstall-Package Microsoft.ApplicationInsights.PerfCounterCollector Uninstall-Package Microsoft.ApplicationInsights.DependencyCollector Uninstall-Package Microsoft.ApplicationInsights.Agent.Intercept Uninstall-Package Microsoft.ApplicationInsights Uninstall-Package Microsoft.AspNet.Web.Optimization Uninstall-Package bootstrap Uninstall-Package jQuery Uninstall-Package WebGrease Uninstall-Package Antlr Uninstall-Package Modernizr                      

After finishing we should have a clean project solution and perfect starting ground. Our solution should now look like the following image:

New Project Dialog

Install ASP.NET API Project Dependencies

Now, that we have a clean solution let's install all necessary packages and project dependencies. Later we will learn about each of the packages. Again, we will do it using the Package Manager Console by executing the following:

            Install-Package Microsoft.Owin.Host.SystemWeb -Version 4.0.0 Install-Package Microsoft.IdentityModel.Protocols.OpenIdConnect -Version 5.2.1 Install-Package Microsoft.IdentityModel.Tokens -Version 5.2.1 Install-Package Microsoft.Owin.Security.Jwt -Version 4.0.0 Install-Package EntityFramework -Version 6.2.0 Install-Package Microsoft.AspNet.WebApi.Cors -Version 5.2.6 Install-Package Microsoft.AspNet.Identity.Owin -Version 2.2.1                      

Connect with Entity Framework 6

Entity Framework 6 is a mature ORM, built and supported by Microsoft. It allows us to interact with the database without the need to know or execute SQL queries. The classes from our code will map to database tables and will basically define database structure. EntityFramework has a migration concept, which allows us to track changes within our DB models which reflect database structure.

Let's now set up our database connection. We will add a connection string to Web.Config. Make sure to add it inside of <configuration>, after <configSections></configSections>

                          <              connectionStrings              >              <              add              name              =              "OktaConnectionString"              providerName              =              "System.Data.SqlClient"              connectionString              =              "Server=.;Database=Okta; Integrated Security=True;"              />              </              connectionStrings              >                      

Create an ASP.NET Model and DbContext

First, we will need a model for tracking sugar levels. Create a file in the Models folder called SugarLevel.cs.

                          using              System              ;              using              System.ComponentModel.DataAnnotations              ;              namespace              SugarLevelTracker.Models              {              public              class              SugarLevel              {              [              Key              ]              public              int              Id              {              get              ;              set              ;              }              [              Required              ]              public              float              Value              {              get              ;              set              ;              }              [              Required              ]              public              string              Description              {              get              ;              set              ;              }              [              Required              ]              public              DateTime              MeasuredAt              {              get              ;              set              ;              }              }              }                      

As you can see, we have standard property for unique identifier - Id, which will be picked up by Entity Framework and used as primary key. We need Value to store our trackings and Description to associate a name with it. We will also use MeasuredAt to indicate when we measured our sugar level.

Let's create our ApplicationDbContext class, inside of new folder called Data. It will be pretty simple:

                          using              System.Data.Entity              ;              using              SugarLevelTracker.Models              ;              namespace              SugarLevelTracker.Data              {              public              class              ApplicationDbContext              :              DbContext              {              public              ApplicationDbContext              ()              :              base              (              "OktaConnectionString"              )              {              }              public              static              ApplicationDbContext              Create              ()              {              return              new              ApplicationDbContext              ();              }              public              DbSet              <              SugarLevel              >              SugarLevels              {              get              ;              set              ;              }              }              }                      

Our DbContext refers to the connection string, and contains a property for sugar levels that will map to a table in the database.

Set Up Migrations

First, we will enable migrations, run the following in Package Manager Console:

Now we can add our initial migration, that will contain creation of SugarLevel table:

We can update the database now:

Verbose flag will allow us to see the generated SQL statements, and this can help us in resolving errors, if any occur.

Create RESTful ASP.NET API Endpoints

Since we are building a RESTful API for simple CRUD functionality, we will have 5 simple endpoints:

  • Get All sugar levels

  • Get sugar level by ID

  • Update an existing sugar level

  • Create new sugar level

  • Delete existing sugar level

Let's create a controller that will bring to life our endpoints. We will use scaffolding, a powerful feature that comes with Visual Studio. It will create a controller with CRUD actions, based on our model and DbContext.

Right click on Controllers folder, and choose Add -> Controller:

New Project Dialog

We will pick Web API 2 Controller with actions, using Entity Framework. On the next screen we will choose our model and DbContext classes, and tell Visual Studio to create async controller actions. Async controller actions will make use of async/await pattern.

Dialog for settings of scaffolded controller.

Using async/await pattern can help us avoid performance bottlenecks and increase overall responsiveness of our application. The catch with async/await pattern is that we have to use it on all methods of all layers in our application.

Enable CORS for ASP.NET and Angular

Since our client application will be running on a different port than our ASP.NET server, we will need to add CORS middleware to the pipeline.

We will enable CORS globally by adding a following to Register() method of our WebApiConfig class, which is located in the App_start folder.

You will also need to add the using statement for CORS.

                          using              System.Web.Http.Cors              ;                      

Then add the following to the end of the Register() method.

                          // Enable CORS for the Angular App              var              cors              =              new              EnableCorsAttribute              (              "http://localhost:4200"              ,              "*"              ,              "*"              );              config              .              EnableCors              (              cors              );                      

Our SPA application will be served by Angular CLI and will be running at 4200 port we added http://localhost:4200 as the allowed origin. Since we will be running both, the frontend and backend locally, we can trust this origin. Therefore, we can use the * as a wildcard for headers and methods, allowing all headers and methods from this origin. In practice, you would want to only allow really needed headers and methods to your clients (origins).

Set a Default Formatter for ASP.NET Web API 2

Sadly, ASP.NET Web API 2 by default uses XML formatter. We will remove it and make sure JSON formatter is used. Add the following code at the end of Register method inside of WebApiConfig class:

                          // Set JSON formatter as default one and remove XmlFormatter              var              jsonFormatter              =              config              .              Formatters              .              JsonFormatter              ;              jsonFormatter              .              SerializerSettings              .              ContractResolver              =              new              CamelCasePropertyNamesContractResolver              ();              config              .              Formatters              .              Remove              (              config              .              Formatters              .              XmlFormatter              );              jsonFormatter              .              SerializerSettings              .              DateTimeZoneHandling              =              Newtonsoft              .              Json              .              DateTimeZoneHandling              .              Utc              ;                      

You'll also need to add the using statement for the JSON serializer to the file:

                          using              Newtonsoft.Json.Serialization              ;                      

We also want to specify the port the API will be running on so that the Angular application can call it. To do so, right click on the project in the solution explorer and click properties. In the main properties window, choose Web from the left-hand menu and set the Project Url property to http://localhost:8080.

Build SPA application with Angular

First, let's begin by using Angular CLI to create our application. Angular CLI is a great tool when it comes to creating angular apps without spending a considerable amount of time and effort configuring our application. We are not required to be experts at Webpack or waste time configuring project structure, module loaders, environment, and libraries before ever starting to write any code. Angular CLI does all of this for us, so we can focus on building SPA applications. For more information on Angular CLI you can check their documentation.

First we need to install Angular CLI globally by running the following command within Powershell or Command Prompt:

            npm              install              -g              @angular/cli                      

If you right click on the solution explorer, and choose command prompt from the context menu, make sure you are in the SugarLevelTracker folder and run :

            ng new sugar-level-tracker              --directory              Angular                      

This command will create an Angular directory in root folder of project, and inside of that Angular directory the Angular CLI will create the Angular app.

We can bootstrap our app by running following command:

Now we can start building our SPA.

First, let's install the following packages: Angular Material, Angular CDK, Angular Date Time Picker, RxJS Compat

            npm              install              @angular/material @angular/cdk ng-pick-datetime rxjs-compat              --save                      

If the installation went well, we should see packages in package.json dependencies list.

To bring in the styles for Angular Material and the Owl DateTime Picker, put these two statements in the styles.css in the root of the Angular application folder:

                          @import              '~@angular/material/prebuilt-themes/deeppurple-amber.css'              ;              @import              '~ng-pick-datetime/assets/style/picker.min.css'              ;                      

Our app contains only one root module, AppModule, and one component AppComponent. The application we are making is a very simple one. Hence, we will use existing AppModule. If later our application gets more complex we can introduce features, routing and shared modules to better organize our code. Since our application will be able to track sugar level we will need to create an appropriate service: SugarLevelService, and components for listing and editing the sugar levels.

It would be nice to also create a TypeScript model for sugar level, to make it easier for us to work with sugar level objects. Let's first create a folder shared inside of our app folder, which is part of Angular application that gets created by Angular CLI. Inside of the shared folder create a new folder called models, and this is where we will create the SugarLevel.ts file:

                          export              default              class              SugarLevel              {              id              :              number              ;              value              :              number              ;              description              :              string              ;              measuredAt              :              string              ;              }                      

After this, let's create api folder inside of shared folder, and inside of it a new file called sugar-level.service.ts:

                          import              {              Injectable              }              from              '              @angular/core              '              ;              import              {              HttpClient              }              from              '              @angular/common/http              '              ;              import              {              Observable              }              from              '              rxjs/Observable              '              ;              import              SugarLevel              from              '              ../models/SugarLevel              '              ;              @              Injectable              ()              export              default              class              SugarLevelService              {              public              API              =              '              http://localhost:8080/api              '              ;              public              SUGARLEVELS_API              =              `              ${              this              .              API              }              /sugarlevels`              ;              constructor              (              private              http              :              HttpClient              )              {}              getAll              ():              Observable              <              Array              <              SugarLevel              >>              {              return              this              .              http              .              get              <              Array              <              SugarLevel              >>              (              this              .              SUGARLEVELS_API              );              }              get              (              id              :              string              )              {              return              this              .              http              .              get              (              `              ${              this              .              SUGARLEVELS_API              }              /              ${              id              }              `              );              }              save              (              sugarLevel              :              SugarLevel              ):              Observable              <              SugarLevel              >              {              let              result              :              Observable              <              SugarLevel              >              ;              if              (              sugarLevel              .              id              )              {              result              =              this              .              http              .              put              <              SugarLevel              >              (              `              ${              this              .              SUGARLEVELS_API              }              /              ${              sugarLevel              .              id              }              `              ,              sugarLevel              );              }              else              {              result              =              this              .              http              .              post              <              SugarLevel              >              (              this              .              SUGARLEVELS_API              ,              sugarLevel              );              }              return              result              ;              }              remove              (              id              :              number              )              {              return              this              .              http              .              delete              (              `              ${              this              .              SUGARLEVELS_API              }              /              ${              id              .              toString              ()}              `              );              }              }                      

Now we can add sugarlevel-list.component.ts. You can easily scaffold the component using the Angular CLI, by opening a command prompt in the Angular/src/app folder and running:

This tells the Angular CLI (ng) to generate (g) a component (c) called sugarlevel-list. This will generate a folder called sugarlevel-list with four files in it:

sugarlevel-list.component.css sugarlevel-list.component.html sugarlevel-list.component.spec sugarlevel-list.component.ts

The Typescript file is the one we care about at the moment. The contents of the sugarlevel-list.component.ts file should be replaced with:

                          import              {              Component              ,              OnInit              }              from              '              @angular/core              '              ;              import              SugarLevelService              from              '              ../shared/api/sugar-level.service              '              ;              import              SugarLevel              from              '              ../shared/models/SugarLevel              '              ;              @              Component              ({              selector              :              '              app-sugarlevel-list              '              ,              templateUrl              :              '              ./sugarlevel-list.component.html              '              ,              styleUrls              :              [              '              ./sugarlevel-list.component.css              '              ]              })              export              class              SugarLevelListComponent              implements              OnInit              {              sugarLevels              :              Array              <              SugarLevel              >              ;              constructor              (              private              sugarLevelService              :              SugarLevelService              )              {}              ngOnInit              ()              {              this              .              sugarLevelService              .              getAll              ().              subscribe              (              data              =>              {              this              .              sugarLevels              =              data              ;              });              }              }                      

And the code for sugarlevel-list.component.html:

                          <mat-card>              <mat-card-header>Sugar Level List</mat-card-header>              <mat-card-content>              <mat-list>              <mat-list-item              *ngFor=              "let sugarLevel of sugarLevels"              >              <h3              mat-line              >              <a              mat-button              [routerLink]=              "['/sugarlevel-edit', sugarLevel.id]"              >              -              </a>              </h3>              </mat-list-item>              </mat-list>              </mat-card-content>              <button              mat-fab              color=              "primary"              [routerLink]=              "['/sugarlevel-add']"              >Add</button>              </mat-card>                      

We can now use the same command that we used to generate the list component to create and edit component:

Replace the code in sugarlevel-edit.component.ts with:

                          import              {              Component              ,              OnDestroy              ,              OnInit              }              from              '              @angular/core              '              ;              import              {              Subscription              }              from              '              rxjs/Subscription              '              ;              import              {              ActivatedRoute              ,              Router              }              from              '              @angular/router              '              ;              import              {              NgForm              }              from              '              @angular/forms              '              ;              import              SugarLevelService              from              '              ../shared/api/sugar-level.service              '              ;              import              SugarLevel              from              '              ../shared/models/SugarLevel              '              ;              @              Component              ({              selector              :              '              app-sugarlevel-edit              '              ,              templateUrl              :              '              ./sugarlevel-edit.component.html              '              ,              styleUrls              :              [              '              ./sugarlevel-edit.component.css              '              ]              })              export              class              SugarLevelEditComponent              implements              OnInit              ,              OnDestroy              {              sugarLevel              :              SugarLevel              =              new              SugarLevel              ();              sub              :              Subscription              ;              constructor              (              private              route              :              ActivatedRoute              ,              private              router              :              Router              ,              private              sugarLevelService              :              SugarLevelService              )              {}              ngOnInit              ()              {              this              .              sub              =              this              .              route              .              params              .              subscribe              (              params              =>              {              const              id              =              params              [              '              id              '              ];              if              (              id              )              {              this              .              sugarLevelService              .              get              (              id              ).              subscribe              ((              sugarLevel              :              any              )              =>              {              if              (              sugarLevel              )              {              this              .              sugarLevel              =              sugarLevel              ;              this              .              sugarLevel              .              measuredAt              =              new              Date              (              this              .              sugarLevel              .              measuredAt              ).              toISOString              ();              }              else              {              console              .              log              (              `Sugar Level with id '              ${              id              }              ' not found, returning to list`              );              this              .              gotoList              ();              }              });              }              });              }              ngOnDestroy              ()              {              this              .              sub              .              unsubscribe              ();              }              gotoList              ()              {              this              .              router              .              navigate              ([              '              /sugarlevel-list              '              ]);              }              save              (              form              :              any              )              {              this              .              sugarLevelService              .              save              (              form              ).              subscribe              (              result              =>              {              this              .              gotoList              ();              },              error              =>              console              .              error              (              error              )              );              }              remove              (              id              :              number              )              {              this              .              sugarLevelService              .              remove              (              id              ).              subscribe              (              result              =>              {              this              .              gotoList              ();              },              error              =>              console              .              error              (              error              )              );              }              }                      

Code for sugarlevel-edit.component.html:

                          <mat-card>              <form              #sugarLevelForm              ="              ngForm              "              (ngSubmit)=              "save(sugarLevelForm.value)"              >              <mat-card-header>              <mat-card-title>              <h2>{{sugarLevel.value ? 'Edit' : 'Add'}} Sugar Level</h2>              </mat-card-title>              </mat-card-header>              <mat-card-content>              <input              type=              "hidden"              name=              "id"              [(ngModel)]=              "sugarLevel.id"              >              <mat-form-field>              <input              matInput              placeholder=              "Sugar Level Description"              [(ngModel)]=              "sugarLevel.description"              required              name=              "description"              >              </mat-form-field>              <mat-form-field>              <input              matInput              placeholder=              "Sugar Level Value"              [(ngModel)]=              "sugarLevel.value"              required              name=              "value"              >              </mat-form-field>              <mat-form-field              class=              "col-xs-12"              >              <input              matInput              [(ngModel)]=              "sugarLevel.measuredAt"              [owlDateTime]=              "dt2"              [owlDateTimeTrigger]=              "dt2"              placeholder=              "Measured At"              name=              "measuredAt"              required              >              <owl-date-time              #dt2              ></owl-date-time>              <mat-error>              Required              </mat-error>              </mat-form-field>              </mat-card-content>              <mat-card-actions>              <button              mat-raised-button              color=              "primary"              type=              "submit"              [disabled]=              "!sugarLevelForm.form.valid"              >Save              </button>              <button              mat-raised-button              color=              "secondary"              (click)=              "remove(sugarLevel.id)"              *ngIf=              "sugarLevel.id"              type=              "button"              >Delete              </button>              <a              mat-button              routerLink=              "/sugarlevel-list"              >Cancel</a>              </mat-card-actions>              <mat-card-footer>              </mat-card-footer>              </form>              </mat-card>                      

After adding components we will need to navigate between them by implementing routing. First, we will import the RouterModule module and Routes type. Then we will create a route configuration of Routes type, and after, we will register RouterModule with route configuration on the root level.

Inside of app.module.ts, let's add the routing configuration right at the top, below the import statements:

                          const              appRoutes              :              Routes              =              [              {              path              :              ''              ,              redirectTo              :              '              /sugarlevel-list              '              ,              pathMatch              :              '              full              '              },              {              path              :              '              sugarlevel-list              '              ,              component              :              SugarLevelListComponent              },              {              path              :              '              sugarlevel-add              '              ,              component              :              SugarLevelEditComponent              },              {              path              :              '              sugarlevel-edit/:id              '              ,              component              :              SugarLevelEditComponent              }              ];                      

Inside of imports array, we will add the following:

RouterModule.forRoot(appRoutes)

You'll also need to import some components from the @angular/router, @angular/material and the ng-pick-datetime packages for the HTML files that were added.

This is how app.module.ts file should look after all this:

                          import              {              BrowserModule              }              from              '              @angular/platform-browser              '              ;              import              {              NgModule              }              from              '              @angular/core              '              ;              import              {              HttpClientModule              }              from              '              @angular/common/http              '              ;              import              {              MatButtonModule              ,              MatCardModule              ,              MatInputModule              ,              MatListModule              ,              MatToolbarModule              }              from              '              @angular/material              '              ;              import              {              BrowserAnimationsModule              }              from              '              @angular/platform-browser/animations              '              ;              import              {              Routes              ,              RouterModule              }              from              '              @angular/router              '              ;              import              {              FormsModule              }              from              '              @angular/forms              '              ;              import              {              OwlDateTimeModule              ,              OwlNativeDateTimeModule              }              from              '              ng-pick-datetime              '              ;              import              {              AppComponent              }              from              '              ./app.component              '              ;              import              {              SugarLevelListComponent              }              from              '              ./sugarlevel-list/sugarlevel-list.component              '              ;              import              {              SugarLevelEditComponent              }              from              '              ./sugarlevel-edit/sugarlevel-edit.component              '              ;              import              SugarLevelService              from              '              ./shared/api/sugar-level.service              '              ;              const              appRoutes              :              Routes              =              [              {              path              :              ''              ,              redirectTo              :              '              /sugarlevel-list              '              ,              pathMatch              :              '              full              '              },              {              path              :              '              sugarlevel-list              '              ,              component              :              SugarLevelListComponent              },              {              path              :              '              sugarlevel-add              '              ,              component              :              SugarLevelEditComponent              },              {              path              :              '              sugarlevel-edit/:id              '              ,              component              :              SugarLevelEditComponent              }              ];              @              NgModule              ({              declarations              :              [              AppComponent              ,              SugarLevelListComponent              ,              SugarLevelEditComponent              ],              imports              :              [              BrowserModule              ,              HttpClientModule              ,              MatButtonModule              ,              MatCardModule              ,              MatInputModule              ,              MatListModule              ,              MatToolbarModule              ,              BrowserAnimationsModule              ,              FormsModule              ,              RouterModule              .              forRoot              (              appRoutes              ),              OwlDateTimeModule              ,              OwlNativeDateTimeModule              ],              providers              :              [              SugarLevelService              ],              bootstrap              :              [              AppComponent              ]              })              export              class              AppModule              {}                      

We will use the service, SugarLevelService to enable our components to communicate with our REST API. Also, it's important to note that Angular HttpClient uses RxJS observables instead of promises, so the sooner we learn RxJS the better. SugarLevelService is registered in the AppModule, as a singleton service and is available everywhere across our app.

Create an Okta Application

Dealing with user authentication in web apps is a massive pain for every developer. This is where Okta shines: it helps you secure your web applications with minimal effort. To get started, you'll need to create an OpenID Connect application in Okta. Sign up for a forever-free developer account (or log in if you already have one).

Okta Signup Page

Once you've logged in and landed on the dashboard page, copy down the Org URL pictured below. You will need this later.

Okta dashboard with org url highlighted.

Then create a new application by browsing to the Applications tab and clicking Add Application, and from the first page of the wizard choose Single-Page App.

Create new app with SPA selected.

On the settings page, enter the following values:

  • Name: AngularCrudApp
  • Base URIs: http://localhost:4200
  • Login redirect URIs: http://localhost:4200/implicit/callback

You can leave the other values unchanged, and click Done.

New Project Dialog

Now that your application has been created, copy down the Client ID and Client secret values on the following page, you'll need them soon (of course, yours will be different).

Okta client credentials screenshot dialog.

Secure Your ASP.NET Core + Angular Application

If you don't already have a Startup.cs file (OWIN Startup class), create one by right-clicking on your project and choosing Add - Class. Pick the OWIN Startup template and name the new class Startup.

Make sure you have these using statements at the top of your Startup.cs file:

                          using              Microsoft.IdentityModel.Protocols              ;              using              Microsoft.IdentityModel.Protocols.OpenIdConnect              ;              using              Microsoft.IdentityModel.Tokens              ;              using              Microsoft.Owin              ;              using              Microsoft.Owin.Security              ;              using              Microsoft.Owin.Security.Jwt              ;              using              Owin              ;              using              System.Threading.Tasks              ;                      

Add the following code to your Configuration method:

                          public              void              Configuration              (              IAppBuilder              app              )              {              // Configure JWT Bearer middleware              // with an OpenID Connect Authority              var              authority              =              "                  https://{yourOktaDomain}                /oauth2/default"              ;              var              configurationManager              =              new              ConfigurationManager              <              OpenIdConnectConfiguration              >(              authority              +              "/.well-known/openid-configuration"              ,              new              OpenIdConnectConfigurationRetriever              (),              new              HttpDocumentRetriever              ());              app              .              UseJwtBearerAuthentication              (              new              JwtBearerAuthenticationOptions              {              AuthenticationMode              =              AuthenticationMode              .              Active              ,              TokenValidationParameters              =              new              TokenValidationParameters              {              ValidAudience              =              "api://default"              ,              ValidIssuer              =              authority              ,              IssuerSigningKeyResolver              =              (              token              ,              securityToken              ,              identifier              ,              parameters              )              =>              {              var              discoveryDocument              =              Task              .              Run              (()              =>              configurationManager              .              GetConfigurationAsync              ()).              GetAwaiter              ().              GetResult              ();              return              discoveryDocument              .              SigningKeys              ;              }              }              });              }                      

Protect Your ASP.NET API Endpoints

Now it's time to protect all our endpoints for SugarLevel controller. Since we want to protect all actions we will simply apply the Authorize attribute at the controller level. We could also apply the attribute to specific actions, or even globally for all controllers and actions. If we applied it globally, we could exclude Authorize on specific actions and controllers by using the AllowAnonymous attribute.

Implement Authentication in Angular

To add authentication to our Angular app we will use Okta's Angular SDK.

            npm              install              @okta/okta-angular              --save                      

In the app.module.ts file, add a config variable right below the appRoutes for the application.

                          const              config              =              {              issuer              :              '                                                https://{yourOktaDomain}                /oauth2/default              '              ,              redirectUri              :              '              http://localhost:4200/implicit/callback              '              ,              clientId              :              '              {clientId}              '              };                      

The OktaAuthModule will use that configuration. Import the module first:

                          import              {              OktaAuthModule              }              from              '              @okta/okta-angular              '              ;                      

Then add the initialization to the imports array.

                          OktaAuthModule              .              initAuth              (              config              );                      

We will also create AuthInterceptor and add it to the AppModule providers array. AuthInterceptor is used to append the authorization header to our requests.

Inside of our shared folder we will create a new folder called interceptors, and there we will create our auth.interceptor.ts file:

                          import              {              Injectable              }              from              '              @angular/core              '              ;              import              {              HttpEvent              ,              HttpHandler              ,              HttpInterceptor              ,              HttpRequest              }              from              '              @angular/common/http              '              ;              import              {              Observable              }              from              '              rxjs/Observable              '              ;              import              '              rxjs/add/observable/fromPromise              '              ;              import              {              OktaAuthService              }              from              '              @okta/okta-angular              '              ;              @              Injectable              ()              export              class              AuthInterceptor              implements              HttpInterceptor              {              constructor              (              private              oktaAuth              :              OktaAuthService              )              {}              intercept              (              request              :              HttpRequest              <              any              >              ,              next              :              HttpHandler              ):              Observable              <              HttpEvent              <              any              >>              {              return              Observable              .              fromPromise              (              this              .              handleAccess              (              request              ,              next              ));              }              private              async              handleAccess              (              request              :              HttpRequest              <              any              >              ,              next              :              HttpHandler              ):              Promise              <              HttpEvent              <              any              >>              {              const              accessToken              =              await              this              .              oktaAuth              .              getAccessToken              ();              request              =              request              .              clone              ({              setHeaders              :              {              Authorization              :              '              Bearer                            '              +              accessToken              }              });              return              next              .              handle              (              request              ).              toPromise              ();              }              }                      

Then wire up the interceptor to intercept requests as they go out by adding it to the app module so that the import for common http looks like:

                          import              {              HTTP_INTERCEPTORS              ,              HttpClientModule              }              from              '              @angular/common/http              '              ;                      

And then add the interceptor to the providers:

On AppComponent and HomeComponent initialization we use Okta's authentication state and provide a callback that will be executed any time state is updated. If our app isn't authenticated HomeComponent will render Login button with a link to redirect URL for us to authenticate. If the user is authenticated our HomeComponent will redirect the user to SugarLevelListComponent.

Generate the home component as before:

Then add the code for home.component.ts:

                          import              {              Component              ,              OnInit              }              from              '              @angular/core              '              ;              import              {              OktaAuthService              }              from              '              @okta/okta-angular              '              ;              @              Component              ({              selector              :              '              app-home              '              ,              templateUrl              :              '              ./home.component.html              '              ,              styleUrls              :              [              '              ./home.component.css              '              ]              })              export              class              HomeComponent              implements              OnInit              {              isAuthenticated              :              boolean              ;              constructor              (              private              oktaAuth              :              OktaAuthService              )              {}              async              ngOnInit              ()              {              this              .              isAuthenticated              =              await              this              .              oktaAuth              .              isAuthenticated              ();              // Subscribe to authentication state changes              this              .              oktaAuth              .              $authenticationState              .              subscribe              (              (              isAuthenticated              :              boolean              )              =>              (              this              .              isAuthenticated              =              isAuthenticated              )              );              }              }                      

Also, the code for home.component.html:

                          <mat-card>              <mat-card-content>              <button              mat-raised-button              color=              "warn"              *ngIf=              "!isAuthenticated"              (click)=              "oktaAuth.loginRedirect()"              >              Login              </button>              <button              mat-raised-button              color=              "warn"              *ngIf=              "isAuthenticated"              [routerLink]=              "['/sugarlevel-list']"              >              Sugar Level List              </button>              <button              mat-raised-button              color=              "warn"              *ngIf=              "isAuthenticated"              (click)=              "oktaAuth.logout()"              >              Login              </button>              </mat-card-content>              </mat-card>                      

Replace all the code in the app.component.html with this:

                          <router-outlet></router-outlet>                      

This tells Angular where to put the output from the components that are handling routing.

Then replace the component handling the '' path with the home component in the routing array in app.component.ts:

                          {              path              :              ''              ,              component              :              HomeComponent              ,              pathMatch              :              '              full              '              },                      

Also, import the OktaCallbackComponent from Okta's Angular SDK and add the callback route to the app component:

                          {              path              :              '              implicit/callback              '              ,              component              :              OktaCallbackComponent              }                      

So the final app.module.ts looks like:

                          import              {              BrowserModule              }              from              '              @angular/platform-browser              '              ;              import              {              NgModule              }              from              '              @angular/core              '              ;              import              {              HTTP_INTERCEPTORS              ,              HttpClientModule              }              from              '              @angular/common/http              '              ;              import              {              MatButtonModule              ,              MatCardModule              ,              MatInputModule              ,              MatListModule              ,              MatToolbarModule              }              from              '              @angular/material              '              ;              import              {              Routes              ,              RouterModule              }              from              '              @angular/router              '              ;              import              {              FormsModule              }              from              '              @angular/forms              '              ;              import              {              OwlDateTimeModule              ,              OwlNativeDateTimeModule              }              from              '              ng-pick-datetime              '              ;              import              {              OktaAuthModule              ,              OktaCallbackComponent              }              from              '              @okta/okta-angular              '              ;              import              {              AppComponent              }              from              '              ./app.component              '              ;              import              {              SugarLevelListComponent              }              from              '              ./sugarlevel-list/sugarlevel-list.component              '              ;              import              {              SugarLevelEditComponent              }              from              '              ./sugarlevel-edit/sugarlevel-edit.component              '              ;              import              SugarLevelService              from              '              ./shared/api/sugar-level.service              '              ;              import              {              HomeComponent              }              from              '              ./home/home.component              '              ;              import              {              AuthInterceptor              }              from              '              ./shared/interceptors/auth.interceptor              '              ;              const              appRoutes              :              Routes              =              [              {              path              :              ''              ,              component              :              HomeComponent              ,              pathMatch              :              '              full              '              },              {              path              :              '              sugarlevel-list              '              ,              component              :              SugarLevelListComponent              },              {              path              :              '              sugarlevel-add              '              ,              component              :              SugarLevelEditComponent              },              {              path              :              '              sugarlevel-edit/:id              '              ,              component              :              SugarLevelEditComponent              },              {              path              :              '              implicit/callback              '              ,              component              :              OktaCallbackComponent              }              ];              const              config              =              {              issuer              :              '                                                https://{yourOktaDomain}                /oauth2/default              '              ,              redirectUri              :              '              http://localhost:4200/implicit/callback              '              ,              clientId              :              '              {clientId}              '              };              @              NgModule              ({              declarations              :              [              AppComponent              ,              SugarLevelListComponent              ,              SugarLevelEditComponent              ,              HomeComponent              ],              imports              :              [              BrowserModule              ,              HttpClientModule              ,              MatButtonModule              ,              MatCardModule              ,              MatInputModule              ,              MatListModule              ,              MatToolbarModule              ,              FormsModule              ,              RouterModule              .              forRoot              (              appRoutes              ),              OwlDateTimeModule              ,              OwlNativeDateTimeModule              ,              OktaAuthModule              .              initAuth              (              config              )              ],              providers              :              [              SugarLevelService              ,              {              provide              :              HTTP_INTERCEPTORS              ,              useClass              :              AuthInterceptor              ,              multi              :              true              }              ],              bootstrap              :              [              AppComponent              ]              })              export              class              AppModule              {}                      

By using Okta to handle authorization we don't need to keep auth state ourselves, which makes building app much simpler.

And now you're done! You have a secure ASP.NET Web API with an Angular frontend to track sugar levels (or whatever else you might want to track!). The complete and working sample that backs this tutorial is available on GitHub as ASPNET MVC Angular CRUD Example.

Learn More About ASP.NET Core and Angular

If you enjoyed building this ASP.NET Core API with Angular, check out more full-stack CRUD posts from Okta.

  • Angular 6 – What's New and Why Upgrade?
  • Token Authentication in ASP.NET Core – A Complete Guide
  • Build a Secure CRUD App with ASP.NET Core and React

As always if you have any questions, comments, or concerns about this post feel free to leave a comment below. For other great content from the Okta Dev Team, follow us on Twitter @OktaDev, Facebook, and watch us on YouTube!

How To Create A Web Api In Visual Studio 2015

Source: https://developer.okta.com/blog/2018/07/27/build-crud-app-in-aspnet-framework-webapi-and-angular

Posted by: brownbefor1967.blogspot.com

0 Response to "How To Create A Web Api In Visual Studio 2015"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel