From 737bddadc2eda6af52a3dd65d488c248826fa7ee Mon Sep 17 00:00:00 2001 From: "PLAINCONCEPTS\\lruiz" Date: Tue, 22 Aug 2017 18:03:12 +0200 Subject: [PATCH] Fix issue #259 --- .../Filters/HttpGlobalExceptionFilter.cs | 9 +----- .../Filters/JsonErrorResponse.cs | 9 ++++++ .../Filters/ValidateModelStateFilter.cs | 30 +++++++++++++++++++ .../Basket/Basket.API/Model/BasketItem.cs | 18 +++++++++-- src/Services/Basket/Basket.API/Startup.cs | 1 + .../modules/basket/basket.component.html | 12 ++++++-- .../Client/modules/basket/basket.component.ts | 10 ++++++- .../Client/modules/orders/orders.component.ts | 4 +-- .../modules/shared/services/data.service.ts | 2 +- 9 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs create mode 100644 src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index ab7989973..dab725915 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -8,7 +8,7 @@ using System.Net; namespace Basket.API.Infrastructure.Filters { - public class HttpGlobalExceptionFilter : IExceptionFilter + public partial class HttpGlobalExceptionFilter : IExceptionFilter { private readonly IHostingEnvironment env; private readonly ILogger logger; @@ -52,12 +52,5 @@ namespace Basket.API.Infrastructure.Filters } context.ExceptionHandled = true; } - - private class JsonErrorResponse - { - public string[] Messages { get; set; } - - public object DeveloperMessage { get; set; } - } } } diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs new file mode 100644 index 000000000..bcadc7358 --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs @@ -0,0 +1,9 @@ +namespace Basket.API.Infrastructure.Filters +{ + public class JsonErrorResponse + { + public string[] Messages { get; set; } + + public object DeveloperMessage { get; set; } + } +} diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs new file mode 100644 index 000000000..8ef72edb6 --- /dev/null +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs @@ -0,0 +1,30 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Basket.API.Infrastructure.Filters +{ + public class ValidateModelStateFilter : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext context) + { + if (context.ModelState.IsValid) + { + return; + } + + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k].Errors) + .Select(e => e.ErrorMessage) + .ToArray(); + + var json = new JsonErrorResponse + { + Messages = validationErrors + }; + + context.Result = new BadRequestObjectResult(json); + } + } +} diff --git a/src/Services/Basket/Basket.API/Model/BasketItem.cs b/src/Services/Basket/Basket.API/Model/BasketItem.cs index 8b98befcc..a41945145 100644 --- a/src/Services/Basket/Basket.API/Model/BasketItem.cs +++ b/src/Services/Basket/Basket.API/Model/BasketItem.cs @@ -1,6 +1,9 @@ -namespace Microsoft.eShopOnContainers.Services.Basket.API.Model +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Microsoft.eShopOnContainers.Services.Basket.API.Model { - public class BasketItem + public class BasketItem : IValidatableObject { public string Id { get; set; } public string ProductId { get; set; } @@ -9,5 +12,16 @@ public decimal OldUnitPrice { get; set; } public int Quantity { get; set; } public string PictureUrl { get; set; } + public IEnumerable Validate(ValidationContext validationContext) + { + var results = new List(); + + if (Quantity < 1) + { + results.Add(new ValidationResult("Invalid number of units", new []{ "Quantity" })); + } + + return results; + } } } diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index a473a29fc..18e21e209 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -54,6 +54,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API services.AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + options.Filters.Add(typeof(ValidateModelStateFilter)); }).AddControllersAsServices(); services.AddHealthChecks(checks => diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.html b/src/Web/WebSPA/Client/modules/basket/basket.component.html index 6787bc0cb..e700f622d 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.html +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.html @@ -1,7 +1,13 @@
Back to catalog - +
+
+
+ +
+
+
Product
@@ -14,7 +20,7 @@
- +
{{item.productName}}
$ {{item.unitPrice | number:'.2-2'}}
@@ -23,7 +29,7 @@ type="number" min="1" [(ngModel)]="item.quantity" - (change)="itemQuantityChanged(item)" /> + (change)="itemQuantityChanged(item)"/>
$ {{(item.unitPrice * item.quantity) | number:'.2-2'}}
diff --git a/src/Web/WebSPA/Client/modules/basket/basket.component.ts b/src/Web/WebSPA/Client/modules/basket/basket.component.ts index 4637e93fa..879ef784b 100644 --- a/src/Web/WebSPA/Client/modules/basket/basket.component.ts +++ b/src/Web/WebSPA/Client/modules/basket/basket.component.ts @@ -1,6 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; +import 'rxjs/Rx'; +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/observable/throw'; + import { BasketService } from './basket.service'; import { IBasket } from '../shared/models/basket.model'; import { IBasketItem } from '../shared/models/basketItem.model'; @@ -12,6 +16,7 @@ import { BasketWrapperService } from '../shared/services/basket.wrapper.service' templateUrl: './basket.component.html' }) export class BasketComponent implements OnInit { + errorMessages: any; basket: IBasket; totalPrice: number = 0; @@ -30,7 +35,10 @@ export class BasketComponent implements OnInit { } update(event: any) { - this.service.setBasket(this.basket).subscribe(x => console.log('basket updated: ' + x)); + this.service.setBasket(this.basket).catch((errMessage) => { + this.errorMessages = errMessage.messages; + return Observable.throw(errMessage); + }).subscribe(x => console.log('basket updated: ' + x)); } checkOut(event: any) { diff --git a/src/Web/WebSPA/Client/modules/orders/orders.component.ts b/src/Web/WebSPA/Client/modules/orders/orders.component.ts index 105965200..a6af462ac 100644 --- a/src/Web/WebSPA/Client/modules/orders/orders.component.ts +++ b/src/Web/WebSPA/Client/modules/orders/orders.component.ts @@ -20,7 +20,7 @@ export class OrdersComponent implements OnInit { ngOnInit() { if (this.configurationService.isReady) { - this.getOrders() + this.getOrders(); } else { this.configurationService.settingsLoaded$.subscribe(x => { this.getOrders(); @@ -31,7 +31,7 @@ export class OrdersComponent implements OnInit { this.interval = setTimeout(() => { this.service.getOrders().subscribe(orders => { this.orders = orders; - if (this.orders.length != this.oldOrders.length) { + if (this.orders.length !== this.oldOrders.length) { clearInterval(this.interval); } }); diff --git a/src/Web/WebSPA/Client/modules/shared/services/data.service.ts b/src/Web/WebSPA/Client/modules/shared/services/data.service.ts index 64288e127..26f7801ca 100644 --- a/src/Web/WebSPA/Client/modules/shared/services/data.service.ts +++ b/src/Web/WebSPA/Client/modules/shared/services/data.service.ts @@ -102,7 +102,7 @@ export class DataService { if (error instanceof Response) { let errMessage = ''; try { - errMessage = error.json().error; + errMessage = error.json(); } catch (err) { errMessage = error.statusText; }