Browse Source

Finish part 5 readme

David Ginzberg 7 years ago
parent
commit
02c3111195
1 changed files with 123 additions and 11 deletions
  1. 123
    11
      README.md

+ 123
- 11
README.md View File

310
 
310
 
311
 
311
 
312
 
312
 
313
-
314
-
315
-
316
 -
313
 -
317
 # Part 3.2.3 - Modify `VoteController`
314
 # Part 3.2.3 - Modify `VoteController`
318
 * Create a `getAllVotes` method in the `VoteController`
315
 * Create a `getAllVotes` method in the `VoteController`
325
 }
322
 }
326
 ```
323
 ```
327
 
324
 
328
-
329
-
330
-
331
-
332
 -
325
 -
333
 -
326
 -
334
 # Part 4 - Data Transfer Object (DTO) Implementation
327
 # Part 4 - Data Transfer Object (DTO) Implementation
337
 * Create a sub package of `java` named `dtos`
330
 * Create a sub package of `java` named `dtos`
338
 
331
 
339
 -
332
 -
340
-# Part 4.1 - Create class `OptionCount`
333
+## Part 4.1 - Create class `OptionCount`
341
 * The `OptionCount` DTO contains the `ID` of the option and a count of votes casted for that option.
334
 * The `OptionCount` DTO contains the `ID` of the option and a count of votes casted for that option.
342
 
335
 
343
 ```java
336
 ```java
363
 }
356
 }
364
 ```
357
 ```
365
 
358
 
366
-# Part 4.2 - Create class `VoteResult`
359
+## Part 4.2 - Create class `VoteResult`
367
 * The `VoteResult` DTO contains the total votes cast and a collection of `OptionCount` instances.
360
 * The `VoteResult` DTO contains the total votes cast and a collection of `OptionCount` instances.
368
 
361
 
369
 ```java
362
 ```java
391
 ```
384
 ```
392
 
385
 
393
 
386
 
394
-# Part 4.3 - Create class `ComputeResultController`
387
+## Part 4.3 - Create class `ComputeResultController`
395
 * Following the principles used in creating the `PollController` and `VoteController`, we create a new `ComputeResultController` class
388
 * Following the principles used in creating the `PollController` and `VoteController`, we create a new `ComputeResultController` class
396
 
389
 
397
 ```java
390
 ```java
417
 * The computed results are sent to the client using a newly created instance of `ResponseEntity`.
410
 * The computed results are sent to the client using a newly created instance of `ResponseEntity`.
418
 
411
 
419
 
412
 
420
-# Part 4.4 - Test via Postman
413
+## Part 4.4 - Test via Postman
421
 * Start/restart the `QuickPoll` application.
414
 * Start/restart the `QuickPoll` application.
422
 * Using the earlier Postman requests, create a poll and cast votes on its options.
415
 * Using the earlier Postman requests, create a poll and cast votes on its options.
423
 * Ensure a JSON file with a `status` of `200` is returned by executing a `GET` request of `http://localhost:8080/computeresults?pollId=1` via Postman
416
 * Ensure a JSON file with a `status` of `200` is returned by executing a `GET` request of `http://localhost:8080/computeresults?pollId=1` via Postman
417
+
418
+
419
+# Part 5 - Error Handling
420
+
421
+## Part 5.1 - Create `ResourceNotFoundException`
422
+
423
+- Create a `exception` package inside of `io.zipcoder.springdemo.QuickPollApplication`
424
+- Create a `ResourceNotFoundException` class that extends `RuntimeException`. We'll use this to signal when a requested resource is not found.
425
+- Annotate the `ResourceNotFoundException` class with `@ResponseStatus(HttpStatus.NOT_FOUND)`. This informs Spring that any request mapping that throws a `ResourceNotFoundException` should result in a `404 NOT FOUND` http status.
426
+- Implement three constructors
427
+  -  A no-arg constructor
428
+  -  A constructor that takes a `String message` and passes it to the superclass constructor
429
+  -  A constructor that takes `String message` and `Throwable cause` and passes both to the superclass constructor
430
+
431
+
432
+## Part 5.2 - Verify polls
433
+
434
+Create a void method in `PollController` called `verifyPoll` that checks if a specific poll id exists and throws a `ResourceNotFoundException` if not. Use this in any method that searches for or updates an existing poll (eg: Get, Put, and Delete methods).
435
+
436
+**Note**: This means that trying to submit a PUT request for a resource that doesn't exist will not implicitly create it; it should throw a 404 instead.
437
+
438
+## Part 5.3 - Create custom Error Responses
439
+
440
+Spring provides some built-in exception handling and error response, but we'll customize it a bit here. Create an `ErrorDetail` class in a new `io.zipcoder.tc_spring_poll_application.dto.error` package to hold relevant information any time an error occurs.
441
+
442
+Fields (Don't forget to provide getters and setters):
443
+
444
+- `String title`: a brief title of the error condition, eg: "Validation Failure" or "Internal Server Error"
445
+- `int status`: the HTTP status code for the current request; redundant but useful for client-side error handling
446
+- `String detail`: A short, human-readable description of the error that may be presented to a user
447
+- `long timeStamp`: the time in milliseconds when the error occurred
448
+- `String developerMessage`: detailed information such as exception class name or a stack trace useful for developers to debug
449
+
450
+
451
+## Part 5.4 - Create a `@ControllerAdvice`
452
+
453
+In this section we add custom handling for the exceptions we created before. A `@ControllerAdvice` is an AOP feature that wraps a controller and adds some functionality when needed. In this case we are adding functionality only when an exception is thrown.
454
+
455
+- Create RestExceptionHandler class annotated with `@ControllerAdvice`
456
+- Create a handler method with the header shown below
457
+- Populate an ErrorDetail object in the method, and return a ResponseEntity containing the ErrorDetail and an HTTP `NOT_FOUND` status
458
+  - Use java.util's `new Date().getTime()` for the timestamp
459
+  - Provide the detail and developer messages from the `ResourceNotFoundException`
460
+
461
+```
462
+@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> handleResourceNotFoundException(ResourceNotFoundException rnfe, HttpServletRequest request) {...}
463
+```
464
+
465
+
466
+
467
+## Part 5.4 - Validating domain entities
468
+
469
+Now it's time to make sure that all objects persisted to the database actually contain valid values. Use the `org.hibernate.validator.constraints.NotEmpty` and `javax.validation.constraints.Size` and `javax.validation.Valid` annotations for validation.
470
+
471
+- In the `Poll` class:
472
+  - `options` should be `@Size(min=2, max = 6)`
473
+  - `question` should be `@NotEmpty`
474
+- To enforce these validations, add `@Valid` annotations to Poll objects in `RequestMapping`-annotated controller methods (there should be 2)
475
+
476
+## Part 5.5 - Customizing validation errors
477
+
478
+In order to customize validation errors we'll need a class for error information. Create a `ValidationError` class in `io.zipcoder.tc_spring_poll_application.dto.error` with the following fields and appropriate getters and setters:
479
+
480
+- `String code`
481
+- `String message`
482
+
483
+We also need a new field in the `ErrorDetail` class to hold errors. There may be multiple validation errors associated with a request, sometimes more than one of the same type, so this field will be a collection, specifically a `Map<String, List<ValidationError>> errors` field.
484
+
485
+
486
+## Part 5.6 - Create a validation error handler
487
+
488
+- add below handler to `RestExceptionHandler`
489
+
490
+```
491
+@ExceptionHandler(MethodArgumentNotValidException.class)
492
+public ResponseEntity<?>
493
+handleValidationError(  MethodArgumentNotValidException manve,
494
+						HttpServletRequest request){...}
495
+```
496
+
497
+In this handler we need to do the following:
498
+
499
+- Create the ErrorDetail object (similar to before)
500
+- Get the list of field validation errors
501
+- For each field error, add it to the appropriate list in the ErrorDetail (see below)
502
+- Return a `ResponseEntity` containing the error detail and the appropriate HTTP status code (`400 Bad Request`)
503
+
504
+```
505
+List<FieldError> fieldErrors =  manve.getBindingResult().getFieldErrors();
506
+		for(FieldError fe : fieldErrors) {
507
+			
508
+			List<ValidationError> validationErrorList = errorDetail.getErrors().get(fe.getField());
509
+			if(validationErrorList == null) {
510
+				validationErrorList = new ArrayList<>();
511
+				errorDetail.getErrors().put(fe.getField(), validationErrorList);
512
+			}
513
+			ValidationError validationError = new ValidationError();
514
+			validationError.setCode(fe.getCode());
515
+			validationError.setMessage(messageSource.getMessage(fe, null));
516
+			validationErrorList.add(validationError);
517
+		}
518
+```
519
+
520
+## Part 5.7 - Externalize strings in a messages.properties file
521
+
522
+Commonly used strings in your Java program can be removed from the source code and placed in a separate file. This is called externalizing, and is useful for allowing changes to text displayed without impacting actual program logic. One example of where this is done is in internationalization, the practice of providing multilingual support in an application, allowing users to use an application in their native language.
523
+
524
+There are two steps needed here to externalize and standardize the validation error messages:
525
+
526
+- Create a `messages.properties` file in the `src/main/resources` directory with the given properties below
527
+- Use an autowired `MessageSource` in the `RestExceptionHandler` to set the message on ValidationError objects (ie: `setMessage(messageSource.getMessage(fe,null));` )
528
+
529
+**`messages.properties` content**:
530
+
531
+```
532
+NotEmpty.poll.question=Question is a required field
533
+Size.poll.options=Options must be greater than {2} and less than {1}
534
+```
535
+