David Ginzberg преди 7 години
родител
ревизия
02c3111195
променени са 1 файла, в които са добавени 123 реда и са изтрити 11 реда
  1. 123
    11
      README.md

+ 123
- 11
README.md Целия файл

@@ -310,9 +310,6 @@ public interface VoteRepository extends CrudRepository<Vote, Long> {
310 310
 
311 311
 
312 312
 
313
-
314
-
315
-
316 313
 -
317 314
 # Part 3.2.3 - Modify `VoteController`
318 315
 * Create a `getAllVotes` method in the `VoteController`
@@ -325,10 +322,6 @@ public Iterable<Vote> getAllVotes(@PathVariable Long pollId) {
325 322
 }
326 323
 ```
327 324
 
328
-
329
-
330
-
331
-
332 325
 -
333 326
 -
334 327
 # Part 4 - Data Transfer Object (DTO) Implementation
@@ -337,7 +330,7 @@ public Iterable<Vote> getAllVotes(@PathVariable Long pollId) {
337 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 334
 * The `OptionCount` DTO contains the `ID` of the option and a count of votes casted for that option.
342 335
 
343 336
 ```java
@@ -363,7 +356,7 @@ public class OptionCount {
363 356
 }
364 357
 ```
365 358
 
366
-# Part 4.2 - Create class `VoteResult`
359
+## Part 4.2 - Create class `VoteResult`
367 360
 * The `VoteResult` DTO contains the total votes cast and a collection of `OptionCount` instances.
368 361
 
369 362
 ```java
@@ -391,7 +384,7 @@ public class VoteResult {
391 384
 ```
392 385
 
393 386
 
394
-# Part 4.3 - Create class `ComputeResultController`
387
+## Part 4.3 - Create class `ComputeResultController`
395 388
 * Following the principles used in creating the `PollController` and `VoteController`, we create a new `ComputeResultController` class
396 389
 
397 390
 ```java
@@ -417,7 +410,126 @@ public class ComputeResultController {
417 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 414
 * Start/restart the `QuickPoll` application.
422 415
 * Using the earlier Postman requests, create a poll and cast votes on its options.
423 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
+