Vidly3 Notes -...

50
Vidly3 Notes Github: https://github.com/mosh-hamedani/vidly-mvc-5 Avoid Null Pointer Exceptions: https://www.journaldev.com/14544/java-lang-nullpointerexception#how- to-fix-javalangnullpointerexception Section 1 Working with Data Passing Data to Views different ways The main common way to pass data to a view is to just pass the object to the View. MovieController //before creating ViewModel public ActionResult Random() { var movie = new Movie() { Name = "Shrek!" }; return View(movie); } Random.cshtml @model Vidly3.Models.Movie @{ ViewBag.Title = "Random"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>@Model.Name</h2> ViewData View Data is another way, however must be cast. Do not use ViewData. MoviesController: //Passing data to views using ViewData 46:37 ViewData has casting issues. public ActionResult Random() { var movie = new Movie() { Name = "Shrek!" }; ViewData["Movie"] = movie; return View(); } Random.cshtml: @model Vidly3.Models.Movie @using Vidly3.Models

Transcript of Vidly3 Notes -...

Page 1: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Vidly3 NotesGithub: https://github.com/mosh-hamedani/vidly-mvc-5

Avoid Null Pointer Exceptions: https://www.journaldev.com/14544/java-lang-nullpointerexception#how-to-fix-javalangnullpointerexception

Section 1 Working with DataPassing Data to Views different waysThe main common way to pass data to a view is to just pass the object to the View.

MovieController

//before creating ViewModel      public ActionResult Random()      {          var movie = new Movie() { Name = "Shrek!" };          return View(movie);      }Random.cshtml

@model Vidly3.Models.Movie   @{    ViewBag.Title = "Random";    Layout = "~/Views/Shared/_Layout.cshtml";} <h2>@Model.Name</h2>

ViewDataView Data is another way, however must be cast. Do not use ViewData.

MoviesController:

//Passing data to views using ViewData 46:37 ViewData has casting issues.       public ActionResult Random()      {          var movie = new Movie() { Name = "Shrek!" };           ViewData["Movie"] = movie;          return View();      }Random.cshtml:

@model Vidly3.Models.Movie    @using Vidly3.Models@{    ViewBag.Title = "Random";    Layout = "~/Views/Shared/_Layout.cshtml";}

Page 2: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

<h2>@( ((Movie)ViewData["Movie"]).Name)</h2>

ViewBagView bag is another way however it must use Magic properties. Do not use ViewBag

MoviesController:

//ViewBag Dynamic Type 48.53 ViewBag uses Magic Property - no compile time safety - still has casting problem.       public ActionResult Random()       {           var movie = new Movie() { Name = "Shrek!" };                       ViewBag.RandomMovie = movie;           return View();       }

Random.csthtml

ViewModelsView models allow us to access and combine multiple models. It allows us to combine Customer and Genre properties for example and then access those properties for our view. Where we can access just the properties we require for that view. In our example below in RandomMovieViewModel we have combined a movie and a list of customers.

MoviesController:

// GET: Movies/Randompublic ActionResult Random(){    var movie = new Movie() { Name = "Shrek!" };    var customers = new List<Customer>        {            new Customer { Name = "Customer 1" },            new Customer { Name = "Customer 2" }        };     var viewModel = new RandomMovieViewModel

Page 3: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

    {        Movie = movie,        Customers = customers    };     return View(viewModel);}

RandomMovieViewModel:

using System;using System.Collections.Generic;using System.Linq;using System.Web;using Vidly3.Models; namespace Vidly3.ViewModels{    public class RandomMovieViewModel    {        public Movie Movie { get; set; }        public List<Customer> Customers { get; set; }    }}

Page 4: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Section 2 Building forms

The Mark-upFirst we need an action.In Controllers open CustomersController

Add a new Action using the shortcut mvcaction4 and then press tab to create an action called New.

//Form to create a new customer       public ActionResult New()       {           var membershiptypes = _context.MembershipTypes.ToList();           var viewModel = new NewCustomerViewModel           {               MembershipTypes = membershiptypes           };           return View(viewModel);       }

Add a new View called New Customer

Page 5: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Form LabelsAmending the forms there are two options.

1. Apply a data annotation to the model with – the problem is that this needs to be recompiled.

a. [Display(Name="Date of Birth")]     public DateTime? Birthdate { get; set; }

2. The other way is in the form itself we can manually add a label.

a. <div class="form-group">       @Html.LabelFor(m => m.Customer.Birthdate)       <label>Date of Birth</label>       @Html.TextBoxFor(m => m.Customer.Birthdate,  new { @class = "form-control" })

    </div>

Focus: When you click on the label and it defaults to the textbox.

If the label is used, the textbox does not focus when you click on the label, unless you add for.

3. <label for="Birthdate">Date of Birth</label>

Drop down ListsAdding a dropdown list for our membership type in the Customer form.

In the Customer Controller we need to get the list of Membershiptypes from the database.

        public ActionResult New()        {            var membershiptypes = _context.MembershipTypes.ToList();            var viewModel = new CustomerFormViewModel            {                MembershipTypes = membershiptypes            };            return View("CustomerForm",viewModel);        }

You may find that when you try to add the _context.Membershiptype it is not found, this is because there is no DBSet for MembershipTypes in the IndentityModel“_context.MembershipTypes”

We need to then go and add a DBSet into IdentityModels go to the ApplicationDBContext and add the highlighted line:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>    {        public DbSet <Customer> Customers { get; set; }        public DbSet <Movie> Movies { get; set; }        public DbSet <MembershipType> MembershipTypes { get; set; }

So we can access all the data for this view we must use ViewModels, that encapsulates all the data for this view. We use IEnumerable, because we only need to iterate over the membership types we do not need to add or remove or any other array functions.

public class NewCustomerViewModel    {        public IEnumerable <MembershipType> MembershipTypes { get; set; }

Page 6: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

        public Customer Customer { get; set; }    }

Back to our CustomerController Action

var viewModel = new CustomerFormViewModel

{     MembershipTypes = membershiptypes};

In the New View we need to add to the start of the View

@model Vidly3.ViewModels.NewCustomerViewModel

@using (@Html.BeginForm("Create", "Customers")){    <div class="form-group">        @Html.LabelFor(m => m.Customer.Name)        @Html.TextBoxFor(m => m.Customer.Name, new { @class = "form-control" })     </div>    <div class="form-group">        @Html.LabelFor(m => m.Customer.Birthdate)        @Html.TextBoxFor(m => m.Customer.Birthdate, "{0:d MMM yyyy}", new { @class = "form-control" })     </div>     <div class="form-group">        @Html.LabelFor(m => m.Customer.MembershipTypeId)        @Html.DropDownListFor(m => m.Customer.MembershipTypeId, new SelectList(Model.MembershipTypes, "Id", "Name"), "Select Membership Type", new { @class = "form-control" })    </div>     <div class="checkbox">        <label>            @Html.CheckBoxFor(m => m.Customer.IsSubscribedToNewsletter) Subscribed to Newsletter?        </label>    </div>     <button type="submit" class="btn btn-primary">Save</button>}

Remember to override how it displays on the form in MembershipTypeID in Customers Model

[Display(Name="Membership Type")]     public byte MembershipTypeId { get; set; }

Page 7: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word
Page 8: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Model BindingThe form is ready, now let’s add a button and implement saving a customer.

In the New.cshtml form add a button at the bottom of the form for the submit button to save

<button type="submit" class="btn btn-primary">Save</button>

In the CustomerController implement a new Action with “mvcaction4+tab” shortcut. Change its name to Create.

public ActionResult Create(NewCustomerViewModel viewModel)      {          return View();      }

To start the model binding first we enter the name of the viewmodel so NewCustomerViewModel and its type viewModel

We run the debugger and you can check the properties that are being sent.

public ActionResult Create(NewCustomerViewModel viewModel)

when you run this in debug mode, you can then change to Customer and customer, and .NET is smart enough to know what we meant.

Place a breakpoint on the return view line and run in debug mode.

We can see that the data was sent

MembershipType is Null

Page 9: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Stop the debugger if we change the type of the parameters to Customer customer .NET is smart enough to bind this object to form data because all the keys where prefixed with Customer

We must declare that [HttpPost] because we do not want a user to be able to use HttpGet because of security reasons. So, we declare that this action can only use Post.

[HttpPost]       public ActionResult Create(Customer customer)

Now we can fix the rest of the data:

[HttpPost]      public ActionResult Create(Customer customer)      {          _context.Customers.Add(customer);          _context.SaveChanges();          return RedirectToAction("Index", "Customers");      }

Page 10: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Saving DataTo add the customer to the Database, first we have to add our dbcontext

public ActionResult Create(Customer customer)       {           return View();       }Add:

_context.Customers.Add(customer);        return View();

Generated SQL statements at runtime, All these statements are wrapped in a transaction, all these changes will be persisted to the database, or none will be persisted.

Then to persist we need to call SaveChanges:

_context.Customers.Add(customer);         _context.SaveChanges();         return View();

Finally, We need to redirect to the list of Customers once complete.

return RedirectToAction("Index", "Customers");

Edit FormWe are going to change the Read Only Details action which when we click on a Customer takes us to a Read Only Details page to the Edit action which will take us to a Edit Form with Editable Edit Text fields.

<tbody>          @foreach (var customer in Model)          {              <tr>                  

           <td>@Html.ActionLink(customer.Name, "Details", "Customers", new  { id = customer.Id })</td>

                  <td>@customer.MembershipType.DiscountRate%</td>              </tr>          }      </tbody>

Page 11: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

We are going to change the details action to an Edit action.

<tbody>           @foreach (var customer in Model)           {               <tr>                                      <td>@Html.ActionLink(customer.Name, "Edit", "Customers", new {  id = customer.Id }, null)</td>                   <td>@customer.MembershipType.Name</td>                   <td>@customer.MembershipType.DiscountRate%</td>               </tr>           }       </tbody>

Updating DataHow to update this customer. We can rename this current action to Save and use it for both Saving a customer and updating a customer, or we can create another Action called Update, and then both can have their own actions for Save and update.

We are going to use the first approach as it is easiest and change the Action to a Save action.

public ActionResult Create(Customer customer)       {           _context.Customers.Add(customer);           _context.SaveChanges();            return RedirectToAction("Index", "Customers");       }Rename action to Save

Does this customer have an ID or not? Otherwise we should update it.

Add an If statement. Note that we are using Single instead of SingleOrDefault because so if the given customer is not found, it will throw an exception. We do not want to deal with a situation where the customer is not found.

Finally, we call Save Changes.

public ActionResult Save(Customer customer)       {           if(customer.Id ==0)           _context.Customers.Add(customer);           else           {           var customerInDb = _context.Customers.Single(c => c.Id == customer.Id);           }                          _context.SaveChanges();

Page 12: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

           return RedirectToAction("Index", "Customers");       }

Section 3 Implementing ValidationVideo Custom Validation

Implement in Customer.cs Model file

[Min18YearsIfAMember][Display(Name="Date of Birth")]public DateTime? Birthdate { get; set; }

1. Create a new Validation Class called Min18YearsIfAMember – We need to check the selected membership type.

2. Derive that class from ValidationAttributea. public class Min18YearsIfAMember: ValidationAttribute

    {3. Validation context gives us access to the containing class ‘Customer’

a. validationContext.ObjectInstance;4. Next Cast it to Customer

a. var customer = (Customer)validationContext.ObjectInstance;5. First, we need to check if customer membership is valid with if statement. Check if

membership type is Pay as you go if its Pay as you go, this is allowed for Children under 18 years.

a. if(customer.MembershipTypeId == 1)          {               return ValidationResult.Success;           }

6. Next, we need to check the Birthdate.a. if(customer.Birthdate == null)

            {                return new ValidationResult("Birthdate is required");            }

b.7. Calculate the age then return only if above 18 years of age.

a. var age = DateTime.Today.Year - customer.Birthdate.Value.Year;      //.Value because it is nullable

b. return (age >= 18)? ValidationResult.Success : new ValidationResult("Customer should be at least 18 years old to register for this membershiptype");

Now working moving on

Magic NumbersWe need to clean our code.

Not use magic numbers like 0 or 1

In MembershipType.cs add:

public static readonly byte Unknown = 0;

Page 13: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

public static readonly byte PayAsYouGo = 1;

Page 14: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Validation SummaryAll errors will be copied to the top header.

In CustomerForm.cshtml and MoviesForm.cshtml at the top add the Validation Summary.

@Html.ValidationSummary()

Remember we had byte assigned for MembershipType which was implicitly required? This is the same with ID in CustomerController, if its instantiated, the values will be created to be 0, then it will have a value, and the error will be gone.

To get rid of the ID Validation we need to go back to CustomerController, in New Action, we have declared the MembershipTypes but not the Customer so instantiate Customer.

//Form to create a new customer       public ActionResult New()       {           var membershiptypes = _context.MembershipTypes.ToList();           var viewModel = new CustomerFormViewModel           {               Customer = new Customer(),               MembershipTypes = membershiptypes           };           return View("CustomerForm",viewModel);       }

Client-Side ValidationsServer-side Validations on the server are crucial for secure applications, however Doing validations on the Client has many benefits though. Such as immediate feedback for responsive application, and also no wasted server-side resources AND there are also security reasons for also keeping server side validations. To do this we must reference jqueryval located in BundleConfig

1. Immediate feedback.2. No wasted server-side resources.3. Security.

I have first run the application with Server Side Validation and checked the network tab to see any Server Side Validation.

If you check in the _Layout file you can see this line of code:

@RenderSection("scripts", required: false)

This Render section, above allows us to add scripts to our views, and what is put there will be rendered in the page. On pages where jquery validation like forms is required we can reference our Jquery validation bundle to enable client side validation.

Page 15: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Server Side ValidationWith Server Side validation, you can see there was where it says Save a request sent to the server

Page 16: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Client Side ValidationNow I have applied Client Side Validation by adding:

In CustomerForm.cshtml:

@section scripts

{

@Scripts.Render("~/bundles/jqueryval")}

Referencing in BundlesConfig.cs

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(                     "~/Scripts/jquery.validate*"));

Now when we run the application you can see there is no response sent from the Server.

Page 17: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Anti Forgery TokensTo add antiforgery tokens that will compare a generated code with one that is stored in the users cookies to prevent a hacker from stealing a session, we must add

In the CustomerForm in the Hidden field section

//hidden field [email protected](m => m.Customer.Id)@Html.AntiForgeryToken()<button type="submit" class="btn btn-primary">Save</button>

In the Customer Controller

[HttpPost]//Create was changed to Save        [ValidateAntiForgeryToken]        public ActionResult Save(Customer customer)        {            //Checking Validations or CustomerForm            if (!ModelState.IsValid)……

As you can see the random generated encrypted code stored in the cookie can be seen, starting “52r79vYW….

Page 18: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

If you wanted to test to see if we can hack our site.

Run the application, and inspect element and modify the cookie code.

Once it is modified it is simulating if a hacker did not have the Anti forgery code.

An Exception is received.

Is this the reason why drop downs and selectors may be more beneficial because the edit text input fields have this problem where a hacker could insert code? Anywhere we have an Edit Text field we should use Anti Forgery tokens for our cookies.

Page 19: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Section 3 Review Video1. Add validation to the Movie form and Movie Controller2. Ensure the following:

Release date is automatically entered Number in stock is initialised to 0 Don’t worry about these default numbers Validation, all fields are required. Number in stock if 0 is entered, a validation says number must be between 1 and 20

3. Add Client-Side Validation DONE4. Add Anti Forgery token DONE5. Add to the number in stock a range annotation ensuring it is between 1-20 DONE

Working Made a copy of this version of the project.

Now viewing Code Review video

Section 4 Building Restful ServicesWhat is a Web APIWhen a request arrives at our application, MVC framework hands off that request to an Action in the Controller, this action will return a view. Which is parsed by Razor view engine and then HTML mark-up is generated on the server and returned to the Client. The alternative way is to generate the HTML view on the client itself and send only the Data to the Client. Each Client will be responsible for generating their own views.

Request is sent to our application

Page 20: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

HTML is generated on the Server and sent back to the Client.

There is another way though. The Data can be sent back to the Clients and each Client can generate that HTML View on the Client Itself saving Server resources.

Data Generated on the Client using Web API

A Web API allows data to be sent and then processed by the Client itself rather than the server generating an entire HTML page and sending that entire HTML page to the client. This saves server resources.

There are a number of benefits:

Page 21: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

1. Less server resources (Improved scalability)2. Less bandwidth (Improved performance)3. Improved support for a broad range of clients – Web Client, mobile app, tablet app –

generated locally

ASP .NET Web APIASP .NET Core has merged ASP .NET MVC with ASP.NET Web API

JQuery Plugin Data Table will allow us to send data.

Type of Request Address DescriptionGET /api/customers Get list of Customers

GET /api/customers/1 Get a customer add the id

POST /api/customers Add a customer send http POST to this end point – POST to create

PUT /api/customers/1 Save a customer

DELETE /api/customers/1 Delete a customerBuilding an APIAdd a new Folders to Controllers called API. Right click the folder click add Controller select “WebAPI 2 Controller – Empty”

Open Global.asax – at the application start add the following lines to the beginning of application start.

GlobalConfiguration.Configure(WebApiConfig.Register)

Create a new Folder in Controllers called “Api”

Create a new API Controller in the new “Api” folder called CustomersController

Customer APINote: that Customer controller is derived from ApiController instead of controller.

We enter all the actions required to query a customer or customers in the database.

public class CustomersController : ApiController { private ApplicationDbContext _context;

Page 22: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

public CustomersController() { _context = new ApplicationDbContext(); } //Get /api/customers public IEnumerable<Customer> GetCustomers() { return _context.Customers.ToList(); }

//Get /api/customers/1 public Customer GetCustomer(int id) { var customer = _context.Customers.SingleOrDefault(c => c.Id == id);

if (customer == null) throw new HttpResponseException(HttpStatusCode.NotFound);

return customer; }

//POST /api/customers //to return the resource. //Post request specified [HttpPost] public Customer CreateCustomer(Customer customer) { if (!ModelState.IsValid) throw new HttpResponseException(HttpStatusCode.BadRequest);

_context.Customers.Add(customer); _context.SaveChanges();

return customer; }

//PUT /api/customers/1 [HttpPut] public void UpdateCustomer(int id, Customer customer) { if (!ModelState.IsValid) throw new HttpResponseException(HttpStatusCode.BadRequest);

var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);

if(customerInDb ==null) throw new HttpResponseException(HttpStatusCode.NotFound);

customerInDb.Name = customer.Name; customerInDb.Birthdate = customer.Birthdate; customerInDb.IsSubscribedToNewsletter = customer.IsSubscribedToNewsletter; customerInDb.MembershipTypeId = customer.MembershipTypeId;

_context.SaveChanges(); }

//DELETE /api/customers/1 [HttpDelete]

Page 23: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

public void DeleteCustomer(int id) { var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);

if (customerInDb == null) throw new HttpResponseException(HttpStatusCode.NotFound); _context.Customers.Remove(customerInDb); _context.SaveChanges(); } }

Page 24: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

How to test this APIBack in our browser if we change the URL to /api/customers we receive the list of the customers is returned as XML see below. WebAPI has a media formatter, the list of customers will be formatted as below. This is because the header automatically will format as XML if there is no content type header set.

There is no Content-Type for the Request Header so it will default to XML.

Later to get a JSON request in Postman we will set the Content-Type for the request header to application/json

Page 25: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

PostmanPostman can be used to help us test each of the actions of our application.

Download and install postman.

Launch from start menu or desktop icon.

With postman we can test if all our Actions are functional. Such as Save, Add Edit or Delete.

Create a basic Request. Save

Page 26: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Save that request and Click Save to First Test

Copy the URL. http://localhost:57416/api/customers from our run project.

Paste into Enter request URL in Postman. Click Send.

You should get a result. Note you can switch the result from JSON and XML and from GET Requests to POST Requests, or DELETE request

JSON

Page 27: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

We can see the list of customers as either JSON or XML. Mostly we will be using JSON because that is native for our JavaScript code. JSON code is more lightweight, because there are no noisy opening and closing tags.

Change to XML or JSON

XML

Now we want to test Saving a Customer.Change to JSON, select one of the customers and copy with ctrl+c and change the request type to POST.

Page 28: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Click on the Body Tab and Click on the Raw radio button and paste in the customer. Remove the ID as it will automatically generate a new ID when we send it.

We need to Click on the Headers tab and make sure there is a content-type = application/json

Now Click Send.

If it has run Successfully it will show you 200OK and also in the below window will show what was generated.

Page 29: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

it will generate a Saved Customer generating a new ID 19 in this case.

Page 30: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Data Transfer DTOThere are a number of reasons for wanting to use DTO’s Improve performance (Get all data in one call), Flatten object hierarchy, Exclude properties

http://computerauthor.blogspot.com/2015/11/data-transfer-object-design-pattern-in-c.html

https://martinfowler.com/eaaCatalog/dataTransferObject.html

https://docs.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5’

Right now, our web API exposes the database entities to the client. The client receives data that maps directly to your database tables. However, that's not always a good idea. Sometimes you want to change the shape of the data that you send to client. For example, you might want to:

Remove circular references (see previous section). Hide particular properties that clients are not supposed to view. Omit some properties in order to reduce payload size. Flatten object graphs that contain nested objects, to make them more convenient for clients. Avoid "over-posting" vulnerabilities. (See Model Validation for a discussion of over-

posting.) Decouple your service layer from your database layer.

To accomplish this, you can define a data transfer object (DTO). A DTO is an object that defines how the data will be sent over the network. Let's see how that works with the Book entity. In the Models folder, add two DTO classes:

We will start to use DTO’s for Data Transfer. Currently with our API, it returns Customer objects. This information can change frequently and break, existing clients that are dependent on the Customer object. For example, we may remove or change a property that can impact the client that depends on that property. We have to make the contract of the API as stable as possible. Changing reasonably slower than our Model.

Your API should never receive or return domain objects. It should instead use a DTO. DTO’s are Data Transfer Objects used to transfer data from the Client to the Server or vice

versa. Using a DTO, there is less chance that our API will break as we refactor our domain model.

API versioning will be covered later.

1. Add a new folder in Solution Explorer called DTO’s2. Create a new Class in that folder called CustomerDto

Page 31: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

3. Open Customer model and copy the attributes.4. Import DataAnnotations and 5. Delete the line MembershipTypes: because this is a domain class and this is creating a

dependency from our Dto to our domain model. We either use primitive types or custom Dtos. That way it will create a custom Dto like: MembershipTypeDto (Which we will do later)

6. Delete all the Display attributes

    public class CustomerDto    {         public int Id { get; set; }         [Required(ErrorMessage = "Please enter a customer's name")]        [StringLength(255)]        public string Name { get; set; }        public bool IsSubscribedToNewsletter { get; set; }        public MembershipType MembershipType { get; set; } DELETE THIS LINE        [Display(Name = "Membership Type")] DELETE THIS LINE        public byte MembershipTypeId { get; set; }        [Min18YearsIfAMember]        [Display(Name = "Date of Birth")] DELETE THIS LINE        public DateTime? Birthdate        {            get; set;        }}

Auto MapperMapping these objects manually to the Api CustomerController can be time consuming. There is an easier way we can use Auto Mapper.

1. Open Package Manager Console: install-package automapper -version:4.1

2. Now create a mapping profile in solution explorer create a new class in App_Start folder called MappingProfile

3. Derive from: Profile

Page 32: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

namespace Vidly3.App_Start{    public class MappingProfile : Profile    {        public MappingProfile()        {            Mapper.CreateMap<Customer, CustomerDto>();            Mapper.CreateMap<CustomerDto, Customer>();        }            }}

We have mapped a Customer to a CustomerDto and a CustomerDto to a Customer. When we run this create map method, it will automapper uses reflection to scan these

types, finds their properties based on their names, which is why we call it a convention-based mapping tool.

1. Here is the mapping profile, we need to load this when the application is started. Open Global.asax

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Http;using System.Web.Mvc;using System.Web.Optimization;using System.Web.Routing;using AutoMapper;using Vidly3.App_Start; namespace Vidly3{    public class MvcApplication : System.Web.HttpApplication    {        protected void Application_Start()        {            Mapper.Initialize(c=>c.AddProfile<MappingProfile>());            GlobalConfiguration.Configure(WebApiConfig.Register);            AreaRegistration.RegisterAllAreas();            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);            RouteConfig.RegisterRoutes(RouteTable.Routes);            BundleConfig.RegisterBundles(BundleTable.Bundles);        }    }}

2. In the Controller in Get Customers method in api/CustomerController we will be modifying our actions to map these objects to CustomerDto

3. Get Customers method, Changing the return type to IEnumerable of <CustomerDto>4. We need to map these objects to CustomerDto we pass a delegate by using:

Select(Mapper.Map<Customer, CustomerDto>);

Page 33: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

List of Customers DtoFrom

//Get /api/customers  public IEnumerable<Customer> GetCustomers()  {      return _context.Customers.ToList();  }To

//Get /api/customers public IEnumerable<CustomerDto> GetCustomers() {     return _context.Customers.ToList().Select(Mapper.Map<Customer, CustomerDto>); }

Page 34: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Update Customer DtoFrom

[HttpPost]       public Customer CreateCustomer(Customer customer)       {           if (!ModelState.IsValid)               throw new HttpResponseException(HttpStatusCode.BadRequest);            _context.Customers.Add(customer);           _context.SaveChanges();            return customer;       }

//PUT /api/customers/1     [HttpPut]     public void UpdateCustomer(int id, Customer customer)     {        if (!ModelState.IsValid)            throw new HttpResponseException(HttpStatusCode.BadRequest);         var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);         if(customerInDb ==null)        throw new HttpResponseException(HttpStatusCode.NotFound);         customerInDb.Name = customer.Name;        customerInDb.Birthdate = customer.Birthdate;        customerInDb.IsSubscribedToNewsletter = customer.IsSubscribedToNewsletter;           customerInDb.MembershipTypeId = customer.MembershipTypeId;            _context.SaveChanges();       }

Page 35: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

To

[HttpPost]     public CustomerDto CreateCustomer(CustomerDto customerDto)     {         if (!ModelState.IsValid)             throw new HttpResponseException(HttpStatusCode.BadRequest);          var customer = Mapper.Map<CustomerDto, Customer>(customerDto);         _context.Customers.Add(customer);         _context.SaveChanges();

//we want to add this id in our DTO and return it to our client.         customerDto.Id = customer.Id;         return customerDto;     } [HttpPut]       public void UpdateCustomer(int id, CustomerDto customerDto)       {           if (!ModelState.IsValid)               throw new HttpResponseException(HttpStatusCode.BadRequest);            var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);            if(customerInDb ==null)           throw new HttpResponseException(HttpStatusCode.NotFound);         Mapper.Map<CustomerDto, Customer>(customerDto, customerInDb);        customerInDb.Name = customer.Name;        customerInDb.Birthdate = customer.Birthdate;        customerInDb.IsSubscribedToNewsletter = customer.IsSubscribedToNewsletter;        customerInDb.MembershipTypeId = customer.MembershipTypeId;

           _context.SaveChanges();       }

As <CustomerDto, Customer> is greyed out this portion can now be deleted once initially typed Visual Studio will now know what it expects and once grey it can be removed.

All the other customerInDb lines can be deleted

Page 36: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Using Camel NotationHow to configure Web API to return JSON objects in camel notation

Look at the results of the API POST Man to get the list of Customers. These are pascal notation, all the first letters are upper case.

As you can see the Pascal notation has a capital letter for the first letter of the word Id, Name, IsSubscribedToNewsletter etc.

In JavaScript we use CamelCase, the first letter of the first word is lowercase.1. In Solution explorer, open App_Start/WebApiConfig.cs2. WebApiFramework has a default routing rule, there is no Action see highlighted below.

public static void Register(HttpConfiguration config)   {

var settings = config.Formatters.JsonFormatter.SerializerSettings;settings.ContractResolver = new CamelCasePropertyNamesContractResolver();settings.Formatting = Newtonsoft.Json.Formatting.Indented;config.MapHttpAttributeRoutes();config.Routes.MapHttpRoute(name: "DefaultApi",routeTemplate: "api/{controller}/{id}", //No Action is present

defaults: new { id = RouteParameter.Optional }           );    }

Page 37: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

Now you can see that we now have Camel Notation with lower case starting letters:

IHttpActionResultFor Restful convention, when we create a customer we want the create Status Code to be 201, to do this we must use IHttpActionResult as the return type. The Status codes response of 200 is the current response in Postman, however in Restful convention the response should be 201 when we create a new Customer. We need more control.

In Customers Controller we are going to change the return type from CustomerDto to IHttpActionResult. IHttpActionResult is similar to ActionResult in MVC Framework. It is implemented by a number of classes.

From

//POST /api/customers        //to return the resource.        //Post request specified        [HttpPost]        public CustomerDto CreateCustomer(CustomerDto customerDto)        {            if (!ModelState.IsValid)                throw new HttpResponseException(HttpStatusCode.BadRequest);

Page 38: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

            var customer = Mapper.Map<CustomerDto, Customer>(customerDto);            _context.Customers.Add(customer);            _context.SaveChanges();             //we want to add this id in our DTO and return it to our client.            customerDto.Id = customer.Id;            return customerDto;        }

To

//POST /api/customers       //to return the resource.       //Post request specified       [HttpPost]       public IHttpActionResult CreateCustomer(CustomerDto customerDto)       {           if (!ModelState.IsValid)               return BadRequest();            var customer = Mapper.Map<CustomerDto, Customer>(customerDto);           _context.Customers.Add(customer);           _context.SaveChanges();            //we want to add this id in our DTO and return it to our client.           customerDto.Id = customer.Id;           //return the URI Unified Resource Identifier /api/customers/10           return Created(new Uri(Request.RequestUri + "/" +customer.Id),customerDto);       }

Before

Page 39: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

After

Now add the same to the Get Customer method

From

//Get /api/customers/1       public Customer GetCustomer(int id)       {           var customer = _context.Customers.SingleOrDefault(c => c.Id == id);            if (customer == null)               throw new HttpResponseException(HttpStatusCode.NotFound);            return customer;       }

To

Page 40: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

//Get /api/customers/1       public IHttpActionResult GetCustomer(int id)       {           var customer = _context.Customers.SingleOrDefault(c => c.Id == id);            if (customer == null)               return NotFound();            return Ok(Mapper.Map<Customer, CustomerDto>(customer));       }

ExerciseImplement API for CRUD operations for Movies, we will have different End points for getting all movies, getting a single movie by ID, end points for adding, updating and deleting a Movie.

We will get an exception when updating a movie.

Property id is part of the object key information and cannot be modified.

I have attached a PDF on how to resolve this issue.

Starting the WebAPI for Movies. – Up to Video 4 Building an API @5:15s

Building Movie APIusing System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Net.Http;using System.Web.Http;using Vidly3.Models; namespace Vidly3.Controllers.Api{    public class MoviesController : ApiController    {        public MoviesController()        {            _context = new ApplicationDbContext();        }        private ApplicationDbContext _context;         //GET /api/movies        public IEnumerable<Movie> GetMovies()        {            return _context.Movies.ToList();        }         //GET /api/movies/1        public Movie GetMovie(int id)        {            var movie = _context.Movies.SingleOrDefault(c => c.Id == id);

Page 41: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

            if (movie == null)                throw new HttpResponseException(HttpStatusCode.NotFound);             return movie;        }         //POST /api/movies        [HttpPost]        public Movie CreateMovie(Movie movie)        {            if (!ModelState.IsValid)                throw new HttpResponseException(HttpStatusCode.BadRequest);             _context.Movies.Add(movie);            _context.SaveChanges();             return movie;        }         //PUT /api/movies/1        [HttpPut]        public void UpdateMovie(int id, Movie movie)        {            if (!ModelState.IsValid)                throw new HttpResponseException(HttpStatusCode.BadRequest);             var movieInDb = _context.Movies.SingleOrDefault(m => m.Id == id);             if (movieInDb == null)                throw new HttpResponseException(HttpStatusCode.NotFound);             movieInDb.Name = movie.Name;            movieInDb.ReleaseDate = movie.ReleaseDate;            movieInDb.DateAdded = movie.DateAdded;            movieInDb.NumberInStock = movie.NumberInStock;            movieInDb.GenreId = movie.GenreId;             _context.SaveChanges();         }         //DELETE /api/movies/1        public void DeleteMovie(int id)        {            var movieInDb = _context.Movies.SingleOrDefault(m => m.Id == id);             if (movieInDb == null)                throw new HttpResponseException(HttpStatusCode.NotFound);             _context.Movies.Remove(movieInDb);            _context.SaveChanges();        }

Page 42: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

    }}

Testing Movies APIAgain, there is no ContentType and when navigate to http://localhost:57416/api/movies and inspect we can see that there is no ContentType

Open Postman and paste in the URL: http://localhost:57416/api/movies

Select a movie and Copy

Page 43: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

SummaryWe learnt about Postman, Data Transfer Object, Auto Mapper, Action Results, Camel Notation

In the next section we will be focusing to Client-Side development, I will show you how to consume these API’s we have built and implement a few nice features in our application.

Section 5 Client-Side DevelopmentWe will be shifting to Client-Side Development

We are going to use jQuery ajax to consume the API’s built in the last section

jQuery plugins

1. Bootbox for Bootstrap dialog boxes2. Data Tables to display tables that have built in support for pagination, searching and sorting

TaskWe are going to extend our customer list and add a delete link in front of each customer. When the user clicks the link, we will use jQuery to call our API, and then remove this customer from the list.In Solution Explorer go to Views -> Customers -> Index. Add a new table heading with ‘th’, called Delete.

<table id="customers" class="table table-bordered table-hover">        <thead>            <tr>                <th>Customer</th>

Page 44: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

                <td>Membership Type</td>                <td>Discount Rate</td>                <td>Delete</td>            </tr>        </thead>And then when rendering a customer in a button. We should use hyperlinks, in this case we will use a button and then use a class=btn-link to make it look like a hyperlink.

<tbody>

  @foreach (var customer in Model)    {    <tr>     <td>@Html.ActionLink(customer.Name, "Edit", "Customers", new { id = customer.Id }, null)</td>     <td>@customer.MembershipType.Name</td>      <td>@customer.MembershipType.DiscountRate%</td>      <td>        <button data-customer-id="@customer.Id" class="btn-link js-delete">Delete</button>      </td>

Give the table an id of customers.

<table id="customers" class="table table-bordered table-hover">

Give the button a second class js-delete

<td> <button data-customer-id="@customer.Id" 

class="btn-link js-delete">Delete</button></td>

Now jQuery to handle the click events of the button. Scroll to the bottom of the file and create a scripts section.

 @section scripts{     <script>        $(document).ready(function () { // Standard jQuery document ready Start            $("#customers .js-delete").on("click", function () {                var button = $(this);                if (confirm("Are you sure you want to delete this customer?")) {                    $.ajax({                        url: "/api/customers/" + $(this).attr("data-customer-id"),                        method: "DELETE",                        success: function () {                            console.log("Success")                            button.parents("tr").remove();                        }

Page 45: Vidly3 Notes - duncanfirth.infoduncanfirth.info/wp-content/uploads/2019/03/Vidly_Documentation.docx  · Web viewIn JavaScript we use CamelCase, the first letter of the first word

                    })                }            });        });    </script>  }