├── .gitignore
├── .mvn
└── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.md
├── gitimages
├── jwt.jpg
└── projectcreate.jpg
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── restfulspring
│ │ └── apiexample
│ │ ├── ApiexampleApplication.java
│ │ ├── config
│ │ ├── MyFilterConfig.java
│ │ └── SecurityConfig.java
│ │ ├── controller
│ │ ├── AccountController.java
│ │ ├── CityController.java
│ │ ├── CountryController.java
│ │ ├── HelloController.java
│ │ └── PersonController.java
│ │ ├── entity
│ │ ├── ApplicationUser.java
│ │ ├── City.java
│ │ ├── Country.java
│ │ ├── Course.java
│ │ ├── Person.java
│ │ └── Student.java
│ │ ├── exception
│ │ └── NotFoundException.java
│ │ ├── filters
│ │ ├── JwtFilter.java
│ │ └── MyFilter.java
│ │ ├── repository
│ │ ├── CityRepository.java
│ │ ├── CountryRepository.java
│ │ ├── PersonRepository.java
│ │ └── UserRepository.java
│ │ └── service
│ │ ├── CityService.java
│ │ ├── CountryService.java
│ │ ├── JwtUtilService.java
│ │ ├── PersonService.java
│ │ └── UserService.java
└── resources
│ └── application.properties
└── test
└── java
└── com
└── restfulspring
└── apiexample
└── ApiexampleApplicationTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 |
30 | ### VS Code ###
31 | .vscode/
32 |
--------------------------------------------------------------------------------
/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007-present the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import java.net.*;
18 | import java.io.*;
19 | import java.nio.channels.*;
20 | import java.util.Properties;
21 |
22 | public class MavenWrapperDownloader {
23 |
24 | private static final String WRAPPER_VERSION = "0.5.6";
25 | /**
26 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
27 | */
28 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
29 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
30 |
31 | /**
32 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
33 | * use instead of the default one.
34 | */
35 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
36 | ".mvn/wrapper/maven-wrapper.properties";
37 |
38 | /**
39 | * Path where the maven-wrapper.jar will be saved to.
40 | */
41 | private static final String MAVEN_WRAPPER_JAR_PATH =
42 | ".mvn/wrapper/maven-wrapper.jar";
43 |
44 | /**
45 | * Name of the property which should be used to override the default download url for the wrapper.
46 | */
47 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
48 |
49 | public static void main(String args[]) {
50 | System.out.println("- Downloader started");
51 | File baseDirectory = new File(args[0]);
52 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
53 |
54 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
55 | // wrapperUrl parameter.
56 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
57 | String url = DEFAULT_DOWNLOAD_URL;
58 | if (mavenWrapperPropertyFile.exists()) {
59 | FileInputStream mavenWrapperPropertyFileInputStream = null;
60 | try {
61 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
62 | Properties mavenWrapperProperties = new Properties();
63 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
64 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
65 | } catch (IOException e) {
66 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
67 | } finally {
68 | try {
69 | if (mavenWrapperPropertyFileInputStream != null) {
70 | mavenWrapperPropertyFileInputStream.close();
71 | }
72 | } catch (IOException e) {
73 | // Ignore ...
74 | }
75 | }
76 | }
77 | System.out.println("- Downloading from: " + url);
78 |
79 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
80 | if (!outputFile.getParentFile().exists()) {
81 | if (!outputFile.getParentFile().mkdirs()) {
82 | System.out.println(
83 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
84 | }
85 | }
86 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
87 | try {
88 | downloadFileFromURL(url, outputFile);
89 | System.out.println("Done");
90 | System.exit(0);
91 | } catch (Throwable e) {
92 | System.out.println("- Error downloading");
93 | e.printStackTrace();
94 | System.exit(1);
95 | }
96 | }
97 |
98 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
99 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
100 | String username = System.getenv("MVNW_USERNAME");
101 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
102 | Authenticator.setDefault(new Authenticator() {
103 | @Override
104 | protected PasswordAuthentication getPasswordAuthentication() {
105 | return new PasswordAuthentication(username, password);
106 | }
107 | });
108 | }
109 | URL website = new URL(urlString);
110 | ReadableByteChannel rbc;
111 | rbc = Channels.newChannel(website.openStream());
112 | FileOutputStream fos = new FileOutputStream(destination);
113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
114 | fos.close();
115 | rbc.close();
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fawad1997/SpringWebAPI/89598fc9c372bba60ec3ade089f1ca9215dd0c38/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java Spring Web API
2 | In this repository, you can learn **Spring Rest API** from beginner to advanced level.
3 | ### Quick Links
4 | - [Creating Project](#creating-project)
5 | - [CRUD with H2 Database](#crud-with-h2-database)
6 | - [Creating Models/Tables/Entities](#creating-models-tables-entities)
7 | - [Creating Repository](#creating-repository)
8 | - [Creating Service](#creating-service)
9 | - [Creating Controller](#creating-controller)
10 | - [Enabling Cross-Origin](#enabling-cross-origin)
11 | - [Using MySQL database instead of H2](#using-mysql-database-instead-of-h2)
12 | - [One to Many Relation in Hibernate](#one-to-many-relation-in-hibernate)
13 | - [Creating Entities](#creating-entities)
14 | - [Many to Many Relation in Hibernate](#many-to-many-relation-in-hibernate)
15 | - [Error Handling](#error-handling)
16 | - [JPA Hibernate Validations](#jpa-hibernate-validations)
17 | - [Creating Filters](#creating-filters)
18 | - [Spring Security](#spring-security)
19 | - [Spring Basic Security](#spring-basic-security)
20 | - [Create table for application users](#create-table-for-application-users)
21 | - [Create repository](#create-repository)
22 | - [Create Service that will implement UserDetailsService](#create-service-that-will-implement-userdetailsservice)
23 | - [Create Security Configuration File](#create-security-configuration-file)
24 | - [Jwt Authentication](#jwt-authentication)
25 | - [Adding Dependencies](#adding-dependencies)
26 | - [Create JwtUtilService](#create-jwtutilservice)
27 | - [Generate Token](#generate-token)
28 | - [Allow Anonymous request](#allow-anonymous-request)
29 | - [Create filter to check token](#create-filter-to-check-token)
30 | - [Register User](#register-user)
31 |
32 | ## Creating Project
33 | In IntelliJ IDEA, go to spring initilizer, create new project by selecting **Spring web** in dependencies. [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/ee38d2323931446cb310ba963d825503ae73a6a4)
34 | 
35 | Give it proper name at create it, it may take few minutes for downloading the dependencies.
36 | Create a package for controllers in src>main>java>[your pakage name]/controller [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/63116f88c2c81305a20e53b795142dd6a3bd47c8)
37 |
38 | Add a controller to display hello world message. **@RestController** makes it restfull controller and **@RequestMapping(value = "/hello")** defines URL mapping
39 | ```java
40 | @RestController
41 | public class HelloController {
42 | @RequestMapping(value = "/hello")
43 | public String sayHello(){
44 | return "Hello World!";
45 | }
46 | }
47 | ```
48 |
49 | ## CRUD with H2 Database
50 | to use database we need few dependencies to be installed
51 | Go to [mavenrepository](https://mvnrepository.com/)
52 | and search for the following dependencies and add it to your [pom.xml](https://github.com/fawad1997/SpringWebAPI/blob/master/pom.xml) _dependencies_ section
53 | Please donot include test scope as we will not be doing testing at that stage [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/ba8bd9484b4901e214a9d1242c4c758c03489524)
54 |
58 | Now you have added a database named H2 database(in-memory db), you need to specify database name too
59 |
60 | 1. Open [application.properties](https://github.com/fawad1997/SpringWebAPI/blob/master/src/main/resources/application.properties) in resources folder
61 | 2. specify database name as follows
62 |
63 | ```
64 | spring.datasource.url=jdbc:h2:~/test;DB_CLOSE_ON_EXIT=FALSE
65 | spring.jpa.hibernate.ddl-auto=update
66 | ```
67 | ### Creating Models-Tables-Entities
68 | Create a package **entity** where you will create Entity classes
69 | e.g we are creating **Person** entity [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/2c70939ef61ba30a905e4ecb2d0c4ecd723497aa)
70 |
71 | Use **@Entity** annotation to make it entity, **@Data** to make setter getters, **@NoArgsConstructor** to create no argument constructor, **@AllArgsConstructor** to make argument constructor. **@Id** to make it primary key, **@GeneratedValue(strategy = GenerationType.AUTO)** for autoincrement.
72 | ```java
73 | @Entity
74 | @Data
75 | @NoArgsConstructor
76 | @AllArgsConstructor
77 | public class Person {
78 | @Id
79 | @GeneratedValue(strategy = GenerationType.AUTO)
80 | private int ID;
81 | private String Name;
82 | private int Age;
83 | private double Height;
84 | private String CNIC;
85 | }
86 | ```
87 | Now your table will be created once the application starts.
88 |
89 | ### Creating Repository
90 | Now create a repository for every entity, create package named **repository** and create interface,, e.g. PersonRepository that will extend **JpaRepository** and use **@Repository** annotation on it. [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/d830dfddcb418ecfe81741f0842342eebb69903f)
91 | ```java
92 | @Repository
93 | public interface PersonRepository extends JpaRepository {
94 | }
95 | ```
96 |
97 | ### Creating Service
98 | Services will contain business logic, e.g. CRUD operation in this case.
99 | Create package **service** and create service for every repository. e.g. **PersonService**
100 |
101 | Use **@Service** annotation on PersonService. In PersonService, create private object of PersonRepository and use **@Autowired** annotation on it, so spring framework will initilize that object. [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/43ad32aac931df96392dc13dbdeedd16816df34b)
102 |
103 | ### Creating Controller
104 |
105 | Now in the controller package, create **PersonController** that will manage Http requests.
106 | Use **@RestController**, **@RequestMapping(value = "/person")** as we do in controllers. Create an object of PersonService in PersonController and use **@Autowired** annotation on it, so spring framework will manage object creation.
107 |
108 | Now create GET, POST, PUT and DELETE methods with **@GetMapping**,**@PostMapping**, **@PutMapping(value = "/{id}")** and **@DeleteMapping(value = "/{id}")**. In the function parameters, use **@PathVariable int id** to get data from URL like localhost/person/1, and if we use **@RequestParam** it would be like localhost/person?id=1 and **@RequestBody Person person** to get data from body. [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/868f7bf954b91d4ef8d5701b6ba27dc39ac8f711)
109 | #### Enabling Cross-Origin
110 | Adding
111 | ```java
112 | @CrossOrigin(origins = "*", allowedHeaders = "*")
113 | ```
114 | on controller so it can be accessed from anywhere. [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/aa6237082aff953739700658aeaba375f9fc8979)
115 |
116 | Adding **@JsonProperty** on entity to will help you to change JSON object name [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/b777ed42d0d20ae0415c530b9a4682396012c11e)
117 |
118 | ### Using MySQL database instead of H2
119 | Remove H2 Database dependency from [pom.xml](https://github.com/fawad1997/SpringWebAPI/blob/master/pom.xml) and add [MySQL Connector](https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.19)
120 |
121 | comment/remove the H2 database configuration from [application.properties](https://github.com/fawad1997/SpringWebAPI/blob/master/src/main/resources/application.properties) file and add MySQL properties as follows: [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/1230fa1723715194d4b5b5feae68705793eb5549)
122 |
123 | ```
124 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
125 | spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
126 | spring.datasource.username=root
127 | spring.datasource.password=
128 | spring.jpa.show-sql=true
129 | spring.jpa.hibernate.ddl-auto=update
130 | spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5Dialect
131 | ```
132 |
133 | ### One to Many Relation in Hibernate
134 | #### Creating Entities
135 | lets create two Entities Country and City for oneToMany Relationship.
136 | Create entity **City** with properties **cityId** and **cityName** annotate them with proper annotations like **@Id**, **@GeneratedValue(strategy = GenerationType.AUTO)** and **@Column(name = "cityId")** and also annotate the table with annotations like **@Entity**, **@Data**, **@NoArgsConstructor** and **@AllArgsConstructor**.
137 | ```java
138 | @Entity
139 | @Data
140 | @NoArgsConstructor
141 | @AllArgsConstructor
142 | public class City {
143 | @Id
144 | @GeneratedValue(strategy = GenerationType.AUTO)
145 | @Column(name = "cityId")
146 | private int cityId;
147 | private String cityName;
148 | }
149 | ```
150 | Now create an entity **Country**, annotate class with annotations **@Entity**, **@Data**, **@NoArgsConstructor** and **@AllArgsConstructor**, and create the following fields [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/17699807e87c42b395db7917fb073c770339be12)
151 |
152 | ```java
153 | @Entity
154 | @Data
155 | @NoArgsConstructor
156 | @AllArgsConstructor
157 | @ToString
158 | public class Country {
159 | @Id
160 | @GeneratedValue(strategy = GenerationType.AUTO)
161 | @Column(name = "countryId")
162 | private int countryId;
163 | private String countryName;
164 |
165 | @OneToMany(cascade = CascadeType.ALL)
166 | @JoinColumn(name = "cc_fk",referencedColumnName = "countryId")
167 | private List cities;
168 | }
169 | ```
170 | Annotate it with **@OneToMany(cascade = CascadeType.ALL)** and **@JoinColumn(name = "cc_fk",referencedColumnName = "countryId")**. Yhis will create cc_fk column in City table and make it foreign key.
171 |
172 | ##### Creating Repositories
173 | same as above [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/6c1d0b675fd4d4dd8b7463c41de71f5c61b5540a)
174 | ##### Creating Services
175 | same as above [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/362bbd5eb20b8473a36e333143ecd28c3a15e25d)
176 | ##### Creating Controllers
177 | same as above [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/72620f3b512d3e5f8ad3a12fcb29846e949c5738)
178 |
179 | ### Many to Many Relation in Hibernate
180 | Lets take the example of two entities **Student** and **Course**, One student can be enrolled in multiple courses, similarly, Each course contains multiple students. If you remember database normalization rules, we need to break the entity to seperate entity for Many to Many relation.
181 |
182 | One ony below table, use **@JoinTable** annotation to specify 3rd table name, column to join from current table, and column from the other table.
183 | ```java
184 | @Entity
185 | @Data
186 | @NoArgsConstructor
187 | @AllArgsConstructor
188 | public class Student {
189 | @Id
190 | @GeneratedValue(strategy = GenerationType.IDENTITY)
191 | private int studentId;
192 | private String name;
193 | private String regNo;
194 | @ManyToMany(cascade = CascadeType.ALL)
195 | @JoinTable(name = "student_courses",
196 | joinColumns = {@JoinColumn(name = "studentId")},
197 | inverseJoinColumns = {@JoinColumn(name = "courseId")})
198 | private List courses;
199 | }
200 | ```
201 | and
202 | ```java
203 | @Entity
204 | @Data
205 | @NoArgsConstructor
206 | @AllArgsConstructor
207 | public class Course {
208 | @Id
209 | @GeneratedValue(strategy = GenerationType.IDENTITY)
210 | private int courseId;
211 | private String courseTitle;
212 | private String courseCode;
213 | @ManyToMany
214 | private List students;
215 | }
216 | ```
217 | ### Error Handling
218 | Suppose users requests a resource by FindbyId, currently it returns null, instead of null, we will now handle the error and return not found error. For that create a package named **exception** and create a class to handle exception. [(referance commit)](https://github.com/fawad1997/SpringWebAPI/commit/71dabd4475d16720909d32e4dadf3c0224bdaaaa)
219 | ```java
220 | @ResponseStatus(HttpStatus.NOT_FOUND)
221 | public class NotFoundException extends RuntimeException {
222 | public NotFoundException(String message){
223 | super(message);
224 | }
225 | }
226 | ```
227 | modify getPerson method to throw notfound error
228 | ```java
229 | public Person getPerson(int id){
230 | Optional person = personRepository.findById(id);
231 | if(!person.isPresent()){
232 | throw new NotFoundException("Person not found!");
233 | }
234 | return person.get();
235 | }
236 | ```
237 | ### JPA Hibernate Validations
238 | To customize table design, we can use different annotatons on top of each field.
239 |
240 | - **@Id** makes it Primary Key.
241 | - **@GeneratedValue(strategy = GenerationType.IDENTITY)** make it autoincrement by 1.
242 | - **@JsonProperty(value = "ID")** make sures it should be **ID** in JSON format instead of **id**
243 | - **@NotNull(message = "Name cann't be null")** makes the field non nullable.
244 | - **@Size(min = 2,max = 100, message = "Name must be minimum 2 characters and maximum 100 characters long")** validates the size of the field.
245 | - **@Email** is used to validate email.
246 | - **@Min(8)** and **@Max(110)** says that number should be between 8-110.
247 | - **@Pattern(regexp = "^\\(?(\\d{5})\\)?[-]?(\\d{7})[-]?(\\d{1})$",message = "CNIC sholud be in format xxxxx-xxxxxxx-x")** is used for regular expression.
248 | - **@Past** makes sure that date should be from past, mnot future.
249 | - **@JsonFormat(pattern = "yyyy-MM-dd")** make sures JSON data should be in this format.
250 |
251 | ```java
252 | @Entity
253 | @Data
254 | @NoArgsConstructor
255 | @AllArgsConstructor
256 | public class Person {
257 | @Id
258 | @GeneratedValue(strategy = GenerationType.IDENTITY)
259 | @JsonProperty(value = "ID")
260 | private int id;
261 | @JsonProperty(value = "Name")
262 | @NotNull(message = "Name cann't be null")
263 | @Size(min = 2,max = 100, message = "Name must be minimum 2 characters and maximum 100 characters long")
264 | private String name;
265 | @Email
266 | private String email;
267 | @JsonProperty(value = "Age")
268 | @Min(8)
269 | @Max(110)
270 | private int age;
271 | @JsonProperty(value = "Height")
272 | private double height;
273 | @JsonProperty(value = "CNIC")
274 | @Pattern(regexp = "^\\(?(\\d{5})\\)?[-]?(\\d{7})[-]?(\\d{1})$",message = "CNIC sholud be in format xxxxx-xxxxxxx-x")
275 | private String cnic;
276 | @Past
277 | @JsonFormat(pattern = "yyyy-MM-dd")
278 | private LocalDate doB;
279 | }
280 | ```
281 | After adding these annotations, our table will be created with those restrictions. But if we insert invalid data through JSON, our application will throw exception or maybe crash. We need to validate these properties in controller to avoid exceptions.
282 | #### Handling Validations in Controller
283 | Use **@Valid** to check for validations, and also inject BindingResult in it which will help us to catch errors, If invalid, then return errors, else, return created object. Simplarly check for validations in PutMapping function too.
284 | ```java
285 | @PostMapping
286 | public ResponseEntity> addPerson(@Valid @RequestBody Person person, BindingResult result){
287 | if(result.hasErrors()){
288 | Map errors = new HashMap<>();
289 | for(FieldError error:result.getFieldErrors()){
290 | errors.put(error.getField(),error.getDefaultMessage());
291 | }
292 | return new ResponseEntity