Home Blog Page 74

Sử dụng Swagger UI trong jersey REST WS project

swagger ui
Sử dụng Swagger UI trong jersey REST WS project

Bài viết được sự cho phép của tác giả Giang Phan

Trong các bài viết trước, tôi đã giới thiệu với các bạn Swagger và cách cài đặt, sử dụng Swagger UI. Trong thực tế, các API thường được thay đổi bởi các developer và họ ít khi mở Swagger editor để cập nhật lại các document hoặc vì lý do nào đó mà họ không cập nhật document mới nhất theo source code. Một vấn đề đặt ra là có cách nào để API document có thể cập nhật một cách tự động mỗi khi code thay đổi và đặt cùng một nơi trong code để developer dễ dàng cập nhật hay không? Câu trả là là có và tôi sẽ hướng dẫn các bạn thực hiện trong phần tiếp theo của bài viết này.

  SWAP-No ROOT: Cách tạo Ram ảo cho Android không cần Root
  Các kĩ sư Pinterest đã xây dựng Progressive Web App như thế nào?

Xem thêm việc làm Linux lương cao trên TopDev

Tích hợp JAX-RS REST project với Swagger UI để tạo API document một cách tự động

Tiếp tục bài viết về xây dựng RESTful API với Jersey, trong bài này tôi sẽ hướng dẫn các bạn cách tích hợp JAX-RS REST project với Swagger UI để tạo API document một cách tự động. Nếu các bạn làm Spring project có thể tìm hiểu về Springfox – đây là một thư viện rất mạnh mẽ và dễ sử dụng để tạo REST API document. Tôi sẽ hướng dẫn các bạn Springfox ở series bài viết về Spring Framework.

Ý tưởng:

  • Tạo REST API sử dụng Jersey 2.
  • Sử dụng Swagger Annotation để mô tả thông tin về resource.
  • Sử dụng Swagger core để sinh file đặc tả Swagger API theo chuẩn OpenAPI 3, output có thể là json hoặc yaml.
  • Sử dụng thư viện Swagger UI để sinh ra giao diện cho document từ file config dưới chuẩn OpenAPI.

Để mọi thứ có thể thực hiện một cách tự động, chúng ta sẽ sử dụng Maven – một chương trình quản lý dự án cho phép các developers có thể quản lý về version, các dependencies (các component, thư viện sử dụng trong dự án) , quản lý build, tự động download javadoc & source, ….

Các maven plugin cần thiết để tích hợp Jersey với Swagger UI:

  • maven-dependency-plugin : plugin này giúp donwload các static Swagger UI file được đóng gói trong webjar từ Maven dependency.
  • maven-war-plugin : plugin này giúp copy các static Swagger UI file vào thư mục swagger-ui trong war file của project.
  • replacer : được sử dụng để thay thế URL đến file OpenAPI specification trong file index.html của Swaggers UI.

Ví dụ

Tôi sẽ sử dụng lại ví dụ ở bài viết “JWT – Token-based Authentication trong Jersey 2.x” để hướng dẫn các bạn cách tạo document tự động cho các API này.

Các bước thực hiện:

  • Tạo maven project và khai báo các thư viện, plugin cần thiết.
  • Cấu hình Jersey.
  • Thêm cấu hình API trong file web.xml.
  • Tạo file đặc tả API cho project: openapi.yaml.
  • Sử dụng các Swagger Annotation để mô tả API.
  • Build project và sử dụng.

Tạo maven project và khai báo các thư viện, plugin cần thiết

Tạo maven project với cấu trúc như sau:

Mở file pom.xml và thêm các dependency sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>SwaggerWithJersey2Example</groupId>
    <artifactId>SwaggerWithJersey2Example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <jersey.version>2.28</jersey.version>
        <lombok.version>1.16.20</lombok.version>
        <javax.servlet-api.version>4.0.1</javax.servlet-api.version>
        <javax.ws.rs-api.version>2.1.1</javax.ws.rs-api.version>
        <swagger.version>2.0.8</swagger.version>
        <swagger-ui.version>3.23.4</swagger-ui.version>
        <replacer.version>1.5.3</replacer.version>
    </properties>
    <dependencies>
        <!-- Javax -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${javax.servlet-api.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>${javax.ws.rs-api.version}</version>
        </dependency>
        <!-- Jersey -->
        <dependency>
            <groupId>org.glassfish.jersey.connectors</groupId>
            <artifactId>jersey-apache-connector</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-server -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-common -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-common</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-multipart</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- Java JWT: JSON Web Token for Java -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.10.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.5</version>
            <scope>runtime</scope>
        </dependency>
        <!-- Uncomment this next dependency if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms: -->
        <!-- <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.60</version>
            <scope>runtime</scope>
        </dependency> -->
        <!-- Swagger -->
        <!-- https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-jaxrs2 -->
        <dependency>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-jaxrs2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-jaxrs2-servlet-initializer -->
        <dependency>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
            <version>${swagger.version}</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <!-- Build with specified Java version -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <!-- Download Swagger UI webjar -->
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <!-- https://mvnrepository.com/artifact/org.webjars/swagger-ui -->
                                <dependency>
                                    <groupId>org.webjars</groupId>
                                    <artifactId>swagger-ui</artifactId>
                                    <version>${swagger-ui.version}</version>
                                </dependency>
                            </artifactItems>
                            <outputDirectory>${project.build.directory}/swagger-ui</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <!-- Add Swagger UI resources to the war file. -->
                <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-war-plugin -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.3</version>
                <configuration>
                    <webResources combine.children="append">
                        <resource>
                            <directory>${project.build.directory}/swagger-ui/META-INF/resources/webjars/swagger-ui/${swagger-ui.version}</directory>
                            <includes>
                                <include>**/*.*</include>
                            </includes>
                            <targetPath>swagger-ui</targetPath>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>
            <plugin>
                <!-- Replace the OpenAPI specification example URL with the local one. -->
                <!-- https://mvnrepository.com/artifact/com.google.code.maven-replacer-plugin/replacer -->
                <groupId>com.google.code.maven-replacer-plugin</groupId>
                <artifactId>replacer</artifactId>
                <version>${replacer.version}</version>
                <executions>
                    <execution>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>replace</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <file>${project.build.directory}/swagger-ui/META-INF/resources/webjars/swagger-ui/${swagger-ui.version}/index.html</file>
                    <replacements>
                        <replacement>
                            <token>https://petstore.swagger.io/v2/swagger.json</token>
                            <value>/api/openapi.json</value>
                        </replacement>
                    </replacements>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Các bạn nhớ update maven project để download tất cả thư viện về máy nhé.

Cấu hình Jersey

Mở file com.gpcoder.config.JerseyServletContainerConfig thêm khai báo package swagger: io.swagger.v3.jaxrs2.integration.resources.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.gpcoder.config;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.logging.LoggingFeature;
//Deployment of a JAX-RS application using @ApplicationPath with Servlet 3.0
//Descriptor-less deployment
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
public class JerseyServletContainerConfig extends ResourceConfig {
    public JerseyServletContainerConfig() {
        // if there are more than two packages then separate them with semicolon
        packages("io.swagger.v3.jaxrs2.integration.resources,com.gpcoder");
        register(new LoggingFeature(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), Level.INFO,
                LoggingFeature.Verbosity.PAYLOAD_ANY, 10000));
        register(JacksonFeature.class);
        
        // This authorization feature is not automatically turned on.
        // We need to turn it on by ourself.
        register(RolesAllowedDynamicFeature.class);
    }
}

Thêm cấu hình API trong file web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
    <display-name>Swagger with Jersey2 Example by gpcoder</display-name>
    <servlet>
        <servlet-name>api</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.gpcoder.config.JerseyServletContainerConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>api</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>

Tạo file đặc tả API cho project: openapi.yaml

Khai báo một số thông tin cơ bản về API và security theo OpenAPI Specification.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
prettyPrint: true
cacheTTL: 0
openAPI:
  info:
    description: "Swagger UI demo by gpcoder.com"
    version: "1.0.0"
    title: "Swagger UI Demo"
    termsOfService: "http://swagger.io/terms/"
    contact:
      email: "contact@gpcoder.com"
    license:
      name: "Apache 2.0"
      url: "http://www.apache.org/licenses/LICENSE-2.0.html"
  servers:
    - url: '/api'
  # 1) Define the security scheme type (HTTP bearer)
  components:
    securitySchemes:
      bearerAuth:            # arbitrary name for the security scheme
        type: http
        scheme: bearer
        bearerFormat: JWT    # optional, arbitrary value for documentation purposes
  # 2) Apply the security globally to all operations
  security:
    - bearerAuth: []         # use the same name as above

Trong project này, chúng ta cần chứng thực user sử dụng JWT, nên cần khai báo một số thông tin về security. Chi tiết về Security OpenAPI Specification, các bạn tham khảo thêm ở link sau: https://swagger.io/docs/specification/authentication/bearer-authentication/

Sử dụng các Swagger Annotation để mô tả API

Bây giờ chúng ta sử dụng các Swagger Annotation được cung cấp bởi Swagger core để mô tả các API.

Chi tiết tất cả các Swagger Annotation, các bạn tham khảo tại đây: https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X—Annotations

AuthService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.gpcoder.api;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.gpcoder.helper.JwTokenHelper;
import com.gpcoder.model.User;
import com.gpcoder.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
@Path("/auth")
@Tag(name = "Authentication services",
    description = "Authenticating a user and issuing a JSON Web Token (JWT)")
public class AuthService {
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Operation(summary = "Authenticating a user",
        description = "Authenticating a user with their username/ password and issuing a JSON Web Token (JWT)",
        responses = {
                 @ApiResponse(description = "Authenticated user based on the given information",
                         content = @Content(mediaType = "application/json",
                         schema = @Schema(implementation = String.class))),
                 @ApiResponse(responseCode = "200", description = "success"),
                 @ApiResponse(responseCode = "403", description = "Wrong username or password."),
                 @ApiResponse(responseCode = "500", description = "Internal Server Error.")
             })
    public Response authenticateUser(
            @Parameter(description = "The user name for login. Some test account: admin, customer, gpcoder", required = true)
            @FormParam("username") String username,
            @Parameter(description = "The password for login", required = true)
            @FormParam("password") String password) {
        // Authenticate the user using the credentials provided
        UserService userService = new UserService();
        User user = userService.getUser(username);
        if (user == null || !user.getPassword().equals(password)) {
            return Response.status(Response.Status.FORBIDDEN) // 403 Forbidden
                    .entity("Wrong username or password") // the response entity
                    .build();
        }
        // Issue a token for the user
        String token = JwTokenHelper.createJWT(user);
        // Return the token on the response
        return Response.ok(token).build();
    }
}

OrderService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.gpcoder.api;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import com.gpcoder.model.Order;
import com.gpcoder.model.Role;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
// URI:
// http(s)://<domain>:(port)/<YourApplicationName>/<UrlPattern in web.xml>/<path>
// http://localhost:8080/api/orders
@Path("/orders")
@PermitAll
@Consumes(value = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
@Produces(value = { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
@Tag(name = "Order service",
    description = "CRUD operations for order service. User must be authorized with some resources.")
public class OrderService {
    @GET
    @Path("/{id}")
    @Operation(summary = "Get order by id", description = "This action published for everyone")
    @ApiResponse(responseCode = "200", description = "Get order by id")
    public Response get(@PathParam("id") int id) {
        System.out.println("OrderService->get()");
        return Response.ok("OrderService->get()").build();
    }
    @Operation(
        summary = "Insert an order",
        responses = {
             @ApiResponse(description = "Inserted an order based on the given information",
                     content = @Content(mediaType = "application/json",
                     schema = @Schema(implementation = String.class))),
             @ApiResponse(responseCode = "200", description = "success"),
             @ApiResponse(responseCode = "401", description = "User cannot access this resource."),
             @ApiResponse(responseCode = "403", description = "User not authorized."),
             @ApiResponse(responseCode = "500", description = "Internal Server Error.")
         })
    @POST
    @RolesAllowed(Role.ROLE_CUSTOMER)
    public Response insert(Order order, @Context SecurityContext securityContext) {
        System.out.println("User: " + securityContext.getUserPrincipal().getName());
        System.out.println("OrderService->insert()");
        return Response.ok("OrderService->insert()").build();
    }
    @PUT
    @RolesAllowed({ Role.ROLE_ADMIN, Role.ROLE_CUSTOMER })
    @Operation(summary = "Update order", description = "Admin and Customer can do this action")
    public Response update(Order order) {
        System.out.println("OrderService->update()");
        return Response.ok("OrderService->update()").build();
    }
    @DELETE
    @Path("/{id}")
    @RolesAllowed(Role.ROLE_ADMIN)
    @Operation(summary = "Delete order by id", description = "Only Admin can do this action")
    public Response delete(@PathParam("id") int id) {
        System.out.println("OrderService->delete()");
        return Response.ok("OrderService->delete()").build();
    }
}

Build project và sử dụng

Các bạn chọn chuột phải lên project -> Run As -> Maven install. Sau khi chạy xong bạn sẽ thấy các thư viện Swagger UI đã được tải về và giải nén trong thư mục target của project như sau:

Mở file index.html trong thư mục target/swagger-ui/META-INF/resources/webjars/swagger-ui/${swagger-ui.version} , bạn sẽ thấy đường dẫn đến file mô tả swagger.json đã được thay thế bằng /api/openapi.json như đã config trong file pom.xml.

Mọi thứ đã xong, bây giờ các bạn có thể start server lên để kiểm tra kết quả.

  • Kiểm tra file openapi.json tồn tại và chứa tất cả các đặc tả về API document. Truy cập vào địa chỉ: http://localhost:8080/api/openapi.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
{
  "openapi" : "3.0.1",
  "info" : {
    "title" : "Swagger UI Demo",
    "description" : "Swagger UI demo by gpcoder.com",
    "termsOfService" : "http://swagger.io/terms/",
    "contact" : {
      "email" : "contact@gpcoder.com"
    },
    "license" : {
      "name" : "Apache 2.0",
      "url" : "http://www.apache.org/licenses/LICENSE-2.0.html"
    },
    "version" : "1.0.0"
  },
  "servers" : [ {
    "url" : "/api"
  } ],
  "security" : [ {
    "bearerAuth" : [ ]
  } ],
  "tags" : [ {
    "name" : "Authentication services",
    "description" : "Authenticating a user and issuing a JSON Web Token (JWT)"
  }, {
    "name" : "Order service",
    "description" : "CRUD operations for order service. User must be authorized with some resources."
  } ],
  "paths" : {
    "/auth" : {
      "post" : {
        "tags" : [ "Authentication services" ],
        "summary" : "Authenticating a user",
        "description" : "Authenticating a user with their username/ password and issuing a JSON Web Token (JWT)",
        "operationId" : "authenticateUser",
        "requestBody" : {
          "content" : {
            "application/x-www-form-urlencoded" : {
              "schema" : {
                "type" : "object",
                "properties" : {
                  "username" : {
                    "type" : "string"
                  },
                  "password" : {
                    "type" : "string"
                  }
                }
              }
            }
          }
        },
        "responses" : {
          "default" : {
            "description" : "Authenticated user based on the given information",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "string"
                }
              }
            }
          },
          "200" : {
            "description" : "success"
          },
          "403" : {
            "description" : "Wrong username or password."
          },
          "500" : {
            "description" : "Internal Server Error."
          }
        }
      }
    },
    "/orders/{id}" : {
      "get" : {
        "tags" : [ "Order service" ],
        "summary" : "Get order by id",
        "description" : "This action published for everyone",
        "operationId" : "get",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "required" : true,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        } ],
        "responses" : {
          "200" : {
            "description" : "Get order by id"
          }
        }
      },
      "delete" : {
        "tags" : [ "Order service" ],
        "summary" : "Delete order by id",
        "description" : "Only Admin can do this action",
        "operationId" : "delete",
        "parameters" : [ {
          "name" : "id",
          "in" : "path",
          "required" : true,
          "schema" : {
            "type" : "integer",
            "format" : "int32"
          }
        } ],
        "responses" : {
          "default" : {
            "description" : "default response",
            "content" : {
              "application/json" : { },
              "application/xml" : { },
              "text/plain" : { }
            }
          }
        }
      }
    },
    "/orders" : {
      "put" : {
        "tags" : [ "Order service" ],
        "summary" : "Update order",
        "description" : "Admin and Customer can do this action",
        "operationId" : "update",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Order"
              }
            },
            "application/xml" : {
              "schema" : {
                "$ref" : "#/components/schemas/Order"
              }
            },
            "text/plain" : {
              "schema" : {
                "$ref" : "#/components/schemas/Order"
              }
            }
          }
        },
        "responses" : {
          "default" : {
            "description" : "default response",
            "content" : {
              "application/json" : { },
              "application/xml" : { },
              "text/plain" : { }
            }
          }
        }
      },
      "post" : {
        "tags" : [ "Order service" ],
        "summary" : "Insert an order",
        "operationId" : "insert",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Order"
              }
            },
            "application/xml" : {
              "schema" : {
                "$ref" : "#/components/schemas/Order"
              }
            },
            "text/plain" : {
              "schema" : {
                "$ref" : "#/components/schemas/Order"
              }
            }
          }
        },
        "responses" : {
          "default" : {
            "description" : "Inserted an order based on the given information",
            "content" : {
              "application/json" : {
                "schema" : {
                  "type" : "string"
                }
              }
            }
          },
          "200" : {
            "description" : "success"
          },
          "401" : {
            "description" : "User cannot access this resource."
          },
          "403" : {
            "description" : "User not authorized."
          },
          "500" : {
            "description" : "Internal Server Error."
          }
        }
      }
    }
  },
  "components" : {
    "schemas" : {
      "Order" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "integer",
            "format" : "int32"
          },
          "name" : {
            "type" : "string"
          }
        }
      }
    },
    "securitySchemes" : {
      "bearerAuth" : {
        "type" : "http",
        "scheme" : "bearer",
        "bearerFormat" : "JWT"
      }
    }
  }
}
  • Kiểm tra Swagger UI đã hiển thị đầy đủ các API document như đã config trong file WebContent/WEB-INF/openapi.yaml và đúng như mô tả trong Swagger Annotation. Truy cập địa chỉ: http://localhost:8080/swagger-ui/index.html

Các bạn có thể chạy thử POST /auth để lấy jwt token và chọn Authorize để config token cho các request đến API cần chứng thực.

Tiếp tục, chúng ta sẽ kiểm tra thông tin đã mô tả trong phương thức OrderService#insert.

Như các bạn có thể thấy, Swagger có thể generate ra 1 file config (.json hoặc .yaml) từ chính code của chương trình. Tuy nhiên phần UI của swagger lại khá rườm rà không đẹp lắm. Để có một beautiful document cho các API, các bạn có thể tìm hiểu thêm về Slate – một công cụ open source giúp generate ra các file html, css,… từ 1 file config .md . Slate có UI cực kì đẹp và khoa học.

Slate rất đẹp nhưng lại không thể generate ra file config như swagger. Vì vậy để tận dụng thế mạnh của Swagger và Slate, chúng ta cần phải convert 1 file config.yaml của Swagger sang 1 file config.md. Sau đó dùng Slate để generate ra các file html, css,… để publish ra bên ngoài. Chi tiết các bạn có thể tham khảo thêm tại link sau: https://github.com/lord/slate.

 

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng IT hấp dẫn trên TopDev

Data types trong RAML

data types
Data types trong RAML

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Khi khai báo các request parameter, path parameter, request body, response body, … trong RAML, chúng ta cần khai báo kiểu dữ liệu của những thông tin này. RAML định nghĩa nhiều loại data types khác nhau giúp chúng ta có thể giải quyết những nhu cầu của mình. Trong bài viết này, chúng ta sẽ cùng tìm hiểu về data types trong RAML như thế nào, các bạn nhé!

  Cách cài đặt .NET Framework 3.5 trên Windows thành công 100%
  Định nghĩa request body và response với RAML

Xem thêm nhiều việc làm IOT Engineering hấp dẫn trên TopDev

Đầu tiên, các bạn hãy nhìn vào hình dưới, tổng quan thì RAML định nghĩa những loại data types được phân cấp như sau:

Như các bạn thấy, ở top của diagram trên, chúng ta có data type là any. Thông tin được định nghĩa với type any sẽ không có hạn chế gì cả, các bạn có thể định nghĩa số, chữ, đối tượng, bất cứ kiểu dữ liệu gì mà các bạn muốn.

Bên dưới data type any, chúng ta có các data type chi tiết, cụ thể hơn, hạn chế hơn nếu thông tin được định nghĩa bởi những data type này. Như các bạn thấy, chúng ta có các scalar types, array, object, union, XSD schema và cả JSON schema.

Nói nôm na scalar types là những kiểu dữ liệu đơn giản, không được định nghĩa từ những kiểu dữ liệu khác, ví dụ như boolean, string, null, file,… Giá trị của những thông tin được định nghĩa với những scalar types này là single, boolean thì chỉ có true, false; string thì giá trị chỉ là các chuỗi…

Kiểu dữ liệu array thì giúp chúng ta định nghĩa mảng dữ liệu, có thể là mảng string, mảng number hoặc thậm chí là mảng các đối tượng, giống như trong Java đó các bạn!

Object thì giúp chúng ta định nghĩa thông tin về đối tượng, với nhiều properties thuộc về đối tượng này.

Union là kiểu dữ liệu giúp chúng ta có thể định nghĩa giá trị sử dụng nhiều loại data types khác nhau.

XSD và JSON schema thì giúp chúng ta định nghĩa thông tin sử dụng schema của XML và JSON.

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Embedded Là Gì? Triển Vọng Công Việc Nào Từ Lĩnh Vực Embedded Cho Ngành IT Việt Nam Hiện Tại?

embedded là gì
Embedded Là Gì? Triển Vọng Công Việc Nào Từ Lĩnh Vực Embedded Cho Ngành IT Việt Nam Hiện Tại?

Sự phát triển của IoT trở nên mạnh mẽ hơn bao giờ hết cũng đồng thời mở ra cơ hội cho sự lớn mạnh của Embedded. Với những cá nhân đang hoạt động trong lĩnh vực công nghệ thông tin, đây không còn là thuật ngữ quá xa lạ, nhưng với người ngoài ngành, đây vẫn là khái niệm còn mới lạ. Vậy Embedded là gì? Triển vọng nghề nghiệp nào cho bạn nếu quyết định theo đuổi vị trí Embedded Developer?

embedded là gì
Sự phát triển của Embedded trong cuộc sống hiện đại

Tìm hiểu về thuật ngữ Embedded là gì

1. Embedded System là gì?

Embedded System còn được biết đến với cái tên thuần Việt là Hệ thống nhúng. Đây là một phần mềm được viết dựa trên sự kết hợp giữa phần cứng và phần mềm của máy tính, với một chức năng chung hoặc một chức năng cụ thể cho hệ thống máy chủ. Khác với phần mềm dành cho web hay mobile, embedded system tương tác với thế giới thật trong một khoảng thời gian thật. Rất nhiều các thiết bị như thiết bị chế biến, y tế, ô tô, đồ gia dụng, các thiết bị di động,… đều cần đến sự ứng dụng của Embedded System.

  Cơ bản về Embedded và ứng dụng
  .NET – Strong Named Assembly là gì?

2. Một số đặc điểm chính của Embedded System

  • Một Embedded system sẽ gồm có phần cứng (hardware), phần mềm (software) và phần sụn (firmware). Do đó sẽ có sự khác biệt về độ phức tạp, chức năng cũng như ảnh hưởng đến các phần này khi sử dụng.
  • Chúng thường được sử dụng cho cảm biến và tính toán thời gian thực trên IoT, thông qua việc kết nối internet mà không yêu cầu cần có sự hoạt động của người dùng.
  • Hoạt động dựa trên bộ vi điều khiển hoặc bộ vi xử lý, đều là các mạch tích hợp cung cấp năng lượng tính toán cho hệ thống.
  • Chúng thường được nhúng trong một hệ thống chung, lớn hơn để thực hiện một chức năng cụ thể, chuyên biệt trong hệ thống chứ không phải là thực hiện các nhiệm vụ khác nhau.

Xem embedded tuyển dụng đãi ngộ tốt trên TopDev

3. Embedded Software là gì?

Xoay quanh embedded không thể không nhắc đến khái niệm Embedded Software. Tương tự như hệ thống nhúng, Embedded Software là một phần mềm cụ thể sẽ được ghi vào bộ nhớ của thiết bị để có thể phục vụ cho mục đích ROM một cách dễ dàng hơn. Firmware được lưu trữ trong các bộ nhớ cố định, trong đó, ROM có thể lập trình và PROM có thể xóa hoặc bộ nhớ Flash.

embedded software

Embedded được ứng dụng như thế nào trong cuộc sống?

Hiện nay hệ thống Embedded đã được ứng dụng rất nhiều trong cuộc sống mà bạn có thể bắt gặp ở bất cứ đâu.

  • Thành phố thông minh (Smart City): Rất nhiều các quốc gia trên thế giới đã ứng dụng các thiết bị điện tử và hệ thống IoT trong việc quản lý mạng lưới thông tin của thành phố. Mạng lưới sinh thái thông minh này sẽ giúp chính quyền dễ dàng hơn trong việc quản lý quy trình nhập cư, tình hình an ninh, giao thông, y tế, giáo dục,… và rất nhiều các dịch vụ cộng đồng khác.
  • Nhà cửa thông minh (Smarthome): Không khó để bắt gặp hầu hết các vật dụng trong ngôi nhà của bạn hiện tại đều ít nhiều có sự góp mặt của Embedded Software. Từ các thiết bị điện tử, điện thoại thông minh, điều hòa không khí, TV, tủ lạnh, máy ảnh, robot hút bụi cho đến máy pha cà phê,… đều có sự hiện diện của phần mềm embedded. Chủ nhà sẽ quản lý các thiết bị này thông qua internet và các đường truyền kết nối không dây. Mục đích của công nghệ này là để tiết kiệm tối đa công sức và thời gian mà con người bỏ ra cho các hoạt động nội trợ hằng ngày.

ứng dụng của embedded

  • Công nghiệp ô tô: Công nghệ hiện đại đã cho phép sản xuất một chiếc xe ô tô với sự can thiệp xuyên suốt của hệ thống embedded. Từ hệ thống hộp số tự động, máy kiểm soát hành trình, hệ thống chống cứng bó phanh hay các cảm biến được thiết kế để giúp xe di chuyển an toàn, giảm thiểu các rủi ro,… Tất cả đều nhờ có sự xuất hiện của phần mềm embedded.
  • Không gian vũ trụ và lĩnh vực quân sự: Để đảm bảo an toàn ở mức cao nhất cho con người trong quá trình chinh phục không gian, việc sử dụng các giải pháp embedded thật sự cần thiết. Hệ thống embedded có thể được ứng dụng trong việc chế tạo các cảm biến hiệu suất cao, các giải pháp để điều hướng và liên lạc trong không gian và trong quân sự. Các tín hiệu được gửi đi giữa hai bên giúp cho việc điều khiển vệ tinh hay khi chiến đấu diễn ra suôn sẻ hơn.

Xem thêm Những Thông Tin Cần Biết Về Vị Trí ICT Business Analyst

Cơ hội công việc nào cho các Embedded Developer ở thời điểm hiện tại

Kết quả thống kê của BBC Research Group cho thấy, từ cách đây hơn 10 năm, tổng doanh thu của thị trường Embedded System và các công việc liên quan đến Embedded Software đã hơn 88 tỷ USD. Ở Việt Nam hiện tại, sự phát triển của các công việc liên quan đến embedded vẫn chưa thật sự bùng nổ so với thế giới. Tuy nhiên, trong thời điểm số hóa toàn cầu như hiện nay, công việc này chắc chắn sẽ được săn đón nhiều hơn trong tương lai.

Tham khảo tuyển dụng embedded Hà Nội lương cao trên TopDev

Các công việc có chuyên môn liên quan đến Blockchain, Fullstack hay Embedded Software trong tương lai sẽ tiếp tục được nhiều công ty chú ý đến. Để có thể tìm được cho mình một công việc ưng ý, bạn hãy chú trọng trong việc nâng cấp chuyên môn và kỹ năng chuyên ngành cho mình. Đó chính là con đường để bạn đón đầu xu hướng công việc trong tương lai và có cho mình mức thu nhập tốt nhất.

Có thể hiểu một cách đơn giản rằng thế giới thông minh mà bạn đã được sống hiện tại chính là kết quả nổi bật nhất của Embedded System. Embedded gắn liền với sự thông minh, do đó trong kỷ nguyên trí tuệ nhân tạo như hiện tại, các Embedded developers đang có cơ hội rộng mở hơn bao giờ hết.

Có thể bạn quan tâm:

Truy cập ngay việc làm IT đãi ngộ tốt trên TopDev

Chia sẻ 6 cách sửa lỗi bàn phím máy tính bị lag, phản hồi chậm

sửa lỗi bàn phím
Chia sẻ 6 cách sửa lỗi bàn phím máy tính bị lag, phản hồi chậm

Bài viết được sự cho phép của blogchiasekienthuc.com

Bạn đã bao giờ cảm thấy khó chịu với bàn phím mà bạn đang gõ chưa, nhất là đối với bàn phím rời (kết nối với máy tính thông qua Bluetooh) thì hay có hiện tượng giật, lag và delay nhẹ. Tức là bạn gõ được một lúc rồi thì chữ mới hiện ra ấy.

Tuy nhiên, trước khi đến với những cách khắc phục bên dưới thì bạn cần phải đảm bảo rằng nguyên nhân không phải đến từ những lỗi khác. Bởi đôi khi máy tính của bạn bị chậm cũng là nguyên nhân khiến bàn phím của bạn bị delay.

Vậy nên, hãy thử khởi động lại máy tính và kiểm tra lại bàn phím một lần nữa xem sao, hoặc nếu có sẵn một cái bàn phím khác thì có thể kết nối thử xem có bị hiện tượng tương tự như vậy không.

Xem thêm các việc làm DevOps hấp dẫn trên TopDev

#1. Tắt tính năng Filter Keys

Filter Keys có cách hoạt động tương tự Sticky Keys. Nói một cách dễ hiểu thì Filter Keys là khả năng tiếp cận giúp bạn điều chỉnh phản hồi của bàn phím khi thao tác tổ hợp phím lặp lại đi lặp lại sẽ được bỏ qua. Đây có thể là nguyên nhân gây ra độ trễ cho bàn phím. Bạn có thể tắt Filter Keys bằng cách sau:

+ Bước 1: Mở Windows Settings lên bằng cách nhấn tổ hợp phím Win + I.

cach-giam-do-tre-ban-phim-tren-windows (1)

+ Bước 2: Bạn tìm đến tính năng Ease of Access => và bấm chọn tính năng này.

cach-giam-do-tre-ban-phim-tren-windows (2)

+ Bước 3: Bạn vào tab Keyboard => sau đó ở tùy chọn Use Filter Keys, bạn chọn OFF.

cach-giam-do-tre-ban-phim-tren-windows (3)

#2. Thay đổi thuộc tính bàn phím

Vâng, thay đổi thuộc tính bàn phím cũng là một cách để giúp bạn giải quyết vấn đề bị delay khi gõ phím.

+ Bước 1: Bạn nhấn tổ hợp phím Win + R để mở hộp thoại Run => sau đó nhập lệnh control keyboard => và bấm Enter để mở Keyboard Properties.

cach-giam-do-tre-ban-phim-tren-windows (8)

+ Bước 2: Ở khu vực Character repeart có hai thanh trượt là Repeart delay và Repeart rate. Bạn hãy điều chỉnh lại mức độ của 2 thanh trượt này và test thử bằng cách viết chữ vào ô bên dưới.

cach-giam-do-tre-ban-phim-tren-windows (9)

+ Bước 3: Sau khi tìm thấy mức Repeart delay và Repeart rate “lí tưởng” thì bạn bấm Apply => và OK để kết thúc. Đây thực chất là cách điều chỉnh tốc độ chuột nhé các bạn !

#3. Sử dụng tính năng Keyboard Troubleshooter

Windows cung cấp cho chúng ta một số công cụ hỗ trợ khắc phục sự cố trong quá trình sử dụng Windows 10, trong đó có cả công cụ phát hiện và sửa lỗi liên quan đến bàn phím. Vậy nên, nếu bạn đang gặp vấn đề về bàn phím thì không có lý do gì chúng ta lại bỏ qua công cụ này cả.

+ Bước 1: Mở Windows Settings lên bằng cách sử dụng tổ hợp phím Win + I trên bàn phím.

+ Bước 2: Tìm đến phần Update & Security => và chọn tính năng Troubleshoot.

cach-giam-do-tre-ban-phim-tren-windows (10)

+ Bước 3: Sau đó bạn bấm vào Additional troubleshooters, bạn tìm và bấm chọn Keyboard => chọn tiếp Run the troubleshooter để Windows thực hiện quá trình Fix lỗi của nó.

cach-giam-do-tre-ban-phim-tren-windows (11)

#4. Cập nhật lại Driver bàn phím

Đôi khi lỗi bàn phím bị trễ tín hiệu cũng có thể do driver bàn phím của nó đã lỗi thời, hoặc bị xung đột. Để cập nhật lại driver, bạn làm như sau:

+ Bước 1: Nhấn tổ hợp phím Win + X => sau đó nhấn vào Device Manager.

Hoặc một cách khác là nhấn chuột phải vào This PC trên màn hình Desktop => chọn Manage => chọn Device Manager.

cach-giam-do-tre-ban-phim-tren-windows (4)

+ Bước 2: Bạn tìm đến driver bàn phím => sau đó nhấp đúp chuột vào nó.

cach-giam-do-tre-ban-phim-tren-windows (5)

+ Bước 3: Bạn vào tab Driver => sau đó nhấn vào Update Driver.

cach-giam-do-tre-ban-phim-tren-windows (6)

+ Bước 4: Chọn Search automatically for drivers.

cach-giam-do-tre-ban-phim-tren-windows (7)

+ Bước 5: Đợi một lúc để Update Driver (nếu có).

NOTE: Nếu cách trên không có tác dụng thì bạn nên Update Driver thông qua Windows Update, hoặc tham khảo bài viết hướng dẫn cách update driver cho máy tính chuẩn nhất nhé.

  12 loại bàn phím cho lập trình viên (Phần 1)
  "Làm PM, theo anh không cần biết về code, nhưng phải hiểu về SQL, database, những khái niệm cơ bản của code"

#5. Đối với bàn phím không dây

Đây là cách sửa lỗi với bàn phím không dây. Nếu bạn sử dụng bàn phím có dây thì hãy bỏ qua cách này nhé.

cach-giam-do-tre-ban-phim-tren-windows (1)

+ Thay pin

Có thể bàn phím bị delay hoặc viết không ra chữ là do Pin đã cạn kiệt. Thay pin bàn phím hoặc sạc Pin cho bàn phím và thử lại xem vấn đề có được giải quyết không.

+ Kiểm tra kết nối

Bạn hãy thử gắn lại USB Receiver của bàn phím vào một khe USB khác. Nếu có thể, hãy đặt bàn phím gần với máy tính của bạn để đảm bảo kết nối được tốt nhất.

#6. Thay bàn phím mới

Và cuối cùng, nếu như đã thử hết cách rồi, và bạn cũng đã thử mang bàn phím đó kết nối sang một máy tính khác nhưng vẫn bị hiện tượng như vậy thì khả năng cao là bàn phím bị lỗi rồi. Bạn vui lòng xuống tiền mua một em bàn phím khác nhé

#7. Lời kết

Ok, trên đây là 5 cách giúp bạn kiểm tra và sửa lỗi bàn phím máy tính khi bị trễ, bị delay hiệu quả nhất. Ngoài những cách mà mình chia sẻ bên trên ra, nếu bạn còn biết thêm phương pháp nào hiệu quả khác nữa thì đừng quên chia sẻ lại cho mọi người thông qua khung comment bên dưới nhé.

CTV: Hoàng Tuấn – Blogchiasekienthuc.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng CNTT hấp dẫn trên TopDev

Làm thế nào để Test Jersey Rest API với JUnit?

test jersey rest api
Làm thế nào để Test Jersey Rest API với JUnit?

Bài viết được sự cho phép của tác giả Giang Phan

Trong bài này tôi sẽ hướng dẫn các bạn cách viết Unit Test để test Jersey REST API sử dụng Jersey Test.

Một số kiến thức bạn cần nắm trước khi xem phần tiếp theo của bài viết này:

Test REST API cần test những gì?

Khi test một resource REST API, thông thường có một vài thứ cần test như sau:

  • HTTP response code : 200 OK, 201 Created, 204 NO Content, …
  • HTTP headers trong response: Content-Type, Content-Encoding, Content-Length, Content-Language, …
  • Payload (JSON, XML).
  Giới thiệu JUnit
  Liệu tôi có làm Junior Developer "mãn kiếp"?

Xem thêm các việc làm IBM lương cao trên TopDev

Tạo Jersey Project

Tương tự như các bài viết trước, chúng ta sẽ tạo Jersey project với cấu trúc như sau:

Thêm thư viện Jersey test sau vào file pom.xml:

<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.test-framework.providers/jersey-test-framework-provider-grizzly2 -->         <dependency>             <groupId>org.glassfish.jersey.test-framework.providers</groupId>             <artifactId>jersey-test-framework-provider-grizzly2</artifactId>             <version>2.29</version>         </dependency>

Xây dựng Jersey Rest API

Giả sử chúng ta có các API sau:

Jersey implement các API như sau:

package com.gpcoder.api;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.SecurityContext;

import com.gpcoder.model.Order;

// URI:
// http(s)://:(port)/// // http://localhost:8080/api/orders
@Path("/orders")
public class OrderService {

@GET
@Path("/{id}")
public Response get(@PathParam("id") int id) {
Order order = new Order();
order.setId(1);
order.setName("gpcoder");
return Response.ok(order).build();
}

@POST
public Response insert(Order order, @Context SecurityContext securityContext) {
int newOrderId = 999;
return Response.status(Status.CREATED).entity(newOrderId).build();
}

@PUT
@Path("/{id}")
public Response update(@PathParam("id") int id, Order order) {
return Response.ok().build();
}

@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") int id) {
return Response.status(Status.NO_CONTENT).build();
}
}

Test Jersey Rest API với JUnit và JerseyTest

Để test Jersey REST API chúng ta sẽ sử dụng thư viện JUnit Test và Jersey Test .

package com.gpcoder.api;

import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import com.gpcoder.model.Order;

public class OrderServiceTest extends JerseyTest {

@Override
public Application configure() {
return new ResourceConfig(OrderService.class);
}

@Test
public void testGetById() {
Response response = target("/orders/1").request().accept(MediaType.APPLICATION_JSON).get();
assertEquals("Should return status 200", 200, response.getStatus());
assertNotNull("Should return user object as json", response.getEntity());
assertEquals("Http Content-Type should be: ", MediaType.APPLICATION_JSON,
response.getHeaderString(HttpHeaders.CONTENT_TYPE));
assertThat("Should return new order contains gpcoder string",
response.readEntity(String.class), containsString("gpcoder"));
}

@Test
public void testCreate() {
Order order = new Order(1, "gpcoder");
Response response = target("/orders").request().post(Entity.entity(order, MediaType.APPLICATION_JSON));
assertEquals("Should return status 201", 201, response.getStatus());
assertNotNull("Should return new order id", response.readEntity(Integer.class));
}

@Test
public void testUpdate() {
Order order = new Order(1, "gpcoder edited");
Response response = target("/orders/1").request().put(Entity.entity(order, MediaType.APPLICATION_JSON));
assertEquals("Should return status 200", 200, response.getStatus());
}

@Test
public void testDelete() {
Response response = target("/orders/1").request().delete();
assertEquals("Should return status 204", 204, response.getStatus());
}
}

Giải thích đôi chút về chương trình trên:

  • Đầu tiên, chúng ta tạo một class Test extend từ JerseyTest. Đây là bắt buộc nếu muốn test JAX-RS và Jersey-based applications sử dụng Jersey test framework.
  • Override phương thức configure() : chỉ định resource cần test.
  • Tiếp theo viết các method JUnit test.
  • Để call các REST API, chúng ta sẽ sử dụng phương thức target()request() để tạo WebTarget, Builder tương tự như cách sử dụng Jersey Client.
  • Cuối cùng gửi các request thông qua các phương thức: post()get()put()delete().

Trên đây là một số hướng dẫn cơ bản để test REST API sử dụng thư viện Jersey test. Trong bài viết tiếp theo, tôi sẽ hướng dẫn các bạn một thư viện khác rất mạnh mẽ để test web service là REST Assured.

Tài liệu tham khảo:

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng IT hấp dẫn trên TopDev

Vì sao feedback rất quan trọng?

sự quan trọng của feedback
Vì sao feedback rất quan trọng?

Bài viết được sự cho phép của tác giả Huy Trần

Có một điều chắc các bạn cũng nhận ra, là khi làm việc với nhiều khách hàng Âu Mỹ hay Nhật, họ thường rất chịu khó đưa ra lời khen ngợi khi chúng ta làm gì đó tốt, thậm chí là chưa tốt, và họ cũng không ngần ngại chỉ trích hay góp ý khi có điều gì đó ngứa tai gai mắt.

Đây là một kĩ năng communication rất quan trọng. Gọi là Giving Feedback.

  10 Công cụ Go-To Tech dành riêng cho các Software Developer
  10 kênh Youtube học lập trình không thể bỏ qua dành cho Junior Web Developer / Designer

Xem thêm tuyển dụng C++ hấp dẫn trên TopDev


Feedback có thể hiểu là đưa ra ý kiến phản hồi cho một ai đó về một cái gì đó sau một chuyện gì đó.

Có 3 loại feedback thường thấy, đó là:

  • Positive Feedback: phản hồi dương tính, à nhầm mang tính tích cực, đưa ra để cho đối phương biết mình ghi nhận những cố gắng của họ, rằng đang làm tốt một việc gì đó, khích lệ họ để keep it up.
  • Constructive Feedback: phản hồi mang tính xây dựng, đưa ra với mục đích cho đối phương biết hành động của họ cần phải thay đổi.
  • Destructive Feedback: đây là thể loại constructive feedback phiên bản lỗi, được đưa ra một cách vội vã thiếu tế nhị, không đúng nơi đúng chỗ, hoặc đúng người nhưng sai thời điểm. Cũng là loại feedback phổ biến nhất ở trên đường, cũng như ở trên mạng. Tác dụng của nó là zero, còn tác hại thì vô cùng. Mình nghĩ người ta hay đưa ra kiểu feedback này chỉ vì ngứa mồm. Ở nhiều nơi mà cuộc sống khắc nghiệt, thì người đưa ra feedback có khả năng nhận được gói mát xa tẩm quất miễn phí chỉ cần trả tiền viện phí.

Ví dụ ở đoạn mở bài có nhắc tới, bạn làm gì cũng đc khách hàng khen, kể cả trễ deadline, thì đó gọi là positive feedback. Khi đồng đội mình hoàn thành dự án và launch thành công thì các sếp thường mở launch party và đọc tên từng người để ghi nhận công lao, đó là positive feedback. Coi Rap Việt ta thường thấy các bạn thí sinh shout out to my teacher Binz hay to anh em homie DCOD thì đó cũng là positive feedback.

Khi bạn thấy một ai đó làm gì sai, bạn muốn họ sửa, hoặc bạn tìm ra một con bug của một phần mềm nào đó và bạn viết bug report, bạn muốn đưa ra một ý tưởng gì đó để cải thiện, cũng lại coi Rap Việt bạn thấy Binz hay đưa ra phân tích về flow, vần của thí sinh, hay bạn thấy anh em WeBuild đưa ra feedback là ko biêt bao giờ huytd lại drop project, phần lớn trong số đó là constructive feedback mặc dầu tùy theo cách đưa ra feedback, nó sẽ trở thành destructive feedback.

Vậy thì phải đưa ra feedback như thế nào?

Có 2 yếu tố quan trọng khi đưa ra feedback đó là:

  • Be specific: đưa ra feedback đúng nơi đúng chỗ và đúng nội dung, và feedback như nào để người ta có thể take action được. Bạn không nên thấy một người mới start một cái side project và bay vào nói “Để xem khi nào anh drop”, vì nó ko add thêm value gì cho anh ấy cả, và khi nghe feedback như thế thì bạn expect họ sẽ làm gì?
  • Be timely: Thường thì real time feedback là tốt nhất, nhưng nếu tình thế ko cho phép thì cũng nên đưa ra feedback trong vòng 48 tiếng đồng hồ (ví dụ các bạn gái khi thấy người yêu mãi chơi game ko lo xếp quần áo, thì thay vì chạy tới rút dây máy PS5, nên bình tĩnh chờ người ta đánh xong ván đã, rồi hãy bắt úp mặt vô tường quỳ gối, nhưng cũng đừng nên chờ 1 tuần sau mới lôi ra nhắc lại chuyện cũ để rồi bị nói là nắng mưa vô cớ).

Để đưa ra feedback một cách tốt hơn, người ta nghĩ ra rất nhiều mô hình, các bạn có thể search thêm với từ khóa “Feedback Model”, hoặc đọc thêm tại đây hoặc đây.

Nhìn chung, các mô hình này đều xoay quanh 4 yếu tố:

  • Context: Nói về tình huống khi bạn nhìn thấy sự việc xảy ra.
  • Behavior: Nói về hành vi của những người liên quan đế vấn đề bạn đang feedback. Nói một cách khách quan nhất, và đừng có đánh giá gì về hành động đó cả.
  • Impact: Nói về ảnh hưởng mà hành động đó gây ra, và nó tác động đến bạn hoặc những người xung quanh như nào.
  • Follow Up: Đưa ra gợi ý về những gì người nhận feedback cần hoặc nên làm.

Ví dụ, bạn đang review code và phát hiện ra một vấn đề gì đó nên sửa:

  • “Code như cức, senior rồi mà còn code như này à?!” – Destructive feedback
  • “Tôi thấy chỗ này bạn xài let với const hơi lộn xộn, nó không ảnh hưởng gì nhiều về mặt logic nhưng làm vậy code style sẽ trở nên thiếu đồng nhất, tôi nghĩ bạn nên sửa hết về let, ý bạn sao?” – Constructive feedback

Một ví dụ khác, bạn Tèo vừa bỏ ra 1 ngày trời để setup mấy cái github actions giúp tự động chạy unit test trên mỗi PR của team, giúp nâng cao chất lượng code của cả team, là một teammate, bạn có thể làm gì?

  • Lên slack và post: “Shout out to my Tèo homie, cái github actions tự động chạy test của bạn ấy giúp cho cả team mình thấy yên tâm hơn khi fix code. Keep it up man!” – Positive feedback
  • Im lặng ko nói gì – No feedback = Destructive feedback, đến kì lương sau thì ko ai thấy Tèo đi làm nữa.

Một ví dụ khác nữa, lên WeBuild thấy một bạn thành viên mới post lên rằng: “Hi @channel, mình là mem mới, rất vui được làm quen với các bạn”, phản ứng của mọi người là:

  • Đồng loạt thả những cái reaction giận dữ, vào comment: “Bớ admin ơi có đứa tag channel!!!”, “Bà nội mày ai cho mày tag channel!!!!”, rồi thi nhau leave room – Destructive feedback, bị chửi bạn mem mới kia sẽ lồng lộn lên chửi lại rồi bỏ WeBuild mà đi, có khi đi nơi khác mà chửi WeBuild ko thương tiếc.
  • Vào comment hoặc ping riêng một cách nhã nhặn: “Bạn ơi, vừa rồi mình thấy bạn tag channel ở #general, đây là public channel nên có mấy nghìn thành viên lận, tag vậy sẽ làm phiền mọi người đấy, bạn edit lại đi nha.” – Constructive feedback, nếu nhận được phản hồi như thế này, mình không nghĩ có ai cảm thấy offense và sẽ vui vẻ sửa sai ngay.

Các bạn thấy đấy, việc đưa ra feedback là rất quan trọng và cần thiết, và đưa ra feedback như nào lại quan trọng không kém. Cái ranh giới giữa constructive feedback và destructive feedback nó chỉ cách nhau một cái ngứa mồm và một nhịp cảm xúc.

Be a good communicator, don’t be that person.

Bài viết gốc được đăng tải tại thefullsnack.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng các vị trí CNTT hấp dẫn trên TopDev

Định nghĩa request body và response với RAML

request body và response
Định nghĩa request body và response với RAML

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Đối với các request với HTTP method là POST, PUT, DELETE, thông thường chúng ta sẽ cần định nghĩa request body. Với RAML thì chúng ta sẽ định nghĩa cấu trúc của request body như thế nào và làm thế nào để định nghĩa response với RAML. Trong bài viết này, chúng ta hãy cùng nhau tìm hiểu các bạn nhé!

  Định nghĩa request URL với RAML
  10 Java Web Framework tốt nhất

Xem thêm tuyển dụng AngularJS lương cao trên TopDev

Giả sử mình định nghĩa một request để thêm mới thông tin sinh viên với nội dung ban đầu như sau:

#%RAML 1.0
baseUri: https://localhost:8081/api
title: Student Management System
version: 1.0

/students:
post:
description: Add new student

Để định nghĩa request body cho request này, chúng ta sẽ khai báo thêm section body với content-type, kiểu dữ liệu và có thể thêm example cho request body này nữa. Ví dụ như sau:

/students:
post:
description: Add new student
body:
application/json:
type: Student
example: { "id" : 5, "name" : "Khanh" }

Như các bạn thấy, mình đã định nghĩa content-type cho data trong request body trong ví dụ này của mình là application/json với kiểu dữ liệu là Student như sau:

types:
Student:
type: object
properties:
id:
required: true
type: integer
name:
required: true
type: string

Để định nghĩa response cho một request trong RAML, chúng ta sẽ sử dụng section responses. RAML cho phép chúng ta định nghĩa response cho từng HTTP  status code và với mỗi response, tương tự như request body, chúng ta cũng có thể định nghĩa response body. Ví dụ như sau:

/students:
post:
description: Add new student
body:
application/json:
type: Student
example: { "id" : 5, "name" : "Khanh" }
responses:
201:
body:
application/json:
type: String
example: "Created"

Như các bạn thấy, ngay sau khi khai báo section cho HTTP status code, chúng ta sẽ khai báo section body để khai báo nội dung của response body. Chúng ta cũng có thể khai báo content-type, kiểu dữ liệu, example tương tự như request body.

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng CNTT hấp dẫn trên TopDev

Tìm hiểu cơ chế Lazy Evaluation của Stream trong Java 8

lazy evaluation
Tìm hiểu cơ chế Lazy Evaluation của Stream trong Java 8

Bài viết được sự cho phép của tác giả Giang Phan

Trong bài viết “Giới thiệu về Stream API trong Java 8” , chúng ta đã tìm hiểu về các đặc điểm, các làm việc của Stream trong Java 8. Ở bài viết này, tôi muốn giải thích kỹ hơn về cơ chế Lazy Evaluation của Stream trong Java 8.

  10 tips để trở thành Java Developer xịn hơn
  Những mã xấu mà Java 8 có thể khử

Xem thêm tuyển dụng Java lương cao trên TopDev

Như chúng ta đã biết, Stream có một đặc điểm rất quan trọng là cho phép tối ưu hóa hiệu xuất của chương trình thông qua cơ chế lazy evaluation, nghĩa là chúng không được thực hiện cho đến khi cần thiết. Các hoạt động tính toán trên source data chỉ được thực hiện khi một terminal operation được khởi tạo và các source element chỉ được sử dụng khi cần.

Hãy xem ví dụ sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
class Employee {
    String name;
    String department;
    int salary;
}
class EmployeeRepository {
    private static final Map<Integer, Employee> employees = new HashMap<>();
    static {
        employees.put(1, new Employee("gpcoder 1", "A", 50));
        employees.put(2, new Employee("gpcoder 2", "B", 100));
        employees.put(3, new Employee("gpcoder 3", "A", 150));
        employees.put(4, new Employee("gpcoder 4", "B", 200));
    }
    
    public Employee findById(Integer id) {
        System.out.println("findById: " + id);
        return employees.get(id);
    }
}
class EmployeeFilter {
    
    public static Predicate<Employee> filterDepartmentEqualsWith(String department) {
        return e -> {
            System.out.println("filterDepartmentEqualsWith: " + e);
            return e.getDepartment().equals(department);
        };
    }
    
    public static Predicate<Employee> filterSalaryGreaterThan(int salary) {
        return e -> {
            System.out.println("filterSalaryGreaterThan: " + e);
            return e.getSalary() >= salary;
        };
    }
}
public class Java8StreamDeeply {
    
    public static void main(String[] args) {
        EmployeeRepository employeeRepository = new EmployeeRepository();
        
        Integer[] empIds = { 1, 2, 3, 4 };
        
        Stream.of(empIds)
          .map(employeeRepository::findById)
          .filter(EmployeeFilter.filterSalaryGreaterThan(100))
          .filter(EmployeeFilter.filterDepartmentEqualsWith("A"))
          .findFirst();
    }
}

Chạy chương trình trên, chúng ta có kết quả như sau:

1
2
3
4
5
6
7
8
findById: 1
filterSalaryGreaterThan: Employee(name=gpcoder 1, department=A, salary=50)
findById: 2
filterSalaryGreaterThan: Employee(name=gpcoder 2, department=B, salary=100)
filterDepartmentEqualsWith: Employee(name=gpcoder 2, department=B, salary=100)
findById: 3
filterSalaryGreaterThan: Employee(name=gpcoder 3, department=A, salary=150)
filterDepartmentEqualsWith: Employee(name=gpcoder 3, department=A, salary=150)

Như bạn thấy, chương trình của chúng ta có 4 phần tử nhưng chỉ 3 phần tử được thực thi. Tại sao vậy?

Trong ví dụ trên, tôi sử dụng 3 Intermediate operations: 1 map() và 2 filter() operations. Chúng ta có 4 phần tử ở Stream source, mỗi phần tử có thể sẽ được thực thi 1 lần qua tất cả intermediate operation.

Đầu tiên, nó sẽ kiểm tra tất cả các operation trên phần tử có id là 1. Vì salary của nó không thõa filter salary, nên xử lý sẽ chuyển sang phần tử kế tiếp – id là 2. Không thực hiện trên filter deparment.

Tiếp theo, id 2 chỉ thõa mãn điều kiện salary, không thõa mãn điều kiện department, nên xử lý chuyển sang phần tử kế tiếp – id là 3.

Tại id 3, thõa mãn tất cả điều kiện ở trên và Stream evaluate yêu cầu Terminal Operations findFirst() và trả về kết quả.

Các operation trên các phần tử còn lại sẽ không được thực thi – id 4.

Có thể thấy rằng, Stream hoạt động tuần tự trên từng phần tử của source, duyệt qua tất cả các immediate operations, rồi đến terminal operation, cuối cùng mới chuyển sang phần tử kế tiếp (có thể không chuyển sang phần tử kế tiếp nếu đã thõa mãn mong muốn của terminal operation).

Tóm lại, quá trình xử lý các Stream một cách lười biếng (lazy) cho phép tránh việc kiểm tra tất cả dữ liệu khi không cần thiết. Cách làm này giúp cho Stream hoạt động rất hiệu quả khi có nhiều intermediate operation và nguồn dữ liệu là lớn.

Bài viết đến đây là hết, sau bài này hy vọng các bạn hiểu rõ hơn về Stream trong Java 8 và vận dụng nó thích hợp để đạt được hiệu quả tốt hơn.

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng các vị trí CNTT hấp dẫn trên TopDev

Định nghĩa request URL với RAML

request url
Định nghĩa request URL với RAML

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Trong bài viết trước, mình đã giới thiệu với các bạn về RAML để định nghĩa API spec. Trong bài viết này, mình sẽ hướng dẫn các bạn chi tiết hơn cách định nghĩa request URL với RAML như thế nào các bạn nhé!

  cURL là gì? Cách sử dụng Curl
  How to Design System like TinyURL – P1

Xem thêm các việc làm Linux hấp dẫn trên TopDev

Để làm ví dụ cho bài viết này, đầu tiên, mình sẽ tạo mới một tập tin .raml với thông tin về API quản lý sinh viên bao gồm thêm, xoá, sửa, tìm kiếm với nội dung ban đầu như sau:

#%RAML 1.0
baseUri: https://localhost:8081/api
title: Student Management System
version: 1.0

Chúng ta sẽ định nghĩa một số request như sau:

  • GET /students để lấy danh sách sinh viên
  • POST /students để thêm mới sinh viên
  • PUT /students/{id} để cập nhập một sinh viên với id được truyền trong request
  • DELETE /students/{id} để xoá một sinh viên với id được truyền trong request
  • GET /students/find-by-name?name=<student-name>
  • GET /students/find-by-ids?id=<id1>,<id2>

Với những request như trên, các bạn có thể định nghĩa một root request “/students” như sau:

/students:

Các bạn cứ hình dung một request URL sẽ chia thành nhiều cấp từ trái sang phải. Ví dụ như request /api/students/find sẽ có 3 cấp là api rồi đến students rồi đến find. Các cấp này ngăn cách bởi dấu “/”. Mỗi cấp sẽ là một level trong tập tin .raml theo thứ tự đó nên trong ví dụ trên của mình, root request cho tất cả các request sẽ là “/students”.

 

Cho root request này, chúng ta có 2 request là GET và POST để lấy thông tin tất cả sinh viên và thêm mới sinh viên, nên các bạn chỉ cần khai báo những request này như sau:

/students:
get:
post:

Các bạn có thể thêm description cho mỗi request nếu muốn, ví dụ như:

/students:
get:
description: Get all students
post:
description: Add new student

Tiếp theo, chúng ta có 2 request với /students/{id} với PUT và DELETE HTTP method, dùng để cập nhập và xoá thông tin của một sinh viên nào đó. Vì chúng ta đã định nghĩa root request với “/students” rồi nên giờ các bạn chỉ cần định nghĩa cho 2 request mới này như sau:

/students:
get:
post:

/{id}:
put:
description: Update information for a student
delete:
description: Delete information of a student

Các bạn có thể định nghĩa thêm thông tin của các path parameter, thông tin về mục đích của path parameter, kiểu dữ liệu là gì, example về giá trị của chúng nữa. Để làm được điều này, chúng ta sẽ dùng section uriParameters. Ví dụ mình có thể định nghĩa thông tin cho path parameter {id} trong ví dụ trên như sau:

/students:
get:
description: Get all students
post:
description: Add new student

/{id}:
uriParameters:
id:
description: Id of the student
type: string
example: "1"
put:
description: Update information for a student
delete:
description: Delete information of a student

Đối với request /students/find-by-name?name=<student-name>, chúng ta cần định nghĩa thêm section /find-by-name với root request là “/students” như sau:

/students:
get:
description: Get all students
post:
description: Add new student

/{id}:
uriParameters:
id:
description: Id of the student
type: string
example: "1"
put:
description: Update information for a student
delete:
description: Delete information of a student

/find-by-name:
get:
description: Find student by name

HTTP method của request này là GET và mình cũng đã thêm description cho request này.

Đối với request /students/find-by-name này, chúng ta có thêm request parameter tên là name. Chúng ta có thể định nghĩa thông tin request parameter này như sau:

/students:
get:
description: Get all students
post:
description: Add new student

/{id}:
uriParameters:
id:
description: Id of the student
type: string
example: "1"
put:
description: Update information for a student
delete:
description: Delete information of a student

/find-by-name:
get:
description: Find student by name
queryParameters:
name: 
description: Name of student
type: string
example: "Khanh"

Như các bạn thấy, mình sử dụng section queryParameters để định thông tin cho tất cả các request parameter của request. Request parameter trong ví dụ này của mình có kiểu dữ liệu String. Các bạn có thể định nghĩa request parameter này có bắt buộc hay không sử dụng thuộc tính “required: false” hoặc sử dụng question mark như sau:

queryParameters:
name?: 
description: Name of student
type: string
example: "Khanh"

Trong ví dụ của mình thì request parameter name là bắt buộc các bạn nhé!

Đối với những request có matrix parameter như /students/find-by-ids?id=<id1>,<id2> thì các bạn có thể định nghĩa matrix parameter đó với type là array. Ví dụ như sau:

/students:
get:
description: Get all students
post:
description: Add new student

/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
put:
description: Update information for a student
delete:
description: Delete information of a student

/find-by-name:
get:
description: Find student by name
queryParameters:
name: 
description: Name of student
type: string
example: "Khanh"
/find-by-ids:
get:
description: Find list of students by a list of Ids
queryParameters:
ids: 
description: List of Ids
type: number[]
example: "1,2,3"

Toàn bộ nội dung của tập tin .raml của ví dụ trong bài viết này như sau:

#%RAML 1.0
baseUri: https://localhost:8081/api
title: Student Management System
version: 1.0

/students:
get:
description: Get all students
post:
description: Add new student

/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
put:
description: Update information for a student
delete:
description: Delete information of a student

/find-by-name:
get:
description: Find student by name
queryParameters:
name: 
description: Name of student
type: string
example: "Khanh"
/find-by-ids:
get:
description: Find list of students by list of Ids
queryParameters:
ids: 
description: List of Ids
type: number[]
example: "1,2,3"

Giới thiệu Castle Mock – Mock REST APIs và SOAP web-services

castle mock
Giới thiệu Castle Mock – Mock REST APIs và SOAP web-services

Bài viết được sự cho phép của tác giả Giang Phan

Trong quá trình phát triển hệ thống hoặc quá trình testing, một trong những vấn đề khó khăn là khi hệ thống của bạn cần tích hợp với một bên thứ ba (3rd party). Do sử dụng API của bên thứ ba nên chúng ta có thể gặp một số vấn đề sau:

  • Bên thứ ba không có hoặc không cung cấp hệ thống test cho chúng ta.
  • Bên thứ 3 đang phát triển API song song với chúng ta hoặc chưa hoàn thành API cho chúng ta sử dụng.
  • Không thể gọi API thật để test: gặp vấn đề về chi phí và bảo mật.
  • Gọi service của bên thứ ba để viết Unit Test rất chậm và đôi khi service của họ die dẫn đến Unit Test của chúng ta failed không mong muốn.
  AspectMock là gì? Tại sao dùng AspectMock với Codeception
  Hướng dẫn tạo mock API Server với Post Man

Xem thêm tuyển dụng Magento hấp dẫn trên TopDev

Để giải quyết vấn đề này, mình cần thu thập API document của họ, và dựng nên con mock service, trả về đúng những trường hợp cần sử dụng trong requirement API document và để team tích hợp.

Trong bài viết này mình sẽ giới thiệu về 1 Mock service rất dễ sử dụng là Castle Mock.

Giới thiệu Castle Mock

Castle Mock là một ứng dụng web cung cấp chức năng để giả lập API RESTful và SOAP web service. Chức năng này cho phép các developer phía client có thể giả lập các behavior và response ở phía server.

Một số tính năng nỗi bật của Castle Mock:

  • SOAP & REST: Có thể Mock cho cả SOAP and REST. Có thể được cấu hình bằng cách import các file định nghĩa service như: WSDL, WADL, Swagger và RAML. Các web service được xác định trong các file cấu hình sẽ được Castle Mock giả lập một cách tự động.
  • Proxy và record responses: Castle Mock có thể được sử dụng như một proxy giữa Client và Server. SOAP và REST có thể được mock hoặc chuyển tiếp đến service thật. Phản hồi (response) từ các yêu cầu được chuyển tiếp có thể được ghi lại tự động và được sử dụng để tạo các phản hồi giả (mocked response).
  • Logging: Tất cả các request gửi đến và response gửi đi sẽ được ghi lại, giúp chúng ta dễ dàng theo dõi cũng như debug khi cân thiết.
  • Easy installation: Castle Mock được xây dựng bằng Java và bản thân ứng dụng được dễ dàng triển khai trên máy chủ Apache Tomcat thông qua WAR file. Ngoài ra, Castle mock cũng dễ dàng được setup và deploy trên Docker.
  • Open source: Castle Mock hoàn toàn miễn phí và là nguồn mở (Giấy phép Apache 2.0). Source code của nó có thể được tìm thấy trên Github.

Download và cài đặt Castle Mock

Yêu cầu cài đặt các phần mềm:

Hướng dẫn cài đặt Java 8

Các bạn xem lại bài viết “Hướng dẫn cài đặt JDK“.

Cài đặt và cấu hình Tomcat Server

Các bạn xem lại bài viết “Triển khai ứng dụng Jersey REST Web service lên Tomcat Server“.

Download và cài đặt Castle Mock

Truy cập link sau để download Castle Mock: https://castlemock.com/use-castle-mock/

Sau khi đã download thành công file .war, chúng ta tiến hành deploy nó lên Apache Tomcat server.

Mặc định Tomcat chỉ cho phép upload tối đa 50MB. Do file castlemock lớn hơn nên mình cần config lại limit size cho phép upload lên tomcat server. Các bạn mở file webapps/manager/WEB-INF/web.xml trong thư mục Tomcat.

Truy cập link sau và deploy castle mock: http://localhost:8090/manager

  • Context Path: /castlemock
  • Chọn file castlemock.war đã download ở trên.

Sau khi đã deploy xong, các bạn truy cập địa chỉ sau: http://localhost:8090/castlemock

Bạn sẽ được yêu cầu nhập username và password truy cập ứng dụng castle mock:

1
2
Username: admin
Password: admin

Sử dụng Castle Mock

Tạo Mock project và tạo các mock REST and SOAP APIs.

REST project

Create a REST project

Trên trang Overview, click “New project” button để bắt đầu tạo một project mới. Nhập tên project, description và chọn chọn loại project là REST.

Tiếp theo, chọn project đã tạo:

Upload API specifications

Mock service có thể được tạo bằng nhiều cách. Sau khi đã tạo project, chúng ta có thể tạo các mock service thủ công hoặc upload file đặc tả API (API specification) nếu đã được định nghĩa từ trước.

Castle Mock hỗ trợ các loại API specification: Swagger, RAML, WADL. Để đơn giản trong bài này, tôi sử dụng Swagger.

Giả sử chúng ta cần tạo mock các API của Swagger sau: http://petstore.swagger.io/ . File mô tả các API này được định nghĩa trong file swagger.json tại link https://petstore.swagger.io/v2/swagger.json

Click button Upload và chọn Swagger:

Chúng ta có 2 lựa chọn: nhập link đến file đặc tả API hoặc upload một file mới. Sau đó check chọn “Generate response for each operation” để swagger tự tạo response cho mỗi API.

Click button Link Swagger hoặc Upload để tạo mock service.

Click lên tên Application:

Create mocked responses

Click tên Resource “/user” -> tên Method “createUser”, chúng ta có kết quả sau:

Sau khi import và check “Generate response …”, Castle Mock giúp chúng ta tạo 1 response như trên.

Một Method có thể có nhiều Response, mỗi Response có Name, HTTP status code, Header và resposne message.

Chúng ta có thể tạo một response mới bằng cách click vào button Create Resposne như sau:

Chúng ta có 2 response:

Update method – Response strategy

Chúng ta có thể tạo nhiều response, như vậy Castle Mock lựa chọn response nào để trả kết quả về cho request? Đó chính là cấu hình response strategy.

Castle Mock hỗ trợ các loại response strategy:

  • Random (ngẫu nhiên): chiến lược này lựa chọn một mock response ngẫu nhiên.
  • Sequence (tuần tự): chiến lược này lựa chọn một mock response theo trình tự. Trình tự được xác định theo thứ tự các mock response được tạo. Trình tự sẽ bắt đầu lại từ đầu khi nó đã đến response cuối cùng.
  • Ngoài ra còn có: Query match, XPath match, JSON Path match, Header Query match.

Để cấu hình Response Strategy, click button “Update method“:

Update method – Method type

Castle Mock hỗ trợ tất cả Method type: GET, PUT, POST, DELETE, …

Update method – Method status

Một mock api có một mode. Mode sẽ xác định cách mock web service sẽ hoạt động như thế nào. Các mode được Castle mock hỗ trợ:

  • Mocked : mode này sẽ sử dụng các mock response đã định nghĩa.
  • Disabled : mode này làm service bị disable. Phía end user sẽ nhận được thông báo lỗi nếu cố gắng gọi một ws bị vô hiệu hóa.
  • Forward requests: mode này sẽ chuyển tiếp sẽ chuyển tiếp tất cả các request đến điểm cuối bên ngoài (service thật). Mode này có thể được sử dụng khi chỉ một số ws bị mock, trong khi phần còn lại sẽ gọi đến ws thật.
  • Record responses : mode này sẽ chuyển tiếp tất cả các request đến điểm cuối bên ngoài và tạo phản hồi giả (mock response) cho các response được trả về từ điểm cuối bên ngoài.
  • Recording once : mode này sẽ thực hiện giống như mode Record response, nhưng sẽ chỉ thực hiện một lần. Khi đã xong, ws sẽ chuyển sang chế độ Mocked thay thế.
  • Echo : mode này sẽ trả về request như là một response.

Update method – Simulate netword deplay

Các request trên internet có độ trễ (deplay) là không tránh khỏi, chúng ta có thể dễ dàng mô phỏng độ trễ phản hồi response một cách dễ dàng thông qua option này.

Test các Mock REST API được tạo bởi Castle Mock với Postman

Để test Mock REST API được tạo bởi Castle Mock, các bạn có thể click lên Resource và lấy thông tin resource như sau:

Mở Postman và gọi đến REST API trên, chúng ta sẽ lần lượt nhận được response status 200 và 400.

SOAP project

Create a SOAP project

Tương tự như tạo REST Project, các bạn chọn loại project là SOAP.

Import WSDL file

Mock SOAP web service được tạo bằng cách import file WSDL – file mô tả SOAP web service. Trên trang SOAP project, click “Upload WSDL” button để upload một file WSDL hoặc trỏ đến URL của file WSDL.

Để đơn giản tôi sẽ sử dụng API Calculator từ http://www.dneonline.com/calculator.asmx với link WSDL như sau: http://www.dneonline.com/calculator.asmx?WSDL

Sau khi Link WSDL:

Chúng ta có thể xem chi tiết API đã import với các response được tạo:

Create mocked responses

Tương tự như REST, chúng ta có thể tạo nhiều Mock response nếu cần.

Test các Mock SOAP API được tạo bởi Castle Mock với SOAP UI

Để test Mock SOAP API được tạo bởi Castle Mock, các bạn có thể click lên tên Project và lấy thông tin URL của file WSDL như sau:

Mở SOAP UI, và import file mock WSDL trên để test.

Bài viết đến đây là hết. Chúc các bạn thành công trong việc sử dụng hiệu quả Mock service :).

Ah, ngoài ra còn một công cụ khác cũng có tính năng tương tự là mountebank. Công cụ này được xây dựng trên nền node.js, các bạn có thể tìm hiểu thêm document trên trang chủ của mountebank nếu thích.

Tài liệu tham khảo:

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm tuyển dụng các vị trí IT hấp dẫn trên TopDev

Offline token với Keycloak

offline token
Offline token với Keycloak

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Offline access là một tính năng của OpenID Connect được định nghĩa tại https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess. Nó giúp cho application với offline token (một loại của refresh token) có thể lấy access token và sử dụng resource mà không cần user phải đăng nhập trong thời gian dài hoặc mãi mãi. Keycloak hỗ trợ chúng ta làm việc với offline access bằng cách sử dụng offline token. Cụ thể như thế nào? Chúng ta hãy cùng nhau tìm hiểu trong bài viết này các bạn nhé!

  Authorization Code grant type với Proof Key for Code Exchange (PKCE) trong OAuth 2.1
  C Token là gì? Cú pháp trong lập trình C/C++

Xem thêm nhiều việc làm NextJS lương cao trên TopDev

Để làm ví dụ, mình sẽ tạo mới một client với grant type Authorization Code trong Keycloak như sau:

 

Điều đầu tiên các bạn cần phải biết là làm thế nào chúng ta lấy được offline token.

Rất đơn giản, trong request lấy authorization code cho client, chúng ta cần truyền thêm scope là offline_access là được:

 

Keycloak sẽ hỏi chúng ta có cho phép Client Application Offline Access hay không?

Nếu các bạn đồng ý thì sau khi có authorization code, request lấy access token cho client này:

 

parse nội dung của refresh token, các bạn sẽ thấy nội dung payload như sau:

Type của token này là Offline thay vì Refresh và các bạn có thể thấy, không có claim “exp” với expiration time như khi chúng ta parse access token:

 

Chúng ta có thể sử dụng offline token này để lấy access token mới tương tự như refresh token.

Ví dụ như sau:

Điểm khác biệt giữa offline token và refresh token là không có expiration time cho offline token.

 

Mặc định thì offline token là nó sẽ luôn valid dù user không đăng nhập hoặc server bị restart, chỉ trừ khi nó bị revoke. Tuy nhiên, các bạn cần sử dụng offline token này ít nhất là một lần trong khoảng thời gian mặc định là 30 ngày, kể từ lần sử dụng gần nhất. Đó là bởi vì, một offline token sẽ associate với một offline session. Và offline session này chỉ có thời gian chờ (idle time) mặc định là 30 ngày như mình nói ở trên. Ở Realm level thì giá trị 30 ngày này được cấu hình trong tab Tokens của Realm Settings, field Offline Session Idle:

Ở tab Tokens này, như các bạn thấy, chúng ta còn có 1 field khác liên quan đến offline session nữa, tên là Offline Session Max Limited. Ý nghĩa của field này là nếu các bạn enable, offline token sẽ bị expired trong thời gian mặc định là 60 ngày, dù chúng ta có sử dụng offline token này bao nhiêu lần đi chẳng nữa:

Các bạn có thể thay đổi các cấu hình liên quan đến offline session ở Realm level nếu muốn, các client sẽ kế thừa cấu hình này. Còn nếu muốn cấu hình cụ thể cho một client nào đó thì hãy expand phần Advance Settings trong mỗi client:

chúng ta có thể thay đổi cấu hình của Client Offline Session Idle và Client Offline Session Max ở đây.

Các bạn có thể xem tất cả các offline token của một client bằng cách vào tab Offline Access của client đó

Tối Ưu Hóa Quy Trình Kiểm Soát Chất Lượng Với 7 Công Cụ QC

7 công cụ qc
Tối Ưu Hóa Quy Trình Kiểm Soát Chất Lượng Với 7 Công Cụ QC

7 công cụ QC đã và đang trở nên quen thuộc với những nhân viên đảm nhận ở vị trí Quality Control trong mỗi nhà máy sản xuất. Và dĩ nhiên chúng là cánh tay đắc lực giúp doanh nghiệp tiết kiệm chi phí vận hành khi đề ra mục tiêu chinh phục khách hàng. Ngày nay có không ít doanh nghiệp đã và đang ứng dụng 7 công cụ QC. Đây được xem là bài toán thông minh  giúp các doanh nghiệp quy mô vừa – nhỏ, thậm chí là các tập đoàn đa quốc gia giải quyết tối ưu những vấn đề về quản trị chi phí vận hành sản phẩm, dịch vụ. 

7 công cụ qc
7 công cụ qc

Công cụ quản lý chất lượng là gì?

QC (Quality Control) hay còn gọi là kiểm soát chất lượng là quy trình quan trọng để kiểm tra, đánh giá và vận hành thử trước khi một sản phẩm/ dịch vụ nào đó chạm tới tay người tiêu dùng. Liệu doanh nghiệp có thể tối ưu hóa hoạt động kinh doanh nhờ sử dụng công cụ kiểm soát chất lượng? 

Hiện nay, hoạt động kiểm soát chất lượng được ứng dụng trong nhiều lĩnh vực kinh tế khác nhau, từ quản lý công nghiệp đến kinh doanh thương mại. Bất kể doanh nghiệp quy mô vừa – nhỏ hay đến các tập đoàn lớn đều cho rằng quản lý chất lượng là một trong những khâu yếu tố quyết định việc sống còn của doanh nghiệp.

  Dù là nhân viên hay ông chủ, bạn cũng cần phải nắm 4 nguyên tắc này để nâng cao chất lượng công việc
  5 tips cải tiến chất lượng phát triển Mobile App

7 công cụ QC phổ biến được các doanh nghiệp đánh giá cao hiện nay

 Hiện nay, các công ty sử dụng tới 7 công cụ QC khác nhau để theo dõi, phân tích và đánh giá chất lượng của sản phẩm hoặc dịch vụ.

1. Lưu đồ (Flowchart)

Việc ai là cha đẻ phát minh ra lưu đồ vẫn là một ẩn số. Song ngày nay lưu đồ được áp dụng rộng rãi vì nó tóm tắt được các bước của một quy trình, có liên quan chặt chẽ với nhau để chỉ ra hoạt động hiệu quả trong hoạt động thực tế. Việc sử dụng công cụ QC này sẽ giúp nhân viên vận hành sản phẩm theo một quy trình cụ thể, rõ ràng và khoa học hơn. 

Lưu đồ dùng để kiểm soát quy trình hỗ trợ khách hàng

2. Biểu đồ mật độ phân bố (Histogram)

Về tổng thể thì đây là biểu đồ dạng cột đơn giản thể hiện tần suất của sản phẩm/dịch vụ xuất hiện ở thị trường nào đó. Qua đó nhân viên QC có thể đánh giá tình hình phân bố của sản phẩm/ dịch vụ. Và trên cơ sở này để có những can thiệp kịp thời để đảm bảo tình hình kinh doanh, sản xuất nhờ sử dụng công cụ QC này. 

Biểu đồ mật độ tần suất số lần phản ứng

3. Biểu đồ Pareto (Pareto Chart)

Là biểu đồ kết hợp đơn giản trong đó các đối tượng được sắp xếp từ tần số cao nhất đến tần số thấp nhất. Pareto cho phép các nhà phân tích kinh tế quan sát giá trị tuyệt đối đại diện cho nguyên nhân của sai số trong cột. Đồng thời, phần trăm lỗi tích lũy cũng được hiển thị dưới dạng một dòng.

4. Phiếu kiểm tra (Check sheet)

Việc thu thập, ghi chép dữ liệu từ phiếu kiểm tra đóng vai trò quan trọng trong việc đánh giá, đưa ra hướng giải quyết thích hợp. Phiếu kiểm tra được sử dụng linh hoạt từ việc điều tra nguyên nhân sản phẩm bị trả lại, kiểm tra, tìm nguyên nhân gây ra lỗi cho đến kiểm tra xác nhận trước khi trưng cầu ý kiến khách hàng. 

Xem thêm các việc làm QC lương cao trên TopDev

5. Biểu đồ quan hệ nhân quả (Cause & Effect Diagram)

Khi có sự cố phát sinh về chất lượng thì việc áp dụng biểu đồ quan hệ nhân quả sẽ giúp nhân viên QC tìm ra được nguyên nhân cội nguồn nằm ở đâu.  Trên thực tế, một vấn đề có thể xuất phát từ nhiều nguyên nhân khác nhau và ở chúng có sự phân cấp với nhau. Ví dụ như nguyên nhân thứ cấp gián tiếp xuất phát từ nguyên nhân thứ cấp trực tiếp, và tất cả đều xuất phát từ nguyên nhân gốc. Việc áp dụng biểu đồ quan hệ nhân quả sẽ giúp nhân viên QC dễ điều tra được sự cố của sản phẩm đó bắt nguồn từ đâu và đưa ra hướng giải quyết kịp thời. 

6. Biểu đồ phân tán (Scatter Diagram)

Mối tương quan giữa nguyên nhân và kết quả hoặc chất lượng bị ảnh hưởng bởi các yếu tố được thể hiện bằng biểu đồ này. Có nhiều yếu tố tác động qua lại và có tính kết hợp ảnh hưởng đến chất lượng của sản phẩm hay dịch vụ. Biểu đồ này có thể mô tả tình hình chất lượng bằng hai hay nhiều đối tượng cùng một lúc. 

7. Biểu đồ kiểm soát (Control Charts)

Biểu đồ kiểm soát được phát triển từ biểu đồ chạy (R – Chart), nhưng được thêm phần giới hạn kiểm soát trên và kiểm soát dưới. Cộng thêm công thức tính năng lực quá trình (Cp, Cpk) giúp nhân viên QC dễ dàng phân tích quan sát quá trình đó có nằm trong thông số có thể chấp nhận được hay không. 

Tìm việc làm QA QC lương cao trên TopDev

Tầm quan trọng của việc ứng dụng công cụ quản lý chất lượng trong sản xuất kinh doanh

Bất kỳ một công ty sản xuất muốn đưa sản phẩm/ dịch vụ của mình tiếp cận người dùng ở phân khúc thị trường nào đó sẽ không thể bỏ qua khâu kiểm soát chất lượng. Hay nói cách khác đầu tư vào đội ngũ QC (Quality Control) luôn đóng vai trò quan trọng giúp công ty  đó cho ra đời những sản phẩm hoàn hảo nhất đến khách hàng của mình. 

Thông thường đội QC sẽ đảm nhận vai trò khi làm việc trực tiếp tại nhà máy, phân xưởng và giám sát chặt chẽ từng công đoạn để đảm bảo rủi ro thấp nhất cho sản phẩm/ dịch vụ nào đó. Ngoài ra nhân viên QC sẽ bắt đầu từ khâu nhập nguyên liệu cho đến khi sản xuất ra sản phẩm đến tay người tiêu dùng cuối cùng. Công việc của nhân viên QC trong doanh nghiệp được thực hiện thường xuyên và liên tục, đòi hỏi họ phải chịu được nhiều áp lực. 

Trên đây là những chia sẻ về 7 công cụ QC vốn quen thuộc trong doanh nghiệp sản xuất. Nếu bạn đang tìm giải pháp để giúp quy trình kiểm soát được diễn ra tiết kiệm và khoa học, hãy tìm hiểu những công cụ QC được nêu trên để công việc trở nên dễ dàng hơn. 

Intern: Hồ Ngọc Bích

Có thể bạn quan tâm:

Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

Hướng dẫn cài đặt và cấu hình Nginx Amplify Agent trên Centos 7

nginx amplify agent
Hướng dẫn cài đặt và cấu hình Nginx Amplify Agent trên Centos 7

Bài viết được sự cho phép của tác giả Lê Chí Dũng

1. Chuẩn bị cài đặt Nginx Amplify Agent:

– Nginx chỉ hoạt động trên server chạy hệ điều hành Linux:

  • Ubuntu 12.04, 14.04, 16.04
  • Debian 7, 8
  • CentOS 6, 7
  • Red Hat 6, 7 (and systems based on it, e.g. Oracle Server)
  • Amazon Linux (latest release)
  • Gentoo Linux (experimental Ebuild)

– Hệ thống đã cài đặt Nginx từ version 1.10 trở lên

– NGINX Amplify Agent chỉ hoạt động  Python 2.6 và 2.7. Python 3 thì không hoạt động.

  App User Centricity: Làm sao tăng tỷ lệ duy trì lên 66%?
  Cài đặt ConfigServer Security and Firewall (CSF) và Webmin trên CentOS 7

Xem thêm các việc làm Android lương cao trên TopDev

2. Cài đặt và cấu hình Nginx Amplify Agent

1. Cài đặt

yum info nginx-amplify-agent

2. Cập nhật

yum update nginx-amplify-agent

3. Start service

systemctl start amplify-agent
# Restart service
systemctl restart amplify-agen

4. Gỡ cài đặt

yum remove nginx-amplify-agent

5. Kích hoat với API key

Bạn lấy mã cài đặt kích hoạt trên UI web Nginx Amplify Agent

Sau đó chạy đoạn code đó

# curl -sS -L -O \
https://github.com/nginxinc/nginx-amplify-agent/raw/master/packages/install.sh && \
API_KEY='ffeedd0102030405060708090a0b0c' sh ./install.sh

6. Kiểm tra Amplify Agent đã started

ps ax | grep -i 'amplify\-'

Cuối  cùng enjoy nhé! Cài đặt vô cùng đơn giản đúng không 😀

Bài viết gốc được đăng tải tại lcdung.top

Có thể bạn quan tâm:

Xem thêm tuyển dụng các vị trí CNTT hấp dẫn trên TopDev

Lấy access token sử dụng refresh token với Keycloak

access token
Lấy access token sử dụng refresh token với Keycloak

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Mình đã giới thiệu về Refresh Token grant type trong OAuth 2.0 trong bài viết về Các loại grant types trong OAuth 2.0. Trong bài viết này, mình sẽ hướng dẫn các bạn cách chúng ta lấy mới access token sử dụng refresh token với Keycloak là như thế nào các bạn nhé!

  Authorization Code grant type với Proof Key for Code Exchange (PKCE) trong OAuth 2.1
  Thêm mới client hỗ trợ OAuth Authorization Code grant type trong Keycloak

Đầu tiên, mình sẽ tạo mới trong Keycloak một client với Authorization Code grant type để làm ví dụ:

Thực hiện lấy access token cho client này, các bạn sẽ thấy kết quả như sau:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJZR29TSXBPblRKeTJmbjhzLWk4WlBKMC1xWkxybEEtNVI4ZjhXbTFjYjVVIn0.eyJleHAiOjE2MzU2NDMxMDYsImlhdCI6MTYzNTY0MjgwNiwiYXV0aF90aW1lIjoxNjM1NjQyNzk0LCJqdGkiOiJjZTY4ZDIxYS04YzdkLTQ1YWItOWMxNC1jYzdkMTM5NWNmOGMiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvaHVvbmdkYW5qYXZhIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjhhNTA5MmZhLWQ4MTUtNDYyMC05MmFiLTRhYzg3NTQyNjg1YyIsInR5cCI6IkJlYXJlciIsImF6cCI6Imh1b25nZGFuamF2YV9hdXRob3JpemF0aW9uX2NvZGUiLCJub25jZSI6IjllZW1meGhiazYiLCJzZXNzaW9uX3N0YXRlIjoiN2E4ZGMxODktMTVlZS00ZWZhLWEwZGEtNmY5ZjdlY2Y2MjczIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiZGVmYXVsdC1yb2xlcy1odW9uZ2RhbmphdmEiXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiS2hhbmggTmd1eWVuIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiaHVvbmdkYW5qYXZhIiwibG9jYWxlIjoiZW4iLCJnaXZlbl9uYW1lIjoiS2hhbmgiLCJmYW1pbHlfbmFtZSI6Ik5ndXllbiIsImVtYWlsIjoiaHVvbmdkYW5qYXZhLmNvbUBnbWFpbC5jb20ifQ.JgklBAraEs__i2CHI0z7KQZBy5JeIVfBPzP4cORxNEcI53zbGEmsCqHZoNt0qwcURGc9EV77jzOnqlq6j7wWPhtwhpzOHT3JupqGD86CyaUJ9rh-GrhnKDy7YMTp9337X7J2qzp5YIIK0tmroGjNR7dIpHn1ThatNLM-8w2KnydT7DhYjHx1gaX7HKXAuaiBhNbR7PYrS1-FsbxT--QjNNs-mgTXl7OVnqpCPcRCt8pBPu15kPbP00i7zJ7AaSmZfPNBt7rFPGkaYAX5kIi8FY0wf2RZuFMcgDpg-01rzfTkoWIXCdDSQPBagAveQUE6viaC-nsV7_-5zTCvGch_0A",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5ZjYzNWQ5Mi1hOTdjLTQwMjktODE2YS05ZWU3YjAxMmE3NDUifQ.eyJleHAiOjE2MzU2NDQ2MDYsImlhdCI6MTYzNTY0MjgwNiwianRpIjoiYTZmYmQwMDUtNGZlOC00NTdjLWEwZmEtMDA5ODBiZjY4NmE0IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2h1b25nZGFuamF2YSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9odW9uZ2RhbmphdmEiLCJzdWIiOiI4YTUwOTJmYS1kODE1LTQ2MjAtOTJhYi00YWM4NzU0MjY4NWMiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiaHVvbmdkYW5qYXZhX2F1dGhvcml6YXRpb25fY29kZSIsIm5vbmNlIjoiOWVlbWZ4aGJrNiIsInNlc3Npb25fc3RhdGUiOiI3YThkYzE4OS0xNWVlLTRlZmEtYTBkYS02ZjlmN2VjZjYyNzMiLCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIn0.zMYO5ZRupTskEs9QxJdnP3d24qcHavrLKG4pkQ-OLN8",
"token_type": "Bearer",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJZR29TSXBPblRKeTJmbjhzLWk4WlBKMC1xWkxybEEtNVI4ZjhXbTFjYjVVIn0.eyJleHAiOjE2MzU2NDMxMDYsImlhdCI6MTYzNTY0MjgwNiwiYXV0aF90aW1lIjoxNjM1NjQyNzk0LCJqdGkiOiI3NGFkNGMyNC1mYTliLTQ4MWMtYTgwZS1jMTVhNDk1NmU0ZDciLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvaHVvbmdkYW5qYXZhIiwiYXVkIjoiaHVvbmdkYW5qYXZhX2F1dGhvcml6YXRpb25fY29kZSIsInN1YiI6IjhhNTA5MmZhLWQ4MTUtNDYyMC05MmFiLTRhYzg3NTQyNjg1YyIsInR5cCI6IklEIiwiYXpwIjoiaHVvbmdkYW5qYXZhX2F1dGhvcml6YXRpb25fY29kZSIsIm5vbmNlIjoiOWVlbWZ4aGJrNiIsInNlc3Npb25fc3RhdGUiOiI3YThkYzE4OS0xNWVlLTRlZmEtYTBkYS02ZjlmN2VjZjYyNzMiLCJhdF9oYXNoIjoiRWpfZGlIMURTa0lINzlsMGpqMENsQSIsImFjciI6IjEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJLaGFuaCBOZ3V5ZW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJodW9uZ2RhbmphdmEiLCJsb2NhbGUiOiJlbiIsImdpdmVuX25hbWUiOiJLaGFuaCIsImZhbWlseV9uYW1lIjoiTmd1eWVuIiwiZW1haWwiOiJodW9uZ2RhbmphdmEuY29tQGdtYWlsLmNvbSJ9.Cm9kcio4DYM8f3WN_KQ5UJIe-RX8rRLKP0wlLJqwObE0tIxXAEqVOzr7gAv9WEpy_wDO3mC7NN4ZyfX8iQlm47UCdnFo-50vbeUDdfa1wtSM2FfnPOhV_76KwVDgD1KqImlHGdb0QbvzxAMS25RPrWeYWQsoJlVFqcOC52_sqQ217vJyuytComZ7BJ_otiKLSiUTmAAgmhc3UAiXjKseefzv858fn8b0Z-tVEJh-CfjwhnazRsxj5ukGYknX56wJHNGT35JU_Wph7rzv9EsRNm36_0Vt-7LyLBKrwWWV55MoPL_il7nzxUrehhfSzAzBzOSMhYqCuSfAAErKUKJGHw",
"not-before-policy": 0,
"session_state": "7a8dc189-15ee-4efa-a0da-6f9f7ecf6273",
"scope": "openid email profile"
}

Nội dung của refresh token nếu mình decode nó sử dụng https://jwt.io/ cơ bản như sau:

Các bạn để ý đến claim “typ” trong nội dung của refresh token trên nhé! Ở đây giá trị của claim này là Refresh, chúng ta còn có một số type khác của refresh token nữa, chẳng hạn như Offline,…

Refresh token sẽ luôn được trả về cùng với access token, giúp chúng ta có thể lấy access token mới mà không cần user phải đăng nhập trở lại.

Để lấy access token mới với refresh token thì trong request để lấy access token, các bạn chỉ cần truyền grant_type=refresh_token, giá trị của refresh token mà chúng ta có trong request lấy access token trước, client ID và client secret.

Với ví dụ này của mình, các bạn có thể lấy access token mới bằng cách request như sau:

Như các bạn thấy, với Refresh Token grant type, chúng ta không cần user phải đăng nhập nữa.

Một refresh token sẽ luôn có expiration time, mặc định của Keycloak là 30 phút các bạn nhé! Mỗi khi một access token mới được issue thì refresh token sẽ được issue lại, và các bạn có thể sử dụng cái mới nhất để expiration time được lâu hơn.

Chúng ta có thể sử dụng một refresh token để request access token nhiều lần. Tuy nhiên, các bạn có thể giới hạn số lần mà một refresh token có thể được sử dụng, bằng cách vào tab Tokens của Realm Settings, cấu hình field Revoke Refresh Token. Nếu các bạn turn on field này, thì có thể cấu hình số lần một refresh token được sử dụng lại:

Mặc định các bạn chỉ có thể sử dụng refresh token một lần thôi các bạn nhé!

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

3D Artist Là Gì? Các Mức Lương Hấp Dẫn Trong Nghề 3D Artist

nghề 3d artist
3D Artist là gì? Các mức lương hấp dẫn trong nghề 3D Artist

Nghề 3D Artist cũng được xem là họ hàng chung của “Art” trong giới mỹ thuật nhưng khác với “Art”, người nghệ sĩ có thể diễn đạt, trình bày những tác phẩm của họ ở bất cứ đâu thì “3D Artist” chỉ có thể được những người nghệ sĩ thể hiện trên các hình ảnh máy tính, được tạo nên nhờ những công cụ phần mềm hỗ trợ. Có thể nói những tác phẩm của một dân trong “Nghề 3D Artist” được tạo nên từ những dòng thuật toán trên nền tảng dữ liệu.

nghề 3d artist

 Nghề 3D Artist là gì?

3D Artist là một nhà thiết kế đồ họa chuyên biệt, họ là những người thông thạo công nghệ máy tính và phần mềm thiết kế mới nhất. Với một 3D Artist, trách nhiệm và nghĩa vụ của bạn bao gồm thiết kế và tạo hoạt ảnh 3D cho các ngành khác nhau bao gồm ứng dụng di động, y học, khoa học, trò chơi điện tử và tiếp thị. 

Một 3D Artist sử dụng năng khiếu nghệ thuật và sự sáng tạo cùng với chuyên môn kỹ thuật của mình để đáp ứng nhu cầu của mọi khách hàng dù cho nó có thật sự oái oăm đến mức nào. Có cả một nền tảng nghệ thuật về nhiếp ảnh hoặc vẽ cũng như các kỹ năng máy tính đặc biệt có thể rất hữu ích trong sự nghiệp này.

Các công cụ hỗ trợ cho dân 3D Artist

 Để dân 3D Artist tạo ra được những tác phẩm thì họ cần có những công cụ hỗ trợ, nên ở đây chúng ta sẽ nói đến Blender Blugin. Blender là một trong các công cụ phổ biến trong Ngành 3D Artist.

Blender Plugin là gì?

Blender được biết là một công cụ phần mềm 3D được sử dụng rất phổ biến, rộng rãi vì rất nhiều tính năng hữu ích với tính chất hỗ trợ mã nguồn miễn phí. Có một điều đặc biệt là phần mềm Blender ngày càng có thêm nhiều Plugin được hỗ trợ có thể tích hợp với nó.

Trong bài viết này, sẽ được nói đến các Plugin Blender có tầm quan trọng và hữu ích có thể giúp một dân 3D Artist tạo ra được nhiều tác phẩm đắt giá hơn. Trong đó, ngoại trừ các Plugin buộc người dùng phải trả phí thì Blender cùng có hỗ trợ một số các Plugin miễn phí giúp dân 3D Artist mới vào nghề hay điều kiện kinh tế chưa ổn có thể trãi nghiệm và sử dụng. 

Các Blender Plugin dành cho dân 3D Artist

Dưới đây, sẽ là 10 loại Plugin hữu ích, phổ biến hàng đầu được sử dụng trong Blender:

  1. Amaranth
  2. RetopFlow
  3. TexTools
  4. Speed ​​Sculpt 
  5. Edge Flow
  6. BoxCutter
  7. HardOps
  8. Boolean
  9. MESHmachine
  10. Gaffer
  11. Blam

3d artist

Một số mức lương của dân trong nghề 3D Artist hiện nay

Trong giới 3D Artist mức lương sẽ tuỳ thuộc vào từng yếu tố như độ tuổi, vị trí như Animation Director, Producer, Graphic Designer,…  , và dưới đây sẽ là những mức lương trung bình hằng tháng được khảo sát chung trên thế giới để một dân 3D Artist mới dấn thân vào hoặc đã đi làm tham khảo. Dưới đây là một số ngành nghề tiêu biểu của nghề 3D Artist tại thị trường Việt Nam.

Thu nhập của một 3D Artist vị trí Animation Director (đạo diễn hình ảnh)

Đạo diễn hoạt hình làm việc trong nhiều lĩnh vực bao gồm: xưởng phim, xưởng sản xuất truyền hình, công ty game và công ty quảng cáo… Mức lương hàng năm của đạo diễn hình ảnh rơi vào khoảng 71,350 USD. Trong khi đó, 10% nhà quay phim kiếm được dưới 32.080 USD/năm và 10% khác kiếm được hơn 187.200 USD  vào năm. Các đạo diễn độc lập cũng có thu nhập cao nhất trong ngành giải trí, với New York là 112.060 USD/năm.

Xem thêm các công việc tuyển dụng 3D Artist lương cao trên TopDev

Thu nhập của một 3D Artist vị trí Producer (nhà sản xuất)

Hầu hết những người trong nghề, cụ thể là một dân 3D Artist đều cho rằng công việc của một giám đốc sản xuất là không thể thiếu trong bất kỳ nhà máy, công ty nào. Giám đốc sản xuất trực tiếp tham gia vào việc lập kế hoạch, kiểm soát và giám sát quá trình sản xuất để đảm bảo giao  sản phẩm kịp thời, đúng yêu cầu về số lượng theo tiêu chuẩn chất lượng đề ra trong kế hoạch. Mức lương quản lý sản xuất nhìn chung dao động từ 615.38 USD đến 879.11 USD/tháng, tùy thuộc vào kỹ năng, kinh nghiệm của ứng viên và quy mô công ty.

Thu nhập của một 3D Artist vị trí Graphic Designer (Thiết kế đồ hoạ)

Thiết kế đồ họa là một trong những ngành nghề không ngừng phát triển, đặc biệt là trong thời đại công nghệ 4.0 hiện nay. Điều này  kéo theo hu cầu nhân lực của nghề 3D Artist không ngừng tăng lên.Nhân viên thiết kế đồ họa là những chuyên gia dùng các phần mềm thiết kế đồ họa để trực quan hóa ý tưởng từ văn bản, nội dung thành hình ảnh, logo, catalogue tạp chí, quảng cáo, … Mức lương trung bình của một nhà thiết kế đồ họa tại Việt Nam dao động vào khoảng từ 351.64 USD đến 879.11 USD// tháng.  Ngoài ra, mức lương thiết kế đồ họa phụ thuộc vào số năm kinh nghiệm.

Ngoài câc vị trí công việc trên thì nghề 3D Artist vẫn còn những vị trí công việc khác như Series Director (đạo diễn series phim), Storyboarder (họa sĩ vẽ storyboard),  Art Director (giám đốc nghệ thuật),…

Những nội dung trên chỉ mới là một phần của cả một giới 3D Artist, là một dân 3D Artist sẽ cần phải biết và tìm tòi thêm rất nhiều thứ. Một 3D Artist cần phải trãi qua quá trình học tập từng bước, từng chi tiết để trãi nghiệm những gì đã học liên quan trong nghề 3D Artist. 

Intern: Trương Thị Hồng Anh

Có thể bạn quan tâm:

Xem thêm Việc làm CNTT hấp dẫn trên TopDev

Format (Làm đẹp) code php trong VS Code

format code php

Bài viết được sự cho phép của tác giả Phạm Bình

Chào các bạn,

Đây là bài viết tiếp theo của bài Code PHP chuẩn convention với PHP CodeSniffer. Trong bài này, mình sẽ hướng dẫn các bạn auto check lỗi và format code PHP trong Vs Code – một trong những code editor được yêu thích nhất hiện nay.

Lưu ý, để hiểu bài viết này hơn, thì bạn cần đảm bảo các yêu cầu sau:

  • Máy tính đã cài composer ở dạng global.
  • Máy tính đã cài PHP CodeSniffer ở dạng global.
  • Bạn hiểu ý nghĩa của 2 công cụ phpcs và phpcbf.
  • Bạn hiểu ý nghĩa của file cấu hình phpcs.xml trong dự án.

Nếu không, hãy đọc phần trước của bài viết này, tại đó mình có cung cấp các nội dung hữu ích về các yêu cầu trên.

  10 Frameworks tốt nhất hiện nay cho PHP
  4 lý do để VS Code là Text Editor ưa thích của mọi lập trình viên

I. GIỚI THIỆU VỀ TÍNH NĂNG FORMAT CODE TRONG VS CODE

Giống như nhiều editor khác, VS Code có tính năng tự động format code của bạn cho chuẩn hơn.

Hình ảnh mượn tạm từ một nguồn khác phambinh.net 

Phím tắt để format code trên VS Code tương ứng với từng hệ điều hành như sau:

  1. Trên Windows: Shift + Alt + F.
  2. Trên Mac: Shift + Option + F.
  3. Trên Linux: Ctrl + Shift + I.

Tuy nhiên, mặc định thì VS Code không tích hợp sẵn việc format code PHP, mà chúng ta phải cài thêm từ extension bên ngoài, và mình sẽ hướng dẫn các bạn cài ở ngay mục phía dưới đây.

II. FORMAT CODE PHP TRONG VS CODE VỚI PHP SNIFFER & BEAUTIFIER

Mình đã dùng thử khá nhiều extension format code PHP, thì thấy có thằng PHP Sniffer & Beautifier này là dễ dùng và dễ tùy biến nhất. Để cài đặt extension này, các bạn vào mục Extensions của VS Code, tìm kiếm với từ khóa là “Sniffer & Beautifier” sẽ ra.

Cách tìm và cài đặt extension trong VS Code

Chúng ta tiến hành cài đặt bình thường như các extension khác, và sau khi cài đặt xong, thì hãy đi đến phần cài đặt riêng của extension này như hình dưới.

Đi đến trang cài đặt riêng cho extension

Tại trang cài đặt riêng của extension Sniffer & Beautifier, có 3 thông tin setting quan trọng là Allowed Auto RulesetsExecutable Path CBF và Executable Path CS. Mình sẽ hướng dẫn các bạn các cấu hình các thông tin này sao cho phù hợp ngay sau đây.

Các setting quan trọng

2.1 Cấu hình thông tin Allowed Auto Rulesets

Tại mục (1) như trong hình, hãy bấm vào Edit in settings.json, và đảm bảo ở phpsab.allowedAutoRulesets được cấu hình như sau:

Để dễ copy, thì mình viết các value ra đây nhé:

1
2
3
4
5
6
7
8
9
10
11
12
{
    // ...
    "phpsab.allowedAutoRulesets": [
        ".phpcs.xml",
        ".phpcs.xml.dist",
        "phpcs.xml",
        "phpcs.xml.dist",   
        "phpcs.ruleset.xml",
        "ruleset.xml"
    ],
    // ...
}

2.2 Cấu hình 2 thông tin Executable Path CBF và Executable Path CS

Tại mục (2) như trong hình, chúng ta cần điền đường dẫn tới 2 công cụ là phpcbf và phpcs. Mình sẽ hướng dẫn các bạn cách lấy thông qua mấy bước đơn giản dưới đây.

Chạy lệnh sau để lấy ra đường dẫn tới thư mục home của composer:

composer config --list --global | grep "home"

Kết quả sẽ có dạng như sau, và phần bôi đậm chính là đường dẫn bạn cần quan tâm.

Lưu ý: mỗi máy sẽ cho ra một kết quả khác nhau, và bên dưới là kết quả trên máy của mình

[home] /Users/admin/.composer

Sau khi có đường dẫn home, thì đường dẫn tới phpcs và phpcbf sẽ có dạng như sau:

# Đường dẫn tới phpcbf
/Users/admin/.composer/vendor/bin/phpcbf

# Đường dẫn tới phpcs
/Users/admin/.composer/vendor/bin/phpcs

Vậy là chúng ta đã có đường dẫn tới phpcbf và phpcs, giờ chỉ cần điền chúng lần lượt mục Executable Path CBF và Executable Path CS trong trang cài đặt của extension Sniffer & Beautifier là xong.

Xem thêm các việc làm PHP lương cao trên TopDev

III. Dùng thử

Khởi tạo một dự án mới với 1 file phpcs.xml ở thư mục gốc, có rules theo PSR12 như sau:

1
2
3
4
<?xml version="1.0"?>
<ruleset name="PHP Standards">
    <rule ref="PSR12" />
</ruleset>

Tạo tiếp 1 file PHP có sẵn một vài lỗi convention:

1
2
3
4
5
6
7
8
9
<?php
// File này cố tình code sai convention
 
function sum ($a ,$b){
    return $a+$b;
}
 
echo sum(1,2);

Bật dự án lên bằng VS Code, và bạn sẽ thấy nó báo rất nhiều lỗi convention như sau:

Rất nhiều lõi convention được phát hiện

Và để fix hết các lỗi này, bạn chỉ cần Nhấn chuột phải chọn Format document, hoặc sử dụng tổ hợp phím format code như mình hướng dẫn ở đầu bài. Và dưới đây là kết quả sau khi format lại code, bạn sẽ thấy các lỗi convention trên đã được fix.

Các lỗi convention đã được fix.

Công việc tích hợp Sniffer & Beautifier vào VS Code để format code PHP đến đây là kết thúc, chúc các bạn thử nghiệm thành công.

Hẹn gặp lại bạn trong các bài viết tiếp theo.

Bài viết gốc được đăng tải tại phambinh.net

Có thể bạn quan tâm:

Truy cập ngay việc làm IT đãi ngộ tốt trên TopDev

Kinh Nghiệm Phỏng Vấn Dành Cho Các IT Support Tương Lai

kinh nghiệm phỏng vấn it support
Kinh Nghiệm Phỏng Vấn Dành Cho Các IT Support Tương Lai

IT Support là một công việc hấp dẫn trên thị trường việc làm công nghệ hiện nay. Nhiều bạn trẻ lựa chọn theo đuổi vị trí này với mong muốn được gắn bó và phát triển trong một môi trường làm việc tốt hơn, thu nhập và cơ hội nghề nghiệp cũng hấp dẫn hơn. Nếu đang cân nhắc đến công việc này, việc tìm hiểu qua những câu hỏi phỏng vấn thường xuất hiện hay một số kinh nghiệm phỏng vấn IT Support từ người đi trước là rất cần thiết. Trau dồi thêm kỹ năng với bài viết này nhé!

kinh nghiệm phỏng vấn it support
Colleagues on coffee break. Side view of cheerful young man and woman sitting at the table and chatting while man holding cup of hot drink

Một số câu hỏi thường xuyên xuất hiện khi phỏng vấn IT Support

1. Điểm mạnh, điểm yếu trong quá trình làm việc của bạn là gì?

Đây là câu hỏi gần như luôn xuất hiện ở bất cứ buổi phỏng vấn cho vị trí công việc nào. Đối với công việc IT Support đây càng là yếu tố quan trọng vì bạn phải thường xuyên tiếp xúc với khách hàng cũng như đàm phán công việc với các phòng ban liên quan.

Để trả lời tốt câu hỏi này với vai trò là một IT Support tương lai, điểm mạnh bạn nên nêu ra có thể là khả năng xử lý vấn đề, nhanh nhạy trong việc nắm bắt các sai sót và tìm cách khắc phục, linh hoạt cũng như có kỹ năng giao tiếp tốt.

Điểm yếu bạn có thể chia sẻ thành thật với nhà tuyển dụng và chắc chắn phải nói được cách mà bạn đang cố gắng để khắc phục điều này. Không có ai hoàn hảo, do đó yếu điểm chắc chắn sẽ xuất hiện, quan trọng là cách bạn khắc phục và cải thiện nó như thế nào. Nhà tuyển dụng sẽ đánh giá cao điều đó.

Xem thêm IT Support Là Làm Gì? Phát Triển Sự Nghiệp Với Vị Trí IT Support

2. Bạn đã có kinh nghiệm làm việc liên quan đến công việc IT Support chưa?

Với câu hỏi này, nhà tuyển dụng sẽ biết được bạn đã có kinh nghiệm làm việc trước đây chưa cũng như những kỹ năng mà bạn có có đáp ứng được yêu cầu của công ty với vị trí này không. Vẫn là điều quan trọng nhất trong mọi cuộc phỏng vấn, hãy thành thật. Kể cả bạn chưa có kinh nghiệm hay kinh nghiệm chưa đủ nhiều thì cũng hãy chia sẻ thành thật.

Điều quan trọng là tâm thế sẵn sàng học hỏi của bạn sẽ giúp nhà tuyển dụng đánh giá cao thái độ và có hướng đào tạo phù hợp thay vì nói quá đà về những gì mình có. Nhà tuyển dụng là những người đi trước và họ đủ năng lực để nhận biết những gì bạn nói có trung thực khi trả lời phỏng vấn hay không.

kinh nghiệm làm IT Support
Một số câu hỏi phỏng vấn vị trí IT Support

3. Theo bạn, đâu là yếu tố giúp bạn thành công với chuyên môn hiện tại của mình?

Đây là một câu hỏi rất hay để nhà tuyển dụng có thể xác định được mục tiêu và khoảng thời gian gắn bó của bạn với công việc của mình. Không có một khuôn mẫu chung hoàn hảo nào cho những câu hỏi như thế này, bạn hãy chia sẻ về hành trình theo đuổi đam mê của mình hay lí do tại sao bạn lại chọn gắn bó với công việc IT Support cho đến thời điểm hiện tại.

Song song với quá trình đó, bạn đừng quên chia sẻ những thành tựu mình đã đạt được trong suốt thời gian làm việc của mình. Và nhấn mạnh đâu là yếu tố giúp bạn có được những thành tựu đó, cũng như trong thời gian tới bạn sẽ làm những gì để đạt được những mục tiêu cao hơn. Điều đó không chỉ cho thấy ước mơ mà còn thể hiện cả sự nhiệt huyết của bạn với công việc của mình.

  IT Support Là Làm Gì? Phát Triển Sự Nghiệp Với Vị Trí IT Support
  Mẫu bảng mô tả công việc của IT Support

4. Bạn hiểu biết như thế nào về công việc IT Support và tại sao bạn lại chọn ngành nghề này?

Dường như khi đã ứng tuyển vào vị trí IT Support, ít nhiều ứng viên đều đã tìm hiểu trước và có sự hiểu biết nhất định với ngành mình mong muốn làm việc. Do đó đây có thể là câu hỏi khiến nhiều người cảm thấy đơn giản và không chuẩn bị nhiều, nhưng sự chuẩn bị chưa bao giờ là dư thừa. Hiểu biết càng nhiều càng cho nhà tuyển dụng thấy được sự nghiêm túc và tâm huyết của bạn trong công việc. Không chỉ là IT Support nói chung mà nếu có thể, hãy tìm hiểu thêm về vị trí này tại công ty đang ứng tuyển, nhà tuyển dụng sẽ đánh giá rất cao điều này.

Với câu hỏi về lý do tại sao bạn lại chọn ngành nghề này, bạn có thể dùng đam mê và những tính cách thích hợp của mình với công việc để chia sẻ với người phỏng vấn. IT Support đòi hỏi khả năng xử lý công việc và giải quyết các vấn đề một cách trôi chảy để đảm bảo sự vận hành trôi chảy của công ty. Bạn phải tham gia xử lý các vấn đề liên quan đến internet, mạng máy tính, phần mềm, phần cứng, các vấn đề về bảo mật thông tin,…

phỏng vấn IT Support

Các kinh nghiệm phỏng vấn IT Support nên cân nhắc

Để chuẩn bị cho một cuộc phỏng vấn thành công nhất, ngoài các vấn đề liên quan đến chuyên môn công việc của mình, bạn cũng đừng quên tìm hiểu thêm các thông tin về công ty như hoạt động, sản phẩm và phòng ban mình làm việc nếu có thể. Việc này sẽ rất hữu ích cho quá trình trả lời các câu hỏi của nhà tuyển dụng cũng như giải đáp được các thắc mắc của bạn khi cần.

Xem thêm các chương trình tuyển dụng IT Support lương cao trên TopDev

Ngoài ra cũng đừng quên chuẩn bị kỹ lưỡng cho vẻ ngoài của mình để tạo ấn tượng tốt trong mắt nhà tuyển dụng. Không cần quá cầu kỳ, chỉ cần đảm bảo một bộ trang phục sạch sẽ mà vẻ ngoài gọn gàng là đủ. Và đừng quên đến đúng giờ hẹn và chuẩn bị đủ các giấy tờ cần thiết theo yêu cầu công ty khi đi phỏng vấn.

Như bất cứ vị trí nào khác, để phỏng vấn thành công cho vị trí IT Support, hãy có sự đầu tư chất lượng và kỹ càng. Hi vọng một số thông tin được chia sẻ trong bài viết sẽ giúp bạn có thêm kinh nghiệm phỏng vấn IT Support. Đón đọc thêm nhiều chia sẻ với các chủ đề khác nhau cùng topdev.vn/blog nhé!

Có thể bạn quan tâm:

Xem thêm tuyển dụng việc làm IT hấp dẫn trên TopDev

PHP 8 có gì mới?

điểm mới của php 8
PHP 8 có gì mới?

Bài viết được sự cho phép của tác giả Phạm Bình

Chào anh em,

PHP đã chính thức phát hành phiên bản 8.0 (26/11/2020), với nhiều cải tiến mới cả về hiệu năng lẫn cũ pháp. Trong bài viết này, chúng ta cùng review các điểm thay đổi có trong PHP 8 nhé.

Trích lời dẫn trên trang chủ PHP.net

PHP 8.0 là một phiên bản cập nhật lớn của PHP. Phiên bản này bao gồm rất nhiều tính năng mới, đồng thời tối ưu cách truyền tham số (có thể đặt tên khi truyền tham số), union types (một biến có thể thuộc một vài kiểu dữ liệu), attributes, constructor, biểu thức match (cú pháp mới, gần giống switch case), toán tử nullsafe (cho phép truy xuất giá trị null một cách an toàn), JIT (trình biên dịch mới, giúp PHP 8 đạt hiệu năng cao), và cải tiến các về type system, xử lý lỗi, và tính nhất quán.

Chúng ta sẽ tìm hiểu lần lượt về các sự thay đổi của PHP 8 so với phiên bản trước lần lượt qua các mục dưới đây.

  Cách thiết lập JIT trong PHP 8
  Callback trong PHP là gì?

Xem thêm nhiều việc làm PHP hấp dẫn trên TopDev

I. TRUYỀN THAM SỐ THEO TÊN GỌI – PHP 8

Việc một function có nhiều tham số (với mình là từ 3 tham số trở nên), có thể khiến developer lúng túng khi sử dụng vì không nhớ rõ ý nghĩa của từng tham số, cũng như thứ tự truyền của chúng.

Ví dụ, function mkdir() (function giúp tạo thư mục) trong PHP có 4 tham số lần lượt là:

  • $directory: Bắt buộc, là đường dẫn để tạo thư mục
  • $permissions: Không bắt buộc, là khả năng truy cập vào thư mục (kiểu 0777, hay 0655), mặc định là 0777.
  • $recursive: Không bắt buộc, có cho phép tạo thư mục con ngay cả khi thư mục cha không tồn tại (cho phép tạo kiểu đệ quy), mặc định là false.
  • $context: Không bắt buộc, còn ý nghĩa là gì thì mình cũng chẳng hiểu lắm, chưa bao giờ dùng đến tham số này.

Các vấn đề mà một developer có thể gặp phải khi sử dụng mkdir() đó là:

  • Đôi khi không nhớ rõ thứ tự của 2 tham số $permissions và $recursive, không biết tham số nào được viết trước.
  • Nếu muốn đổi giá trị của tham số $recursive từ false thành true, developer buộc phải truyền cả tham số $permissions (vì $permissions đứng trước $recursive).

Cả hai vấn đề trên đều gây bất tiện khi sử dụng, để khắc phục nó, thì PHP cung cấp cú pháp mới cho phép truyền tham số theo tên gọi:

<?php // PHP 7.x mkdir('./hello/world', 0777, true); // PHP 8 mkdir(recursive: true, directory: './hello/world'); // Hoặc mkdir('./hello/world', recursive: true);

Lưu ý:
– Đây chỉ là cú pháp mới mà PHP 8 cung cấp để tiện sử dụng hơn khi cần, còn bạn vẫn có thể sử dụng cú pháp cũ bình thường trên phiên bản PHP 8 này.

– Tên tham số bạn không được phép “tự nghĩ ra” mà phải tuân theo tài liệu của PHP. Như trong ví dụ trên, recursive và directory là 2 tham số mà mình buộc phải tuân theo tài liệu mà PHP cung cấp.

Để xem “tên chuẩn” các tham số, có 2 cách:

Cách 1: Xem tài liệu chính thức trên trang chủ php.net (lưu ý phải xem trên trang chính thức, các trang khác có thể viết không đúng).

Cách 2: Sử dụng ReflectionFunction.

<?php // Muốn xem chi tiết các tham số của function mkdir $refFunc = new ReflectionFunction('mkdir'); foreach ($refFunc->getParameters() as $param) {     print $param . PHP_EOL; } /* Output Parameter #0 [ <required> string $directory ] Parameter #1 [ <optional> int $permissions = 0777 ] Parameter #2 [ <optional> bool $recursive = false ] Parameter #3 [ <optional> $context = null ] */

Lưu ý siêu quan trọng

Khuyên bạn nên sử dụng Cách 2 là chính xác nhất, Cách 1 không hẳn sẽ chính xác với mọi function. Như function mkdir trong ví dụ trên, thì tài liệu PHP viết sai 2 tên tham số là pathname và mode, đáng lẽ nó phải là directory và permissions mới đúng.

Tài liệu PHP ghi sai tên tham số, đáng lẽ phải là directory và permissions mới đúng.

II. TỐI GIẢN CÁCH SET THUỘC TÍNH TỪ CONTRUCTOR – PHP 8

Việc khởi tạo giá trị cho thuộc tính trong contructor của PHP 7 thường được viết như sau:

<?php // PHP 7 class Point {   public float $x;   public float $y;   public float $z;   public function __construct(     float $x = 0.0,     float $y = 0.0,     float $z = 0.0,   ) {     $this->x = $x;     $this->y = $y;     $this->z = $z;   } }

Nhưng với PHP 8, bạn có thể viết ngắn gọn thành

<?php // PHP 8 class Point {   public function __construct(     public float $x = 0.0,     public float $y = 0.0,     public float $z = 0.0,   ) {} }

III. UNION TYPES – PHP 8

PHP ngày càng hoàn thiện hơn về độ chặt chẽ của dữ liệu, trong phiên bản PHP 7.4, chúng ta có thể khai báo kiểu dữ liệu cho thuộc tính của lớp, tuy nhiên nó vẫn gặp một vài bất tiện. Nhưng bất tiện này đã khắc phục trong phiên bản 8 với khái niệm Union types – Cho phép một thuộc tính có thể thuộc nhiều kiểu dữ liệu.

<?php // PHP 7 class Number {     // Giả sử có một biến $numeber, và ta mong muốn nó ở dạng số     // Số thì có nhiều kiểu, số nguyên, số thập phân.     private $number;     // Nhưng hơi buồn     // Chúng ta không thể chỉ ra biến $number chỉ được phép thuộc dạng số với PHP 7     public function __construct($number)     {         $this->number = $number;     } } // Dẫn đến việc khởi tạo class, ta vẫn có thể truyền string như thường // Và không gặp một lỗi nào cả new Number('NaN'); // PHP 8 class Number {     // PHP 8 cho phép bạn khai báo biến $number chỉ được phép thuộc một trong 2 kiểu dữ liệu int hoặc float     // Khắc phục vấn đề của PHP 7 nêu trên     public function __construct(         private int|float $number     ) {} } // Nếu dùng thế này, bạn sẽ gặp lỗi new Number('NaN');

IV. NULLSAFE OPERATOR – PHP 8

Nullsafe operator – tạm dịch là Toán tử null an toàn. Cho phép bạn truy cập vào giá trị của một thuộc tính ở giá trị null mà không bị lỗi.

Ở các phiên bản PHP trước, để chắc chắn trước khi truy cập vào một giá trị, ta thường kiểm tra nó khác null để tránh lỗi, và nhìn nó có vẻ dài dòng như sau:

<?php $country =  null; if ($session !== null) {   $user = $session->user;   if ($user !== null) {     $address = $user->getAddress();     if ($address !== null) {       $country = $address->country;     }   } }

Nhưng với PHP 8, bạn có thể rút gọn thành:

<?php $country = $session?->user?->getAddress()?->country;

Khi bất kỳ thuộc tính nào trong “chuỗi” $session?->user?->getAddress()?->country là null, thì $country sẽ nhận giá trị là null và không có lỗi nào xảy ra.

V. BIỂU THỨC MATCH – PHP 8

Một biểu thức mới khá tương đồng với lệnh switch case được đưa vào trong phiên bản PHP 8 lần này là match, nhưng có một số điểm khác biệt sau:

  • match là một biểu thức, nghĩa là giá trị của nó có thể được trả về, bạn có thể dễ dàng lưu nó vào một biến để xử lý cho tiện.
  • Mỗi một nhánh của match chỉ được xử lý trên một dòng, nghĩa là bạn không cần break
  • match so sánh chặt chẽ về kiểu dữ liệu, giống như bạn so sánh sử dụng === vậy.

Bạn có thể xem ví dụ sau để hiểu rõ hơn:

<?php // PHP 7 switch (8.0) {   case '8.0':     $result = "Oh no!";     break;   case 8.0:     $result = "This is what I expected";     break; } echo $result; // Oh no! // PHP 8 $result = match (8.0) {   '8.0' => "Oh no!",   8.0 => "This is what I expected", }; echo $result; // This is what I expected

Lưu ýmatch khá tương đồng với switch case, nhưng không thể thay thế hẳn switch case.

VI. TRÌNH BIÊN DỊCH JIT – PHP 8

Hiểu nhanh thì JIT (Just In Time) là một kỹ thuật được PHP tích hợp vào việc biên dịch code PHP thành mã máy, giúp PHP 8 đạt được hiệu năng cao gấp 1,5 – 2 lần so với PHP 7. Nhưng đừng vội mừng, hãy xem biểu đồ dưới đây và mình sẽ giải thích cho bạn hiểu:

Biểu đồ so sánh tốc độ của PHP khi có JIT và không có JIT.

Trong biểu đồ trên:

  • bench.php, micro_bech.php, N-body, Mandelbrot đều là các bài test trên ứng dụng PHP đơn giản.
  • Các bài test còn lại, lần lượt test trên các ứng dụng PHP có độ phức tạp tăng dần.
  • Đường màu đen, kẻ dọc ở biểu đồ trên là ngưỡng tối đa khi PHP không sử dụng JIT.

Từ biểu đồ trên, rút ra nhận xét rằng:

Chỉ có ứng dụng PHP đơn giản khi áp dụng JIT mới có hiệu năng cao, còn các ứng dụng PHP phức tạp, sử dụng các framework, cms phổ biến như WordPress, Symfony (Chắc laravel cũng không ngoại lệ) thì sử dụng JIT lại không đem lại nhiều lợi ích. Thậm chí như trường hợp của Symfony khi áp dụng JIT còn làm ứng dụng chạy chậm hơn so với lúc không áp dụng.

Để nói hết về JIT thì bài viết này là chưa đủ, mình sẽ trình bày chi tiết ở một bài viết khác về cách cài đặt, ưu nhược điểm, cũng như phân tích nên dùng JIT trong dự án thực tế hay không.

VII. MỘT SỐ CẬP NHẬT KHÁC TRÊN PHP 8

Một số cập nhật điển hình khác trên PHP 8 như sau:

  • Thay vì sử dụng PHPDoc để chú thích, PHP 8 đã cung cấp thêm cấu trúc metadata.
<?php // PHP 7 class PostsController {     /**      * @Route("/api/posts/{id}", methods={"GET"})      */     public function get($id) { /* ... */ } } // PHP 8 class PostsController {     #[Route("/api/posts/{id}", methods: ["GET"])]     public function get($id) { /* ... */ } }
  • Toán tử @ vốn để “câm lặng lỗi” không còn được sử dụng ở PHP 8.
<?php // Khi thêm @ phía trước, nếu function có thực hiện lỗi cũng sẽ không báo lỗi // Nhưng đó đã là quá khứ rồi. // Ở PHP 8 vẫn báo lỗi bình thường @unlink('hello/world');
  • Cụm try {} catch () {} có thể không cần biến $exception trong catch.
<?php // PHP 7 try {     1/0; } catch (\Exception) { // Sẽ báo lỗi cú pháp ở dòng này     die('Something wrong'); } // PHP 8 try {     1/0; } catch (\Exception) { // Chạy bình thường     die('Something wrong'); }
  • Cho phép dấu phẩy ở cuối danh sách tham số.
<?php // PHP 7 class Uri {     private function __construct(         ?string $scheme,         ?string $user,         ?string $pass,         ?string $host,         ?int $port,         string $path,         ?string $query,         ?string $fragment // <-- Tham số cuối cùng không được phép có dấu phẩy     ) {         ...     } } // PHP 8 class Uri {     private function __construct(         ?string $scheme,         ?string $user,         ?string $pass,         ?string $host,         ?int $port,         string $path,         ?string $query,         ?string $fragment, // <-- Được phép viết dấu phẩy ở tham số cuối cùng     ) {         ...     } }

VIII. TỔNG KẾT

Trên là những cập nhật điển hình nhất của PHP 8, và bài viết của mình đã nêu được khoảng 80% những gì PHP cập nhật trong phiên bản lần này. Nếu muốn tìm hiểu chi tiết, bạn có thể đọc từ tài liệu chính thức của PHP.

Xin chào, hẹn gặp lại.

Bài viết gốc được đăng tải tại phambinh.net

Có thể bạn quan tâm:

Truy cập ngay việc làm IT đãi ngộ tốt trên TopDev

Các khái niệm cơ bản trong Apache Kafka

apache kafka
Các khái niệm cơ bản trong Apache Kafka

Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh

Để làm việc với Apache Kafka, các bạn cần nắm một số khái niệm cơ bản về:

  • Producer
  • Consumer
  • Broker
  • Cluster
  • Topic
  • Topic Partitions
  • Partition Offset
  • Consumer Group
  Apache Kafka là gì?
  Cài đặt Apache Kafka sử dụng Docker Compose

Xem thêm tuyển dụng UI UX Designer hấp dẫn trên TopDev

Producer

Producer là những application produce data và gửi data tới Apache Kafka Server. Data này sẽ là những message có định dạng, được gửi dưới dạng mảng byte tới Apache Kafka server. Ví dụ như các bạn có một tập tin .txt chứa text bên trong, chúng ta có thể dùng Producer để đọc từng dòng trong tập tin này rồi gửi tới Apache Kafka server.

Consumer

Consumer là những application nhận message từ Apache Kafka server với message được gửi từ Publisher. Consumer cần phải subcribe vào một topic nào đó của Apache Kafka server để có thể nhận được tất cả các message được emit vào topic này.

Sau khi nhận được data, Consumer có thể thêm code để xử lý data theo nhu cầu của mình.

Broker

Broker là Apache Kafka server, là cầu nối giữa Message Publisher và Message Consumer, giúp chúng có thể trao đổi message với nhau.

Cluster

Cluster là một group các Brokers hay nói cách khác là group các Apache Kafka server.

Topic

Topic được định nghĩa trong Apache Kafka server, là nơi để Publisher send data và cũng là nơi để Consumer subscribe để nhận data từ Publisher. Sử dụng topic giúp Apache Kafka phân loại được message và Consumer cũng biết là mình lấy data từ đâu.

Topic Partitions

Apache Kafka là một distributed messaging system và chúng ta có thể setup Apache Kafka server với cluster. Trong trường hợp một topic nhận quá nhiều message tại cùng một thời điểm, chúng ta có thể chia topic này thành những partitions được share giữa các Apache Kafka server với nhau trong một cluster được handle các message này.

Một partition sẽ small và independent với các partitions khác. Số lượng partition cho mỗi topic thì tuỳ theo nhu cầu của ứng dụng mà chúng ta có thể quyết định.

Partition Offset

Trong một topic partition, các message sẽ được lưu trữ và đánh dấu theo từng offset. Mỗi khi một message mới vào topic, Apache Kafka server sẽ quyết định message này nằm trong partition nào, offset nào của partition đó. Giá trị của các offset sẽ tăng dần trong một partition và chỉ available cho một Apache Kafka server.

Để lấy được một message trong Apache Kafka server, chúng ta cần chỉ định rõ topic name, partition number và offset number.

Consumer Group

Consumer group là một group các Consumer consume message từ Apache Kafka server. Mỗi một Consumer Group sẽ share với nhau việc handle messsage.

Bài viết gốc được đăng tải tại huongdanjava.com

Có thể bạn quan tâm:

Xem thêm việc làm CNTT hấp dẫn trên TopDev

PHP Developer là gì? Lộ trình trở thành PHP Web

php developer
Lộ trình trở thành PHP web developer

Bài viết được sự cho phép của tác giả Phạm Bình

Hello các bạn,

Trong bài viết này mình sẽ chia sẻ với các bạn lộ trình để trở thành PHP web developer. Thực ra mình không rõ nên coi nội dung dưới đây là lộ trình, là kỹ năng, hay kiến thức nữa, nhưng đại loại nếu bạn muốn trở thành PHP web developer thì nội dung dưới đây sẽ có ích với bạn.

I. PHP WEB DEVELOPER LÀ GÌ

Tìm hiểu qua một chút về “cái đích” mà chúng ta sẽ hướng đến, thì PHP web developer là tên một nghề liên quan đến công việc lập trình web. Trong đó PHP là tên ngôn ngữ lập trình, web developer tạm dịch là “Người phát triển web”, vậy PHP web developer thì hiểu là “Người phát triển web sử dụng ngôn ngữ PHP” (Ngoài PHP thì còn nhiều ngôn ngữ khác cũng làm được web).

Ok, dễ hiểu đúng không. Nhưng thực ra, cái tên “PHP web developer” thì là cách gọi vắn tắt của “PHP/JavaSctipt/HTML/CSS/bla bla,… web developer“, nghĩa là để trở thành PHP web developer thì bạn cần phải học rất nhiều thứ, và PHP chỉ là một trong số chúng.

  10 PHP Instagram Scripts & Widgets tốt nhất
  Câu chuyện trước khi PHP có composer

Xem thêm tuyển dụng PHP lương cao trên TopDev

Giới thiệu thêm

Để tạo ra một trang web, bạn sẽ cần các kiến thức:
– Kiến thức về HTML/CSS/JavaScript: Dùng để tạo giao diện trang web.
– Kiến thức về ngôn ngữ lập trình: Dùng để xử lý các tính năng có trên trang web (hiểu nôm na là vậy), và PHP là một ngôn ngữ lập trình.
– Kiến thức về cơ sở dữ liệu: Là nơi lưu trữ dữ liệu cho trang web. PHP thường kết hợp MySQL – một loại cơ sở dữ liệu.
– Kiến thức về webserver: Là máy chủ để chạy web, Apache và Nginx là 2 loại web server phổ biến.

Bạn thấy không, có phải mỗi PHP đ*o đâu, cả mớ kiến thức đấy chứ. Mà đấy mới chỉ là “phần nổi của tảng băng” thôi đó, phần chìm mình sẽ liệt kê chi tiết trong bài viết, mà nói trước là mình cũng không thể liệt kê hết được vì nó quá nhiều.

II. QUY ƯỚC

Các kiến thức được chia thành 3 level: Hiểu, Biết sử dụng, Sử dụng thành thạo. Trong đó:

  • Hiểu: Đã từng tiếp xúc với kiến thức đó, hiểu ý nghĩa và vai trò, đã từng làm một số ví dụ đơn giản. Mức này rất dễ đạt được.

Tôi Hiểu PHP, vì tôi biết nó phù hợp với các dự án web, tôi cũng từng làm một dự án nhỏ về website tin tức sử dụng PHP để hiểu hơn về nó.

  • Biết sử dụng: Bao gồm Hiểu và đã từng áp dụng kiến thức đó trong một dự án cụ thể, dự án được áp dụng phải đủ lớn, đủ nghiêm túc, không phải là các pet project.
    Lưu ý: Một số từ khác trong bài viết như Biết thiết kếBiết tối ưuBiết vận dụngBiết làm cũng tương đồng với Biết sử dụng.

Tôi biết sử dụng PHP, vì tôi từng sử dụng nó trong một dự thực tế. Tuy đây chỉ là dự án cá nhân, nhưng tôi hoàn thiện nó với thái độ nghiêm túc, cố gắng hoàn thiện ở mức cao nhất có thể. Trong quá trình hoàn thành dự án, tôi vẫn học được thêm nhiều kiến thức mới.

  • Sử dụng thành thạo: Bao gồm Biết sử dụng và lặp đi lặp lại trong một vài dự án, trong quá trình lặp lại không (hoặc ít) học được thêm kinh nghiệm mới.

Tôi sử dụng thành thạo PHP, vì tôi từng tham gia vào nhiều dự án thực tế sử dụng PHP, trong quá trình đó, tôi không (hoặc ít) học được thêm kiến thức mới về PHP.

Nếu so với tháp Bloom trong bài viết Kinh nghiệm làm việc được tính như thế nào, thì:

  • Level Hiểu trong bài viết này tương ứng với level Hiểu của tháp Bloom.
  • Level Biết sử dụng tương ứng với level Vận dụng của tháp Bloom.
  • Level Sử dụng thành thạo tương ứng từ Level Phân tích trở lên của tháp Bloom.

III. LỘ TRÌNH

Lưu ý:
– Lộ trình dưới đây chưa được kiểm chứng, mà chỉ được đúc kết từ kinh nghiệm cá nhân của mình.
– Lộ trình dưới đây đã được mình tối ưu (không quá ngắn cũng không quá dài), đảm bảo phù hợp với nhu cầu tuyển dụng hiện tại.
– Lộ trình dưới đây sẽ được cập nhật thường xuyên.

Level 1: Nhập môn lập trình

Đây là kiến thức cơ bản nhất mà bất kỳ lập trình viên nào cũng phải biết, chứ không riêng web developer:

  • Biết sử dụng ít nhất một ngôn ngữ lập trình.
  • Biết sử dụng một số giải thuật cơ bản: sắp xếp nhanh (quick sort), sắp xếp nổi bọt, tìm kiếm nhị phân, tìm kiếm tuần tự.
  • Biết sử dụng một số cấu trúc dữ liệu cơ bản: stack, queue, linked list.
  • Biết sử dụng lập trình hướng đối tượng.
  • Hiểu một số quy trình phát triển phần mềm: quy trình thác nước, agile/scrum.

Level 2: Nhập môn lập trình web

Đây là các kiến thức mà bất kỳ web developer nào cũng phải biết:

  • Biết sử dụng HTML, CSS, JavaScript.
  • Biết sử dụng một số thư viện web cơ bản: jQuery, bootstrap.
  • Biết sử dụng PHP.
  • Biết sử dụng ít nhất một database quan hệ, gợi ý bạn nên học MySQL.
  • Biết thiết kế database quan hệ (chuẩn 3NF, phi chuẩn, index trong database).
  • Biết chuyển giao diện từ file thiết kế sang giao diện web (biết cắt html).
  • Biết sử dụng mô hình MVC trong lập trình web.
  • Biết sử dụng một số design pattern phổ biến: singleton, factory.
  • Hiểu về cách hoạt động của trang web: backend, frontend, client side, server side, request, response, header.
  • Sử dụng thành thạo coding convention trong PSR.
  • Biết sử dụng CLI.
  • Biết sử dụng một số tool trên CLI: npm, composer.
  • Biết sử dụng Restful API.
  • Hiểu về UI, UX.
  • Biết sử dụng GIT.
  • Biết sử dụng wordpress.
  • Biết sử dụng ít nhất một PHP framework, gợi ý bạn nên học Laravel.
  • Hiểu về một số lỗi bảo mật: xss, csrf, sql injection.
  • Hiểu về SEO.
  • Hiểu về SOLID.

Đang cập nhật thêm…

Level 3: Lập trình web chuyên sâu

Đạt tới “cảnh giới” này, bạn có thể lựa chọn 1 trong 3 hướng phát triển dưới đây:

2.1 Frontend developer

Frontend developer là những người làm về mặt giao diện, trải nghiệm, tương tác của trang web. Bất kể menu, màu chữ, font chữ, các modal bật lên, đóng lại,… tóm lại là những thứ mà người dùng có thể nhìn thấy đều được coi là frontend và đều là công việc của frontend developer.

  • Sử dụng thành thạo HTML, CSS và JS.
  • Sử dụng thành thạo ít nhất một frontend framework: reactjs, angular, vuejs.
  • Sử dụng thành thạo ít nhất một css preprocessor như scss, PostCSS.
  • Sử dụng thành thạo ít nhất một tool build frontend, gợi ý bạn nên chọn webpack.
  • Biết tối ưu SEO.
  • Biết thiết kế UI/UX.
  • Sử dụng thành thạo css BEM rules.

Đang cập nhật thêm…

Tìm việc làm Frontend ngay

2.2 Backend developer

Nếu frontend được thể hiện ngoài giao diện – cái mà người sử dụng có thể dễ dàng nhìn thấy và cảm nhận, thì backend lại là “trái tim” nằm phía sau, âm thầm xử lý các tác vụ mà người dùng thực hiện.

Frontend có thể dễ dàng vẽ lên một cái nút “Đăng nhập”, nhưng bấm vào cái nút đó là hàng loại các thao tác tìm kiếm, kiểm tra, xác minh được thực hiện phía backend.

  • Sử dụng thành thạo PHP
  • Sử dụng thành thạo ít nhất một database, gợi ý bạn nên học MySQL.
  • Sử dụng thành thạo ít nhất một framework, gợi ý bạn nên chọn Laravel.
  • Sử dụng thành thạo lập trình hướng đối tượng.
  • Biết sử dụng Linux.
  • Biết sử dụng ELK stack.
  • Biết sử dụng docker.

Đang cập nhật thêm…

Tìm việc làm Backend ngay

3.3 Full stack developer

Về cơ bản, Fullstack developer là người có thể làm được hết (hoặc phần lớn) các công việc của cả Frontend developer và Backend developer.

  • Experience with Back-end API development by using one of the following programming languages: JavaScript (NodeJS), Java, Ruby, PHP, Python, C#, Golang or Scala
  • Have strong knowledge in REST API
  • Experience with JavaScript or TypeScript with ReactJS, Angular, or Vue. Docker, NoSQL database, Relational database, Scrum/Agile development methodologies

Tìm việc làm Fullstack Developer lương cao

Bài viết gốc được đăng tải tại phambinh.net

Có thể bạn quan tâm:

Truy cập ngay việc làm IT đãi ngộ tốt trên TopDev