Home Blog Page 71

0x0B và JSON parser

json parser
0x0B và JSON parser

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

Vào một ngày đẹp trời, một mail rì pọt từ user gửi đến, nội dung như này:

Tui đang dùng app ABC của mấy người. Ở màn hình order, tui copy một đoạn text từ Powerpoint ra và dán vào mục Order Message. Xong tui bấm nút tiếp tục, thì app ngừng hoạt động, màn hình chỉ hiện lên một nửa, đây là nội dung lời nhắn:

“Happy birthday, Dave! We got a gift for you! Hope you like it, Mom and Dad.”

Nhìn từ bên ngoài, lời nhắn kia trông không có gì khả nghi, nhưng nếu nhìn kĩ bằng cách bật chế độ hiển thị các kí tự ẩn (hidden characters) trên editor (với Emacs thì M-x whitespace-mode, với các editor khác thì các bạn tự Google đi), thì ta có thể thấy ngay một vài kí tự lạ xuất hiện:

"Happy birthday, Dave!^K^K We got a gift for you!^K^K Hope you like it,^K Mom and Dad."

Để chi tiết hơn, ta có thể dùng một công cụ xem các kí tự ẩn nào đó, ví dụ như công cụ này. Ta sẽ thấy kí tự lạ kia là 0x0b.

Té ra, 0x0b hay còn gọi là vertical tab \v, là một dạng kí tự ngắt dòng giống như \n, mã ASCII là 11, có thể được chèn vào bằng cách gõ Ctrl + K (đó là vì sao Emacs hiện ra ^K). Giống như kí tự tab \t (giúp nhảy nhanh về bên phải vài kí tự), \v có chức năng tương tự nhưng là nhảy xuống dòng, nhưng ở thời điểm hiện tại thì không còn ai dùng đến kí tự này nữa.

Nhưng khổ cái là nó vẫn còn tồn tại trong nhiều tài liệu, và nghiêm trọng hơn, nó không nằm trong chuẩn của JSON [1]. Chính vì thế nên đa số các JSON parser (như của V8 và Spidermonkey) đều không thể parse được nếu nội dung có chứa \v, và còn nhiều dạng kí tự ẩn (non-printable characters [2]) khác nữa. Và sẽ quăng lỗi khi phải parse nội dung chứa các kí tự lạ đó.

  Cách tạo REST API với JSON Server
  Chuyển đổi JSON qua CSV sử dụng thư viện Jackson

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

Ở ví dụ đầu bài, vì user input chứa kí tự không hợp lệ, JSON.parse() quăng lỗi khiến cho chương trình không thể chạy tiếp, và ngắt luôn quá trình render (nhân tiện, thằng nào nghĩ ra ba cái vụ client-side rendering vậy???). Mọi thứ diễn ra bên dưới, chỉ để lại cái màn hình render nửa vời cho anh bạn user xấu số.

Cho nên cẩn tắc vô áy náy, chúng ta nên replace hết các kí tự không mong muốn trước khi đưa một đoạn nội dung nào đó từ user vào xử lý, ví dụ:

// Viết regex phải có tâm:
// - \v: thay thế kí tự \v thành \n
// - \p{C}: match tất cả các kí tự non-printable
const filterUnwantedChars = str => str.replace(/\v/g, "\n")
                                      .replace(/\p{C}/g, "");

Và một lưu ý khác, đó là đừng bao giờ sử dụng hàm JSON.parse() trực tiếp, vì nó sẽ throw error, mà nên wrap nó và handle lỗi có thể xảy ra một cách hợp lý để tránh làm “bể” UI chỉ vì user input như ví dụ đầu bài (nhớ dùm, chúng ta là JS dev, không phải Elixir hay Erlang dev, chúng ta không có cái triết lý gọi là “Let it crash”).

const safeParseJSON = str => {
    let result = null;
    try {
        result = JSON.parse(str);
    } catch (e) {
        console.error("Failed to parse JSON", e);
    }
    return result;
};

Bằng cách này, chúng ta expect hàm safeParseJSON trả về một object khi parse thành công, và trả về kết quả null khi có lỗi xảy ra, đồng thời có thể kiểm tra log để biết chính xác là lỗi gì, mà không làm ngắt quá trình thực thi của code [3].

Bổ sung: Sau khi published bài viết này, thì thím @nhducit có bổ sung thêm một điểm, đó là đối với React, từ v16 có hỗ trợ method componentDidCatch(), hoạt động giống như catch {} để không làm crash quá trình execute code của component [4], bằng cách này chúng ta có thể handle lỗi một cách an toàn.

Đọc thêm

  1. The JavaScript Object Notation (JSON) Data Interchange Format, https://tools.ietf.org/html/rfc7159#section-7 
  2. Non-printable Characters, https://www.regular-expressions.info/nonprint.html 
  3. What is the difference between throw error and console.error?, https://stackoverflow.com/questions/25377115/what-is-the-difference-between-throw-error-and-console-error 
  4. Error handling in React 16, https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html 

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

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

Giới thiệu về MicroProfile

microprofile
Giới thiệu về MicroProfile

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

MicroProfile là một tập hợp các APIs dựa trên Jarkata EE giúp chúng ta dễ dàng xây dựng các ứng dụng Java Enterprise theo mô hình kiến trúc microservices. Các APIs này bao gồm:

Trong đó:

  • Open Tracing dùng để theo dõi flow của một request tới các service.
  • Open API dùng để tạo API documentation, chúng ta cũng thường hay gọi nó là Swagger. Các bạn có thể xem thêm Swagger trong Spring Boot.
  • Rest Client dùng để gọi tới một RESTful Web Service, tương tự như RestTemplate hoặc WebClient trong Spring.
  • Config dùng để làm những việc liên quan đến cấu hình của ứng dụng.
  • Fault Tolerance dùng để handle các trường hợp ứng dụng đang bị lỗi.
  • Metrics dùng để định nghĩa các metrix của ứng dụng.
  • JWT Propagation dùng để làm việc với access token trong OAuth2 và OpenId Connect.
  • Health expose các thông tin runtime của các services.
  • CDI của Jakarta EE, hỗ trợ dependency injection trong ứng dụng.
  • JSON-P (JSON Processing) dùng để convert Java object POJO qua JSON.
  • JAX-RS hiện thực RESTful Web Service.
  • JSON-B (JSON Binding) dùng để convert JSON qua Java object.
  • Jakarta Annotation của Jakarta EE, định nghĩa một tập các annotation để làm việc với các ứng dụng Jakarta EE.
  Building Microservices Application - Phần 1: Sử dụng Netflix Eureka, Ribbon và Zuul
  Building Microservices Application - Phần 2: Xử lý "Chain of Failures" dùng Circuit Breaker Pattern với Netflix Hystrix

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

Chúng ta có thể sử dụng công cụ MicroProfile Starter để tạo mới project MicroProfile, tương tự như Spring framework, tại https://start.microprofile.io/:

Để làm ví dụ cho bài viết này, mình sẽ tạo mới một service sử dụng MicroProfile như sau:

Các bạn không cần select các APIs nhé! Mặc định thì tất cả các APIs của MicroProfile sẽ được khai báo trong ứng dụng.

Nhấn DOWNLOAD để tải project về máy, sau đó thì import nó với Maven project vào IDE mà các bạn đang sử dụng nhé!

Kết quả của mình như sau:

Kiểm tra External Libraries của Maven project này:

các bạn sẽ thấy ngoài các dependencies của MicroProfile, chúng ta còn thấy các dependencies của Jakarta EE. Chúng toàn là các APIs, không có implementation nên để chạy được ứng dụng MicroProfile, chúng ta phải sử dụng các server runtime có hỗ trợ cho MicroProfile như Open Liberty, Payara, WildFly,… các bạn nhé!

Mình đã chọn sử dụng Open Liberty với Liberty Maven plugin để chạy ví dụ cho bài viết này như sau:

Nội dung của tập tin server.xml được generated như sau:

<?xml version="1.0" encoding="UTF-8"?>
<server description="${project.name}">
<featureManager>
<feature>microProfile-4.0</feature>
</featureManager>

<httpEndpoint id="defaultHttpEndpoint"
httpPort="9080"
httpsPort="9443"/>

<webApplication location="${project.name}.war" contextRoot="${app.context.root}">
<classloader apiTypeVisibility="+third-party"/>
</webApplication>
<mpMetrics authentication="false"/>

<!-- This is the keystore that will be used by SSL and by JWT. -->
<keyStore id="defaultKeyStore" location="public.jks" type="JKS" password="atbash"/>

<!-- The MP JWT configuration that injects the caller's JWT into a ResourceScoped bean for inspection. -->
<mpJwt id="jwtUserConsumer" keyName="theKeyId" audiences="targetService" issuer="${jwt.issuer}"/>
</server>

Như các bạn thấy, feature microProfile-4.0 được khai báo để sử dụng trong thẻ <featureManager>. Ngoài ra còn có một số cấu hình khác. Các properties như project.name, jwt.issuer, app.context.root được khai báo trong thẻ <bootstrapProperties> của Liberty Maven plugin trong tập tin pom.xml các bạn nhé!

Chạy project với Liberty Maven plugin, các bạn có thể access tới endpoint mặc định của generated project là http://localhost:9080/data/hello, kết quả như sau:

Ngoài ra thì với feature microProfile-4.0 được khai báo để hỗ trợ thì chúng ta còn có thể access tới một số endpoint khác như:

OpenAPI để xem API documentation http://localhost:9080/openapi/ui/:

Health check service http://localhost:9080/health/:

Metrics http://localhost:9080/metrics/:

Tuỳ theo nhu cầu thì các bạn hãy sử dụng MicroProfile cho phù hợp nhé!

Bài viết gốc được đăng tải tại huongdanjava.com
Có thể bạn quan tâm:
Xem thêm công việc CNTT hấp dẫn trên TopDev

Vài ghi chép về V8 và Garbage Collection

v8 và garbage collection
Vài ghi chép về V8 và Garbage Collection

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

Bài viết này chỉ đề cập đến V8 (là JavaScript engine đứng sau Google Chrome và NodeJS), sau khi đọc bài này, nên tìm đọc thêm về SpiderMonkey (Firefox), Chakra (Edge) và Carakan (Opera), các yếu tố về kĩ thuật trong các engine này có thể sẽ khác nhiều so với V8.

Lý do chọn viết về V8 thì rất là đơn giản, vì engine này có nguồn tài liệu cực kì phong phú và gần như là, hễ tìm với từ khóa JavaScript engine thì nó cứ ra V8 =))

Thực ra, nếu nhìn nhận một cách khách quan về V8 cũng như những kĩ thuật mà team này bỏ ra cho công việc optimization một ngôn ngữ như JavaScript, và đào sâu vào những kĩ thuật đó, thì đó là một kho tàng kiến thức đồ sộ mà chỉ có dại lắm mới dám bỏ qua không ngó ngàng tới.

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

Viết bài này, mình không có tham vọng gom hết đống kiến thức đồ sộ đó vào một bài viết nhỏ, nên tất nhiên sẽ còn nhiều điểm thiếu sót, hy vọng các bạn đọc xong sẽ nhiệt tình góp ý, cũng như thu về được một ít thông tin vụn vặt, để từ đó mà đem đào sâu hơn vào engine lý thú này.

■  Trong khi đọc, các bạn nên hạn chế đọc những dòng in nghiêng, đây là những đoạn bình luận nhố nhăng không đóng góp gì nhiều vào nội dung bài viết =))

1. Hidden Class

Mọi thứ trong JavaScript đều là object, và mọi thuộc tính của một object thì đều có thể được thêm vào hoặc bỏ đi (thay đổi layout), hoặc thay đổi kiểu dữ liệu (type) bất cứ lúc nào (on the fly). Điều này khiến cho việc optimize một ngôn ngữ “động” như JavaScript (dynamically typed language) trở nên rất khó khăn.

Ví dụ luôn, giả sử ta có đoạn code như thế này:

class Car {
    door_open() {
        // ...
    }
}

class Girl {
    // girls has no door
}

const open_the_door = (object) => {
    object.door_open();
};

Trong ví dụ trên, hàm open_the_door() nhận vào một object và gọi hàm door_open() của object đó, tuy nhiên, vì không có cách nào quy định một cách cụ thể kiểu dữ liệu nhận vào của hàm open_the_door(), compiler sẽ không thể nào biết trước được liệu object truyền vào có tồn tại hàm door_open() hay không. Mà nếu không biết thì phải kiểm tra, bằng cách tra cứu (lookup – duyệt hết toàn bộ hàm/thuộc tính có trong object đó và tìm). Rõ ràng, là không hề tối ưu, và nguyên nhân thì lại do chính thiết kế của JavaScript.

■  “not a design weakness, but a weakness by design” 

 

Team V8 giới thiệu một khái niệm gọi là hidden class, gán vào cho mỗi object để giúp cho việc tracking kiểu và các thuộc tính của chúng một cách thuận tiện hơn. Và mỗi lần object thay đổi, thì hidden class của nó cũng sẽ thay đổi tương ứng.

Một ví dụ thực tế, có lẽ là rất thường gặp:

let product = {};
product.title = api.getBookTitle(book_id) || "";
product.pages = api.getBookPages(book_id) || "";

Với cách viết như trên, ta có tổng cộng 3 lần thay đổi cấu trúc của object product. Đầu tiên, là ở câu lệnh let product = {}, lúc này V8 sẽ tạo ra hidden class C0 để biểu diễn cấu trúc của product (là một object rỗng). Tiếp theo, khi gặp câu lệnh gán product.title, thì cấu trúc thay đổi, V8 thay thế hidden class C0 thành C1 (có thêm thuộc tính .title), và cuối cùng là thành C2 ở câu lệnh gán product.pages, quá trình thay đổi diễn ra như hình bên dưới:

Việc xảy ra đến 3 lần thay đổi cấu trúc, dẫn đến 3 lần V8 phải tạo ra hidden class mới là không hề tối ưu. Ta nên khởi tạo tất cả các thuộc tính của một object ngay trong khi khởi tạo chính object đó, bằng cách viết rút gọn:

let product = {
    title: api.getBookTitle(book_id) || "",
    pages: api.getBookPages(book_id) || ""
};

Với cách viết này, V8 chỉ tạo ra một hidden class duy nhất cho object product, vì không có sự thay đổi cấu trúc nào xảy ra sau câu lệnh khởi tạo:

Các object có cùng kiểu hoặc cấu trúc (hoặc thuộc cùng một class) thì sẽ có chung một hidden class, V8 sẽ không tạo mới mà sử dụng lại các hidden class đã có nếu trùng khớp.

Ví dụ với câu lệnh sau, hidden class của product thay đổi từ C2 về lại C1 chứ không tạo mới:

delete product.pages;

Tuy nhiên, nếu trong trường hợp trên, thuộc tính bị xóa là .title thì sẽ lại có một hidden class C3 được tạo ra.

Bằng cách sử dụng hidden class, V8 luôn biết trước được cấu trúc của một class/object, từ đó có thể tối ưu việc truy xuất đến các thuộc tính của chúng bằng nhiều cách, một trong các kĩ thuật tối ưu mà V8 áp dụng đó là inline caching.

Có thể hiểu nôm na, inline caching là việc tạo ra “đường tắt” (lưu luôn vị trí chính xác của từng vùng nhớ cho từng thuộc tính vào trong code) giúp cho việc truy xuất đến thuộc tính đó diễn ra nhanh hơn, thay vì cách dùng “đường chính” (thực hiện lookup vị trí của thuộc tính đang cần truy xuất trong bộ nhớ).

■  Cái chữ “đường tắt” kia đúng ra phải gọi là “fast path”, bản thân cái từ đó nó cũng mang nhiều ý nghĩa và đọc vào nghe thấm thía hơn. Thế nào là fast path? có fast hẳn phải có slow, vậy slow path khác fast path như nào? Bạn phải tự đặt ra đc câu hỏi như vậy. Tiếc là viết tiếng Việt không dùng được những cái vô lời hữu ý như vậy được, chán bỏ bà.

 

Ví dụ khi truy xuất một thuộc tính của một object:

let x = product.title;

Giả sử V8 sẽ sinh ra machine pseudo code để xử lý câu lệnh trên như thế này:

create $x
create $product_hidden_class_offset
assign $product_hidden_class_offset = lookup_hidden_class($product)
create $p_title_offset
assign $p_title_offset = lookup_property($product_hidden_class_offset, "title")
assign $x = $p_title_offset
■  Okay, khi thực thi, V8 sẽ sinh ra machine code và chạy trực tiếp đống code đó, trên đây không phải là machine code, mà chỉ là pseudo code mà mình chế ra nhằm giúp dễ theo dõi bài viết hơn mà thôi. Suy cho cùng, đâu có ai muốn đọc machine code trong một bài viết về JavaScript đâu đúng không?

 

Ở đây ta thấy có việc tra cứu một thuộc tính xảy ra (hàm lookup_property, giả sử hàm này trả về kết quả là vị trí của vùng nhớ chứa thuộc tính title, là 0xDAEDBEEF).

Nếu thuộc tính product.title được sử dụng thường xuyên trong chương trình, thì việc tra cứu liên tục như vậy rất tốn kém, để tối ưu, V8 sẽ cache output của hàm này lại sau lần gọi đầu tiên, để rồi nó sẽ thay đổi đống machine pseudo code đã sinh ra thành:

create $x
assign $x = 0xDAEDBEEF

Trong bài viết tiếp theo, chúng ta sẽ tìm hiểu thêm về cơ chế sinh code và tối ưu động này của V8. Còn bây giờ, hy vọng các bạn đã hiểu được tầm quan trọng của hidden class cũng như những lợi ích mà nó đem lại trong việc cải thiện performance của JavaScript.

Đọc thêm:

2. Garbage Collection

Nói đến công việc dọn rác (thu dọn và xóa sổ những object/giá trị không còn được dùng tới, trả lại bộ nhớ để dùng cho việc khác), đây là một phần khá quan trọng mà ít người quan tâm trong JavaScript. Ngày nay, khi mà JavaScript được dùng càng nhiều cho cả phía server lẫn các single page application, vòng đời của một JS app ngày một dài ra, vai trò của GC ngày một lớn.

■  Trước đây nhiều người vẫn hay nói đùa là JavaScript thì cần gì GC, chạy trên browser, khi nào hết mem thằng user nó F5 một phát thì tất cả bay biến hết mẹ nó rồi còn đâu 

 

GC của V8 là một Generational Garbage Collector. Trong quá trình thực thi, các giá trị (biến, object,…) được tạo ra nằm trong bộ nhớ heap. V8 chia heap ra làm nhiều khu vực, trong đó ta chỉ đề cập đến hai khu vực chính là new-space (chứa các đối tượng nhỏ, có vòng đời ngắn) và old-space (chứa các thành phần sống dai hơn, bự hơn).

Hai khu vực này cũng là hai đối tượng cho hai loại thuật toán GC khác nhau, đó là scavenge và mark-sweep / mark-compact.

Khi chúng ta khai báo một giá trị mới, giá trị này sẽ được cấp phát nằm rải rác trong khu vực new-space, khu vực này có một kích thước nhất định, thường là rất nhỏ (khoảng 1MB đến 8MB, tùy vào cách hoạt động của ứng dụng). Việc khai báo như thế này tạo ra nhiều khoảng trống không thể sử dụng được trong bộ nhớ.

■  Vì sao lại có những khoảng trống đó thì là kiến thức cơ bản, và bắt buộc các bạn phải biết, không biết thì Google, mình không thích nói nhiều.

 

Khi new-space đã đầy, thì scavenge sẽ được kích hoạt để dọn dẹp các vùng nhớ “chết”, giải phóng mặt bằng, có thể sẽ gom góp các vùng nhớ rời rạc lại gần nhau cho hợp lý, vì new-space rất nhỏ, nên scavenge được kích hoạt rất thường xuyên. Trong quá trình giải tán đô thị của scavenge, nếu các vùng nhớ nào còn trụ lại được sau 2 chu kỳ, thì được điều đi vùng kinh tế mới promote lên khu vực old-space, nơi mà có sức chứa lên đến hàng trăm megabytes, và là nơi mà thuật toán mark-sweep hoặc mark-compact hoạt động, với chu kỳ dài hơn, ít thường xuyên hơn.

Tất cả những thuật toán GC trên đều hoạt động thông qua hai bước chính là:

  • Bước đánh dấu: thuật toán sẽ duyệt qua tất cả các giá trị có trong khu vực bộ nhớ mà nó quản lý, bước duyệt này đơn giản chỉ là depth-first search, tìm gặp và đánh dấu.
  • Bước xử lý: sau quá trình duyệt, tất cả những giá trị chưa được đánh dấu, sẽ bị coi là đã “chết”, và sẽ bị xóa bỏ, trả lại bộ nhớ trống (sweep), hoặc gom góp lại để lấy lại các khoảng trống trong bộ nhớ không sử dụng được (compact).

Điểm khác nhau giữa scavenge và mark-sweep/mark-compact nằm ở cách mà chúng được implement, các bạn có thể xem thêm chi tiết về hai thuật toán trên trong bài A tour of V8: Garbage Collection mà mình sẽ dẫn link bên dưới.


Về nguyên lý đánh dấu (marking) của các thuật toán trên, chúng ta sẽ làm quen với khái niệm reachability.

Tất cả mọi đối tượng được khai báo trong global scope, hoặc các DOM elements thì được gọi là root. Và đứng từ các roots, tất cả mọi giá trị local có quan hệ trực tiếp hoặc gián tiếp với các roots này sẽ được coi là còn “sống” (reachable). Những đối tượng nào không có mối liên hệ trực tiếp hoặc gián tiếp vói bất kì roots nào, thì coi như là đã “chết” (unreachable).

Ví dụ với đoạn code sau:

let a = { name: "huy" };

function hello() {
    let b = a;
    // you're here
}

Trạng thái của heap và sơ đồ biểu diễn reachability của từng giá trị, ngay tại vị trí // you're here được thể hiện như sau:

Ở đây ta có a và hello là hai giá trị thuộc global scope, vì thế chúng được gọi là các root. Biến a tham chiếu đến một object nằm trong heap, và biến b bên trong hàm hello cũng tham chiếu tới chính object này.

Khi hàm hello() được thực thi xong, và chúng ta đi ra khỏi scope của hàm đó, thì mọi tham chiếu đến các giá trị bên trong hàm đó đều sẽ bị hủy đi, lúc này b trở thành unreachable, và sẽ trở thành đối tượng để bị GC nó thịt (mặc dù bị thịt lúc nào thì không ai biết trước được).

Lưu ý, đối với các root, chúng ta không thể sử dụng lệnh delete để xóa sổ chúng, ví dụ:

delete a; // trả về false
delete hello; // trả về false

Nhưng chúng ta có thể gán chúng bằng null để cho các giá trị mà chúng tham chiếu tới bị GC hốt (nhưng chính biến đó thì lại vẫn còn tồn tại, ở đây, cả a lẫn hello đều vẫn bảo toàn tính mạng), ví dụ, sau lệnh dưới đây, sơ đồ của chúng ta sẽ là:

a = null;

Vậy thì đến bao giờ a và hello mới bị giải phóng khỏi bộ nhớ? Câu trả lời là: chừng nào ứng dụng của chúng ta còn chạy, thì chúng vẫn sẽ còn tồn tại trong bộ nhớ. Dân gian gọi là memory leak. Chính vì thế, nên hạn chế việc tạo và sử dụng các biến global, nếu không thực sự cần thiết.


Nói tiếp về vấn đề khi sử dụng delete và null, ở trên chúng ta đã biết delete không thể xóa sổ các root, tuy nhiên nó vẫn hoạt động tốt thuộc tính của các object:

delete a.name;

Khi chạy lệnh trên, ta có thể chủ động làm cho giá trị a.name trở thành mục tiêu của GC, nhưng cách này có một hiệu ứng tiêu cực, đó là nó làm thay đổi hidden class của a, mà như chúng ta đã biết ở phần trước, việc này gây ảnh hưởng tới performance. Tương tự, nếu chúng ta gán a.name là null, nó cũng sẽ trở thành mục tiêu của GC.

a.name = null;

Nhưng lại một lần nữa, việc gán một biến thành null chỉ có thể làm cho giá trị mà biến đó tham chiếu tới trở thành mục tiêu bị xóa sổ, nhưng không thể xóa sổ chính biến đó. Trong trường hợp này thì biến a.null vẫn còn tồn tại.

Cách tốt nhất để hủy một giá trị là đưa nó vào một scope nào đó, ví dụ như sử dụng JS Modules hoặc IIFE (immediately invoked function expression):

(function() {
    let a = { name: "huy" };
})();

a; // ReferenceError: a is not defined

Tiếp, khi truyền một hàm vào setInterval hoặc setTimeout, một tham chiếu đến hàm đó sẽ được tạo ra, khiến cho hàm này không thể bị GC hốt, dù cho chúng ta đã ra khỏi scope chứa nó, và sẽ vẫn tồn tại cho đến chừng nào nó được kích hoạt.

Đối với trường hợp của setTimeout, chúng ta có thể yên tâm, vì sau một khoảng thời gian, nó sẽ được chạy, và cuối cùng sẽ bị GC hốt, tuy nhiên đối với setInterval, chuyện lại không hề đơn giản:

function do_something() {
    setInterval(function run() {
        // do something
    }, 1000);
}
// you're here

Hàm run vẫn còn tồn tại và vẫn còn được thực thi sau mỗi 1 giây, kể cả khi hàm do_something đã kết thúc vòng đời của nó. Chính vì thế, phải luôn luôn lưu lại tham chiếu của mỗi câu lệnh setInterval, và chạy clearInterval khi không còn cần đến:

function do_something() {
    let runner = setInterval(function run() {
        // do something
    }, 1000);
    
    // do more thing

    clearInterval(runner);
}

Một vài lưu ý khác, GC của V8 là stop-the-world, có nghĩa là, khi GC chạy thì toàn bộ chương trình sẽ bị dừng lại, thời gian dừng có khi lên đến vài trăm mili giây, là một con số khá lớn.

Team V8 áp dụng một vài kĩ thuật khác gọi là concurrent marking, giúp cho ứng dụng JavaScript vẫn có thể được thực thi (tất nhiên là concurrent) trong khi GC hoạt động. Tuy không hoàn toàn giúp cho ứng dụng tránh bị đứng, nhưng cũng cải thiện được performance rõ rệt, các bạn có thể đọc thêm qua bài Concurrent marking in V8 (link bên dưới).


Trên đây là một vài ghi chép về Garbage Collection trong V8, hy vọng qua bài viết này, các bạn đã hiểu thêm phần nào về những gì xảy ra bên dưới một ứng dụng JavaScript, và về cách mà JavaScript hoạt động, từ đó có cái nhìn sâu sắc hơn, và cẩn trọng hơn trong quá trình làm việc với thứ ngôn ngữ quái đản đó.

Hôm nào có thời gian mình sẽ làm một bài đi sâu hơn về cơ chế thực thi code của V8 (Crankshaft, TurboFan,…). Cảm ơn các bạn đã đọc đến tận những dòng này 

Đọc thêm:

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

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

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

Chạy ứng dụng web với Liberty Maven plugin

liberty maven plugin
Chạy ứng dụng web với Liberty Maven plugin

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

Open Liberty cũng là một Java Server runtime giúp chúng ta chạy các ứng dụng Java web application. Sử dụng Liberty Maven plugin sẽ giúp chúng ta nhanh chóng deploy ứng dụng lên Open Liberty một cách dễ dàng trong quá trình development. Trong bài viết này, mình sẽ hướng dẫn các bạn cách sử dụng Liberty Maven plugin này các bạn nhé!

  Nói về ServletContext và ServletConfig trong Jakarta EE Servlet
  67 tools, libraries và resources giúp Web developer "dễ thở" hơn

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

Đầu tiên, mình sẽ tạo mới một Jakarta EE Maven project để làm ví dụ:

Mình sẽ không sử dụng Maven Jetty plugin để chạy project này mà sẽ sử dụng Liberty Maven plugin.

Các bạn có thể khai báo Liberty Maven plugin như sau:

<plugin>
<groupId>io.openliberty.tools</groupId>
<artifactId>liberty-maven-plugin</artifactId>
<version>${liberty-maven-plugin.version}</version>
</plugin>

với:

<liberty-maven-plugin.version>3.5.1</liberty-maven-plugin.version>

Để plugin này hoạt động, chúng ta cần định nghĩa thông tin về Open Liberty server trong một tập tin tên là server.xml nằm trong thư mục /src/main/liberty/config:

Nội dung của tập tin server.xml này như sau:

<server description="Open Liberty server">
<featureManager>
<feature>jakartaee-9.1</feature>
</featureManager>
<webApplication location="jakartaee-liberty.war" contextRoot="/"/>
<httpEndpoint host="*" httpPort="${default.http.port}" httpsPort="${default.https.port}"
id="defaultHttpEndpoint"/>
</server>

với 2 properties “default.http.port” và “default.https.port” được định nghĩa trong tập tin pom.xml như sau:

<liberty.var.default.http.port>9080</liberty.var.default.http.port>
<liberty.var.default.https.port>9443</liberty.var.default.https.port>

Liberty Maven plugin sẽ tự động replace giá trị của các properties với key bắt đầu với liberty.var, được khai báo trong tập tin pom.xml với các properties được khai báo trong tập tin server.xml.

Với Liberty Maven plugin, chúng ta cần định nghĩa feature mà chúng ta muốn chạy trong tập tin server.xml này. Như các bạn thấy, ở đây mình đang cần chạy Jakarta EE 9.1 nên mình đã khai báo feature này trong thẻ <featureManager>. Các bạn có thể xem toàn bộ feature của Open Liberty tại đây.

Lúc này, nếu chạy ứng dụng bằng Maven với “mvn clean liberty:run” và đi đến địa chỉ http://localhost:9080/, các bạn sẽ thấy kết quả như sau:

Với khai báo trong tập tin server.xml thì ứng dụng của chúng ta sẽ chạy ở địa chỉ http://localhost:9080/jakartaee-liberty/ các bạn nhé!

Kết quả như sau:

Liberty Maven plugin hỗ trợ chúng ta live coding, có nghĩa là Open Liberty sẽ tự động load những thay đổi của chúng ta trong code trong quá trình development. Các bạn có thể chạy câu lệnh Maven “mvn clean liberty:dev” để làm được điều này, giúp cho quá trình development của chúng ta nhanh hơn, rất tiện đó các bạn!

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 việc làm CNTT hấp dẫn trên TopDev

VisGrid – Thiết lập Selenium-Grid qua giao diện

visgrid
VisGrid – Thiết lập Selenium-Grid qua giao diện

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

Trong phần trước, chúng ta phải chạy các câu lệnh Java với Selenium Server để khởi tạo Hub và Node. Và chúng ta có hai màn hình Console để điều kiển Hub và Node. Điều này đôi khi gây ra một số bất lợi cho chúng ta khi theo dõi quá trình chạy của kịch bản kiểm thử tự động.

Phần này, mình xin giới thiệu với các bạn một công cụ mà cho phép chúng ta khởi tạo Hub và Node trên UI – VisGrid.

  Cài đặt Visual Studio: Công cụ lập trình mạnh mẽ của Microsoft
  Hướng dẫn tạo Copyright Header trên từng file .cs trong Visual Studio

Xem thêm tuyển dụng PHP lương hấp dẫn trên TopDev

Sau khi download VisGrid về, chúng ta chạy chương trình VisGrid-*.jar và một UI sẽ hiện thị như sau:

Start Visgrid

Kế tiếp, chúng ta thiết lập các thông số cho Hub tại tab Hub và nhấn vào nút Start Hub. Sau khi khởi động Hub, hai nút Stop Hub và Create Node sẽ được hiển thị

Start Hub

Để khởi tạo các Node, chúng ta nhấn vào nút Create Node. Khi nhấn vào Create Node, một panel sẽ được hiển thị để chúng ta thiết lập cấu hình cho Node. Nhấn nút Add để khởi tạo một Node.

Start Node

Như vậy là chúng ta đã có thể sử dụng Hub và Node cho việc thực thi kịch bản kiểm thử Selenium qua Selenium-Grid.

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

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

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

Từ spacemacs đến init.el

spacemacs
Từ spacemacs đến init.el

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

Chuyển từ vim sang emacs, sự lựa chọn hàng đầu là spacemacs. Lý do? vì spacemacs có evil-mode, giúp sử dụng keybinding giống vim trên emacs, có khả năng tự động cài đặt những package cần thiết (ví dụ khi mở file *.rs mà chưa cài rust-mode), tự động gỡ bỏ các package không sử dụng, giao diện được config sẵn, và kèm theo hơn 200 packages các kiểu được cài sẵn… nói chung đây là một bản emacs không có gì ngoài magic 

  Namespace trong C#
  Tìm hiểu chi tiết về Spacing trong CSS

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

Tuy nhiên, magic quá mức vừa là điểm mạnh, vừa là vấn đề hết sức nghiêm trọng của spacemacs. Ngoài việc nó làm cho quá trình khởi động chậm đi rất nhiều (mình đã từng rất bất ngờ khi biết emacs của nhiều người chỉ tốn có 2 giây để khởi động), việc sử dụng khái niệm layer làm giấu đi nhiều thứ mà đáng lẽ ra chính người dùng phải kiểm soát được, và tạo thêm nhiều sự phức tạp không đáng có, ví dụ như không ít lần spacemacs bị crash hoặc tệ hơn là một chức năng nào đó bỗng nhiên dở chứng không hoạt động được nữa, sau khi cài một hoặc một vài packages mới, và dù gỡ nó ra cũng không giúp ích gì được.

Trên kênh support chính của team spacemacs, là github, thì đa phần mọi issue đều được reply một cách chung chung là:

  • “Bạn đã thử chạy emacs với lệnh –debug-init chưa?”
  • “Bạn post cấu hình file .spacemacs lên xem nào?”

Những câu trả lời như thế này không hề có ích, đơn giản là vì output của --debug-init không thực sự hữu dụng vì spacemacs còn load thêm quá nhiều thứ linh tinh khiến cho việc debug gặp khá là nhiều khó khăn.

Thêm một vấn đề khi sử dụng spacemacs và evil-mode đó là cảm giác sợ bỏ lỡ mất những gì thuộc về emacs truyền thống, tuy nhiên vấn đề này về sau không phải là mối bận tâm của mình nữa.

Nhìn chung, spacemacs là một giải pháp khá thích hợp cho những ai muốn chuyển từ vim sang, nhưng chỉ thích hợp dùng một thời gian. Để có thể tìm hiểu sâu hơn về emacs, thì kiểu gì cũng nên tự tìm cách xây dựng cho mình một file config riêng. Nhiều người cực đoan thậm chí còn nói như này (hơi tục một tí nhé ):

  • “Xài spacemacs cũng giống như mặc quần sịp của thằng khác vậy” 

Thế nên sau một thời gian thì mình cũng bắt đầu thử nghiệm một vài phiên bản emacs customized khác, như doom-emacs (sau đó thì bợ luôn cái theme doom-one của ông này), centaur-emacs (không thích lắm),… nhưng nhìn chung thì vẫn đi tới quyết định tự cấu hình một file init.el riêng.

Kết quả là giờ mình có một bản emacs mà tự mình có thể kiểm soát mọi thứ bên trong nó, tốc độ startup chỉ 2.8s (kiểm tra bằng M-x emacs-init-time), và quan trọng là cực kì nhanh, mượt. Kèm theo đó là việc viết code Emacs Lisp cũng rất là sướng 

Cách cấu hình như nào thì mình sẽ không ghi lại ra bài này, vì đã từng post ở bên blog tiếng Anh rồi, và nó cực kì dễ, ai cũng có thể làm theo 

Các bạn cũng có thể tham khảo thêm cấu hình init.el mà mình đang sử dụng, sử dụng use-package để cài đặt và quản lý config, kèm theo các package cơ bản như là evil-mode cho vim keybinding, doom-one theme, helm cho vụ search và fuzzy match, projectile để quản lý project, neotree để hiện cây thư muc, which-key để hiện danh sách các keybinding khi bắt đầu gõ phím tắt, general để customize phím tắt, flycheck để kiểm tra code khi đang gõ, company cho vụ autocomplete,…

Ngoài ra, vì việc cấu hình emacs sử dụng lisp, việc tìm hiểu xem viết code lisp như thế nào mới đúng chuẩn cũng là một việc khá quan trọng :))

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 việc làm công nghệ hấp dẫn trên TopDev

Nginx là gì, setup một server serve static file với Nginx

nginx là gì
Nginx là gì, setup một server serve static file với Nginx.

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

Chắc hẳn nếu bạn đang là lập trình viên, bạn cũng đã có đôi lần nghe qua Nginx. Nginx là một web server rất nổi tiếng, nó có thể được dùng để serve static file, làm load balancer cho hệt thống đằng sau nó, hay thậm chí là mail server hay video streaming server, với việc sử dụng thêm “rtmp module”

Do sự giới hạn về trình đô nên hôm nay mình sẽ chỉ nói về cách mà Nginx hoạt động xử lí request đang tới như thế nào và sau đó mình sẽ hướng dẫn các bạn setup một server để serve static file.

Đầu tiên Nginx hoạt động như thế nào và nó ra đời để giải quyết vấn đề gì.

Trước khi nginx ra đời thì có Apache Server cũng đã làm rất tốt nhiệm vụ của một web server. Nhưng sau này khi mà internet đến với nhiều người hơn, một lượng lớn kết nối đổ dập tới server khiến cho Apache không xử lí được.

  10x engineer - cắt giảm chi phí 10 lần

  Biến Git và GitHub trở thành công cụ đắc lực cho Software Engineer

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

Apache handle mỗi request bằng cách tạo ra 1 thread để xử lí nó, khiến server tiêu tốn rất nhiều RAM và bị sập khi số lượng Request đồng thời lớn do bộ nhớ không đám ứng được.

Còn đối với Nginx, nginx không xử lí request theo hướng thread, nó xử lí request bất đồng bộ theo hướng sự kiện, sử dụng ít tài nguyên.

Thành phần chính của Nginx là Master Process và N Worker Process.

Master Process là cái được khởi chạy đầu tiền, đọc file cấu hình, khởi tạo và chạy N Worker processs, hứng request phân chia cho Worker và đồng thời cũng nhận kết quả từ Worker Process để trả lại cho client.

Tiếp theo, hãy cùng tạo 1 server serve static file.

  • Trước hết cài đặt Nginx, với linux các bạn chỉ cần chạy lệnh sau
    sudo apt install nginx

Mặc định thì File config nằm ở “/etc/nginx/nginx.conf”

Theo mặc định khi bạn chạy một server và cần nginx là front-end cho server đó thì bạn phải khai báo server và các thuộc tính cần thiết trong thư mục “site-enabled”, giả sử mình có một server có tên là “dongnguyen.dev” đang chạy thì mình phải tạo một file config trong thư mục “sites-enabled” có tên tuỳ thích nhưng hơn hết đễ dễ nhìn mình sẽ đặt tên file config của mình là “dongnguyen.dev.conf”

Theo mặc định Nginx khi khởi chạy sẽ gom tất cả nội dung trong thư mục “sites-enabled” để phân tích là xử lí. Có thể nhận thế điều này khi trong file “nginx.conf” trên có xuất hiện dòng này

Mình cấu hình nội dung file như sau.

Trong Block “server” chúng ta đang cho nói với Nginx rằng hãy lắng nghe trên cổng 443 và sử dụng giao thức HTTP/2 và sử dụng chứng chỉ SSL cho server có tên là “dongnguyen.dev”

Để thêm SSL config cho server chúng ta include ssl config trong thư mục snippet của Nginx. Để tạo SSL cho server chúng ta có thể sử dụng một dịch vụ miễn phí của https://letsencrypt.org/

Đầu tiên chạy lệnh sau, lệnh này sẽ install certbot cli cho chúng ta.

  1. sudo apt-get update
  2. sudo apt-get install software-properties-common
  3. sudo add-apt-repository universe
  4. sudo add-apt-repository ppa:certbot/certbot
  5. sudo apt-get update

Chạy tiếp

sudo apt-get install certbot python-certbot-nginx

Giờ mình sẽ dùng certbot để generate private key và fullchain, nếu tạo thành công bạn sẽ nhìn thấy 2 file quan trọng tên là “fullchain.pem” và “privkey.pem”

Các bạn thêm 2 file sau vào thư mục Snippet của Nginx, nhớ thay đổi tên server cho phù hợp với bạn.

ssl_certificate /etc/letsencrypt/live/dongnguyen.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dongnguyen.dev/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

Qua trở lại với file config cho con server của chúng ta trong “sites-enabled”

ở đây chúng ta có hai location để nói với nginx sau khi “listen” trên cổng 443 nếu nhận được request nào match với các location trên thì xử lí theo những gì quy ước trong đó

Lưu ý không giống như Golang Router Handler function nào được khai báo trước thì sẽ được check trước, với nghinx thì người lại các location được khia báo sau sẽ có độ ưu tiên cao hơn.

Như trong hình khi client truy cập vào “dongnguyen.dev/static/xxx/yyy/zzz” thì sẽ match,l nó sẽ thực hiện tiếp việc “try_files “ và check em liệu trong “/var/www/dongnguyen.dev” có file mà client đang lấy hay không nếu không sẽ trả về status 404.

Trước khi khởi động server hãy chắc chắn bạn chỉnh sửa đúng file config

Chạy lệnh “sudo nginx -t” nếu các file config của bạn OK thì nó sẽ báo như hình dưới.

Khởi chạy server bằng cách chạy lệnh “sudo systemctl start nginx”.

Tạo một vài static file trong thư mục “/var/root/www/dongnguyen.dev/static/” xem chúng ta có lấy được gì ko.

Mình đã copy một đống anh vào trong server của mình, mình tạo thư mục “/var/root/www/dongnguyen.dev/static/image” để chứa ảnh. và bây giờ nếu muốn lấy ảnh nào đó thì mình chỉ cần map theo đúng đường dẫn của ảnh là được. Ví dụ để lấy file “cold.png” thì mình chỉ cần truy cập

https://dongnguyen.dev/static/image/cold.png

Mở trình duyệt lên và tèng teng.

Vậy là ta đã dùng nginx để static file và nếu mình truy cập vào “https://dongnguyen.dev” thì nó sẽ proxy_pass qua con server đang chạy trên cổng 8080 của mình.

Đến đây là hết rồi :)) Nginx có thể làm được rất nhiều thứ hay nếu bạn muốn học Nginx hay vào ngay trang chủ của nó. Riêng mình do giới hạn về trình độ nên mình chỉ có thể chia sẻ đến đây.

Cảm ơn các bạn đã đọc bài. ^_^

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

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

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

Những dấu hiệu bạn đang không làm tốt công việc kiểm thử

kiểm thử
Những dấu hiệu bạn đang không làm tốt công việc kiểm thử

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

Trong cuộc sống chúng ta không quá xa lạ với những dấu hiệu. Khi ra đường chúng ta sẽ dễ dàng bắt gặp những dấu hiệu về đường một chiều, bảng báo cấm đậu xe. Khi khám bác sĩ, bác sĩ cũng hay hỏi về những dấu hiệu như hắc hơi, sổ mũi, ho v.v. Việc nhận biết những dấu hiệu đó giúp ích chúng ta rất nhiều trong cuộc sống.

Tương tự trong kiểm thử cũng có những dấu hiệu của riêng nó. Khi bạn nhận được nhiều lời phàn nàn từ khách hàng, khi số lượng bug bạn để “lọt” đến tay khách hàng ngày càng nhiều, khi tất cả các trường hợp kiểm thử có kết quả “Passed”, khi bạn không biết tài liệu kiểm thử lưu ở đâu, một lỗi được trao đổi liên tục qua lại giữa bạn và developer, khi bạn chẳng học được gì mới trong dự án v.v Tất cả những dấu hiệu đó cho thấy có thể bạn đang không làm tốt công việc của một kỹ sư kiểm thử. Việc nhận ra những dấu hiệu đó càng sớm càng tốt sẽ giúp bạn điều chỉnh, chỉnh sửa ngay lập tức hay ngăn ngừa vấn đề diễn biến xấu thêm.

Xem thêm tuyển Tester lương cao trên TopDev

Cách đây vài tuần, mình có đặt một câu hỏi tương tự về những dấu hiệu giúp nhận biết bạn không làm tốt công việc kiểm thử trên LinkedIn và nhận được gần 100 lượt bình luận và chia sẻ của cộng đồng kỹ sư kiểm thử trên thế giới. Mặc dù có nhiều dấu hiệu còn nhiều tranh cãi nhưng cũng có một số dấu hiệu nhận được sự đồng thuận cao của giới kỹ sư kiểm thử và mình cũng muốn chia sẻ với cộng đồng kỹ sư kiểm thử Việt Nam về những dấu hiệu này

*Lưu ý: Những dấu hiệu bên dưới là tập hợp của những ý kiến của các cá nhân và những dấu hiệu họ quan sát được và trong một ngữ cảnh cụ thể. Có thể đúng và có thể sai. Việc bạn đang có những dấu hiệu trên không đồng nghĩa bạn là một “tester dở” hay bạn đang gặp rắc rối mà đó chỉ đơn thuần là “dấu hiệu”, “biểu hiện” hay “triệu chứng”. Việc giải mã những dấu hiệu trên và tìm ra nguyên nhân thực sự của vấn đề đòi hỏi phân tích chi tiết hơn.

signs_4

Những dấu hiệu liên quan đến Lỗi:

  • Tỉ lệ lỗi đến tay người dùng tăng cao (Defect Leakage Rate)
  • Lỗi được tìm thấy trễ trong các giai đoạn sau của chu kỳ kiểm thử
  • Lỗi tìm thấy nhiều trong User Acceptance Test (Kiểm thử chấp nhận)
  • Lỗi được đóng và mở nhiều lần, lặp đi lặp lại
  • Lỗi không hợp lệ chiếm hơn 15% tổng số lỗi của dự án
  • Khách hàng tìm được lỗi crash hệ thống
  • Rất ít lỗi được tìm thấy và log lên hệ thống
  • Chỉ tập trung tìm lỗi mà không tập trung ngăn ngừa lỗi
  • Không tập trung phân tích hệ thống để tìm lỗi nghiêm trọng
  • Lỗi được tìm thấy trong các trường hợp kiểm thử đã được kiểm thử và đánh dấu là “Passed”
  • Bên thứ 3 tìm được lỗi trong bộ trường hợp kiểm thử “Happy path”
  • Lỗi đáng ra phải được phát hiện bởi Tester nhưng lại được phát hiện bởi Developer hay được đánh dấu trong Release Note
  10 lý do kiểm thử phần mềm trở thành một nghề thời thượng
  3 tips làm việc với JavaScript giúp bạn tiết kiệm thời gian

Những dấu hiệu liên quan đến việc Thực thi kiểm thử:

  • Tất cả các trường hợp kiểm thử đều Passed
  • Các trường hợp kiểm thử được in nghiêng, in đậm, font chữ mà sắc khác nhau.
  • Kỹ sư kiểm thử chỉ thực thi theo đúng các bước trong trường hợp kiểm thử mà không tìm cách “break” ứng dụng
  • Không kiểm thử sản phẩm dưới góc nhìn người dùng cuối
  • Khi bạn quá tự tin để đưa sản phẩm ra thị trường và bỏ qua “luật Murphy
  • Có 7000 trường hợp kiểm thử cho một chức năng đơn giản của hệ thống
  • Tự động hóa tất cả các trường hợp kiểm thử và chỉ sử dụng một công cụ duy nhất
  • Không nắm được Developer đã kiểm thử những gì rồi
  • Viết nhiều trường hợp kiểm thử dư thừa và quá tập trung và một tính năng
  • Dành nhiều thời gian để debug lỗi của automation hơn là kiểm thử sản phẩm
  • Tập trung quá nhiều và các tài liệu kiểm thử (kế hoạch kiểm thử, trường hợp kiểm thử, v.v) hơn là mục đích chính của kiểm thử
  • Không tập trung vào quá trình kiểm thử như thực thi kiểm thử sai môi trường yêu cầu, cắt bỏ các bước, hiểu sai yêu cầu/hướng dẫn kiểm thử, tài liệu kiểm thử
  • Đã 12h trưa và không ai trong đội kiểm thử biết hôm nay sẽ kiểm thử cái gì, chỉ kiểm thử ngẫu nhiên vài chỗ
  • Khi có ai thắc mắc về một tính năng của ứng dụng thì bạn luôn trả lời “tính năng này xưa giờ nó vậy..”
  • Không hiểu được yêu cầu business đối với sản phẩm đang kiểm thử
  • Kỹ sư kiểm thử đọc qua tài liệu đặc tả yêu cầu và tuyên bố “Hiểu hết, không hỏi gì thêm”

Những dấu hiệu liên quan đến Quản lí kiểm thử:

  • Hoãn đưa sản phẩm ra thị trường chỉ để tìm câu trả lời cho câu hỏi “Làm sao con bug này có thể sót được”
  • Điều chỉnh công việc kiểm thử chỉ để cho khớp với ước tính ban đầu
  • Không biết nơi lưu trữ các tài liệu, trường hợp hay kết quả kiểm thử khi người phụ trách vắng mặt
  • Tài liệu kiểm thử chỉ có một version duy nhất
  • Tài liệu, các trường hợp kiểm thử không được cập nhật các tính năng mới của sản phẩm kiểm thử
  • Khi việc kiểm thử vượt quá ngân quỹ, thời gian và chất lượng kém
  • Khi bạn phải làm việc nhiều thời gian hơn bình thường
  • Khi ai đó đánh thức bạn khi bạn đang ngủ (đặc biệt là trong giờ làm :-))
  • Sếp tự động thêm tính năng vào sprint và không thông báo cho đội kiểm thử
  • Khi bạn không rõ milestone kế tiếp của dự án là khi nào
  • Bạn không trả lời được câu hỏi “Bạn test tới đâu rồi”
  • Bạn không có sẵn các kế hoạch hay chiến lược kiểm thử cho việc kiểm thử phần nào, kiểm thử nhiều ít, cách tiếp cận, rủi ro v.v

Những dấu hiệu liên quan đến Giao tiếp:

  • Đội kỹ sư kiểm thử tách biệt với các đội khác
  • Kỹ sư kiểm thử ngồi trước máy tính nhiều giờ, tai đeo headphone
  • Kỹ sư kiểm thử không nói chuyện với Dev và ngược lại
  • Kỹ sư kiểm thử không nói chuyện với Chủ sản phẩm và ngược lại
  • Kỹ sư kiểm thử không nói chuyện với Khách hàng và ngược lại
  • Không ai tìm bạn để hỏi hay xin ý kiến
  • Kênh giao tiếp chủ yếu thông qua e-mail và bảng báo bug
  • Khi mọi người quyết định mà không thông qua kỹ sư kiểm thử
  • Khi bạn cảm thấy dường như bạn đứng ngoài lề trong các cuộc thảo luận về công nghệ, công cụ, qui trình
  • Quản lí dự án dường như “tránh né” bạn. Không cung cấp đủ nguồn lực cho công việc kiểm thử và xếp kiểm thử của bạn vào độ ưu tiên thấp
  • Kỹ sư kiểm thử ít khi đặt câu hỏi cho bạn đồng nghiệp, Dev, quản lí dự án, chủ sản phẩm, khách hàng v.v
  • Khi bạn không rõ phần nào đã kiểm thử rồi, phần nào chưa
  • Không tham gia hoặc nhận được báo cáo hàng ngày từ Dev, BA, Scrum Master, quản lí dự án
  • Bạn có những phát kiến, tìm hiểu thú vị…và bạn không chia sẻ cho team
  • Bạn không nói cho mọi người biết sai lầm/lỗi của bạn để giúp mọi người nhận biết và phòng tránh nó
  • Bạn không thường xuyên hỏi “Tại sao…”
  • Bạn và Developer thường xuyên phải hỏi qua hỏi lại một con bug vì mọi người không hiểu bạn đang mô tả vấn đề gì
  • Bạn yêu cầu về thiết bị và nguồn lực chỉ 1 ngày trước khi bạn tiến hành kiểm thử
  • Bạn phớt lờ lỗi
  • Bạn không chỉ ra được phần thiếu sót trong đặc tả yêu cầu. Không hiểu và sử dụng công cụ phù hợp cho từng giai đoạn của dự án
  • Khi bạn quan sát thấy một kỹ sư kiểm thử ngồi “yên lặng” và không có ý kiến suốt buổi họp

Những dấu hiệu liên quan đến Liên tục cải tiến:

  • Không thường xuyên thử học những kỹ năng mới để làm công việc tốt hơn
  • Nhận việc một cách cam chịu cũng như phớt lờ học hỏi những điều mới mẻ hay triển khai những ý tưởng mới
  • Không thường xuyên tự đánh giá bản thân để cải hoàn thiện mình
  • Không học thêm điều gì mới hay thấy thử thách trong cả tuần làm việc

Những dấu hiệu khác (nhưng không kém phần quan trọng):

  • Nhận được nhiều phàn nàn từ khách hàng
  • Luôn mặc định áp dụng một qui trình (đã từng chạy tốt trong quá khứ) cho những dự án khác nhau
  • Bị sa thải
  • Bạn thức dậy và thấy mệt mỏi
  • Bạn có cùng cách tiếp cận cho các dự án kiểm thử
  • Bạn cho rằng từ khách hàng, Developer đến Quản lí là những tên ngốc
  • Bạn/những lỗi bạn tìm thấy không nhận được sự tôn trọng cần thiết từ các phòng ban khác.

Bên trên là những dấu hiệu mình tổng hợp từ diễn đàn LinkedIn và dĩ nhiên không phải là một danh sách của tất cả các dấu hiệu. Nếu bạn phát hiện thêm những dấu hiệu khác, bạn có thể chia sẻ bằng cách comment bên dưới bài viết. Ngoài ra bài viết chỉ tập trung liệt kê các dấu hiệu chứ không đi sâu vào phân tích cũng như đưa ra giải pháp. Nếu mọi người có hứng thú thì có thể tìm đọc quyển “Common System and Software Testing Pitfalls” của Donald G. Firesmith để có cái nhìn chi tiết và sâu hơn những vấn đề nêu trên.

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

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

Các Phần Mềm Dành Cho Dân IT Mà Mọi Lập Trình Viên Nên Biết

các phần mềm dành cho dân it
Các Phần Mềm Dành Cho Dân IT Mà Mọi Lập Trình Viên Nên Biết

Lập trình chưa bao giờ là dễ dàng nhưng tại sao chúng ta không làm cho nó dễ dàng hơn? Bằng cách sử dụng các ứng dụng hỗ trợ việc lập trình trở nên thuận tiện và nhanh chóng hơn bao giờ hết. Việc sử dụng các ứng dụng để thực hiện công việc một cách hiệu quả nhanh chóng hơn đã không còn là một việc xa lạ. Một ứng dụng tốt sẽ hỗ trợ rất nhiều trong quá trình làm việc, tăng tốc độ làm việc và nâng cao hiệu quả hơn. Vậy đâu là phần mềm thích hợp dành cho lập trình viên? TopDev sẽ giới thiệu đến bạn các phần mềm dành cho dân IT mà mọi lập trình viên nên biết.

các phần mềm dành cho dân IT
Các phần mềm dành cho dân IT mà mọi lập trình viên nên biết

1. Notepad++

Notepad++ (Notepad Plus) một công cụ để soạn thảo ngôn ngữ lập trình dành cho hệ điều hành Microsoft Windows. Notepad++ không chỉ gây ấn tượng bởi sự gọn nhẹ mà còn hỗ trợ nhiều ngôn ngữ lập trình khác nhau gồm: ASP, PHP, Java, C#…

Notepad++
Notepad++

Đây là một phần mềm  được biết đến bởi nhiều lập trình viên từ những người mới vào nghề đến những lập trình viên có kinh nghiệm lâu năm. Mặc dù đơn giản, gọn nhẹ nhưng Notepad++ không chỉ dừng lại ở những tính năng cơ bản mà còn cực kỳ hữu ích khi cho phép người dùng cài đặt thêm các plugin hỗ trợ, mang lại hiệu quả tốt hơn khi lập trình nên nó đã dần trở thành một công cụ lập trình phổ biến và được yêu thích bởi các Coder.

Bên cạnh những ưu điểm về tính năng, dung lượng nhỏ gọn, việc được cung cấp miễn phí cũng là một ưu điểm để người dùng biết đến nhiều hơn. Với Notepad++ người dùng chỉ cần tải về và sử dụng thôi nhé, không tốn một chi phí nào chỉ tốn tiền mạng thôi.

2. Adobe Dreamweaver

Adobe Dreamweaver

Adobe Dreamweaver là một công cụ lập trình đơn giản dùng để thiết kế web. Đây là một trong những công cụ giảng dạy được yêu thích tại các trường học do sự đơn giản dễ sử dụng, đồng thời cung cấp nhiều mẫu giao diện trang web để người dùng có thể sử dụng hoặc tham khảo.

 Để tiện cho người dùng Adobe Dreamweaver hỗ trợ nhiều ngôn ngữ lập trình khác nhau như: PHP, HTML, CSS, JavaScript, ASP… Bạn có thể sử dụng chính phần mềm này để đưa trang web của bạn launching mà không cần công cụ nào khác.

3. Microsoft Visual Studio code

Đến từ Microsoft thì Visual Studio Code chưa bao giờ làm các anh em IT phải thất vọng. Visual Studio Code được xem là có mọi tính năng mà bất kỳ lập trình viên nào cũng mong đợi ở một phần mềm soạn thảo code. Bên cạnh đó là các tính năng khác vô cùng hữu ích.

Là một phần mềm dành cho dân IT, Visual Studio Code, nhẹ nhưng mạnh mẽ, sẽ không khiến chúng ta phải đợi lâu trong việc khởi động. Đồng thời ứng dụng cũng cung cấp mã nguồn mở và đa nền tảng và nhiều tính năng thú vị khác.

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

Với thiết kế giao diện đơn giản, đẹp và dễ sử dụng Visual Studio Code sẽ được chào đón bởi cộng đồng IT.

Trên hết, Visual Studio Code là phần mềm miễn phí, vẫn thường xuyên được hãng cập nhật để tốt hơn. Ứng dụng đã nhận được sự công nhận và tin từ hàng triệu lập trình viên trên toàn thế giới. 

4. ReQtest

ReQtest là một ứng dụng được phát triển dựa trên đám mây, bao gồm một bộ mô-đun: quản lý yêu cầu, quản lý kiểm thử, theo dõi và báo cáo lỗi. ReQtest được thiết kế với mục đích thực hiện các nhiệm như kiểm tra, theo dõi lỗi và quản lý những yêu cầu được đưa ra. Về cơ bản ứng dụng này là một nền tảng dùng để quản lý dự án, được sử dụng trong việc phát triển và triển khai phần mềm. 

  10 lý do kiểm thử phần mềm trở thành một nghề thời thượng
  19 tips cho các kỹ sư phần mềm hữu ích trong 2024

5. OutSystems

OutSystems là một nền tảng cung cấp các công cụ cho các công ty để phát triển, triển khai và quản lý các ứng dụng doanh nghiệp đa kênh. OutSystems có đủ những thứ mà các nhà phát triển ứng dụng cần có để dễ dàng tạo, thực hiện và quản lý các ứng dụng doanh nghiệp một cách nhanh chóng và dễ dàng hơn. Nền tảng OutSystems rất tốt trong việc giảm chi phí phát triển ứng dụng, với các công cụ tùy chỉnh được tạo ra với một phần chi phí phù hợp.

6. Eclipse

Eclipse

Eclipse là một ứng dụng cung cấp môi trường phát triển tích hợp dùng cho lập trình trên máy tính. Nó chứa một không gian làm việc cơ sở và một hệ thống plug-in mở rộng để tùy chỉnh môi trường

Eclipse hỗ trợ các công cụ thao tác các kiểu nội dung bất kỳ nhờ vậy lập trình viên hoàn toàn có thể tự mình phát triển các phần mềm dựa trên các ngôn ngữ lập trình phổ biến Java, Python… Bên cạnh đó, Eclipse cũng hỗ trợ tốt cho người dùng trên nhiều hệ điều hành khác nhau. Nhờ vậy mà Eclipse được nhiều người biết tới và đánh giá cao trong lĩnh vực lập trình.

Bằng phần mềm Eclipse, Google đã phát triển thành công một bộ công cụ phát triển ứng dụng cho di động. 

Xem thêm 3PL Là Gì? Top Các Công Ty 3PL Ở Việt Nam

7. Brackets

Tương tự như những phần mềm lập trình khác Brackets có đầy đủ các tính năng cơ bản cho lập trình viên. Nhưng khác với các công cụ khác mặc dù là công cụ lập trình nhưng Brackets lại hướng đến nhóm đối tượng là những người chuyên thiết kế website.

Phần mềm hỗ trợ sử dụng các ngôn ngữ thiết kế như HTML, CSS hay Javascript.

Trên đây là các phần mềm dành cho dân IT mà một lập trình viên nên biết. Hy vọng sau khi tham khảo những phần mềm trên thì bạn sẽ tìm được cho mình công cụ phù hợp để tạo ra những sản phẩm tuyệt vời cho mình và người dùng.

Intern: Nguyễn Thị Huyền Trang


Tuyển Dụng Nhân Tài IT Cùng TopDev
Đăng ký nhận ưu đãi & tư vấn về các giải pháp Tuyển dụng IT & Xây dựng Thương hiệu tuyển dụng ngay!
Hotline: 028.6273.3496 – Email: contact@topdev.vn
Dịch vụ: https://topdev.vn/page/products

 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

Wisard – Công cụ tạo lập kịch bản kiểm thử Selenium

wisard
Wisard – Công cụ tạo lập kịch bản kiểm thử Selenium

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

Trong bài trước, mình đã giới thiệu với các bạn về Selenium IDE, một công cụ mặc định của Selenium cho phép chúng ta có thể record/playback các kịch bản kiểm thử, đồng thời chuyển các kịch bản kiểm thử này thành các mã nguồn với các ngôn ngữ khác nhau như C#, Java, Python, Ruby…

Trong phần này, mình sẽ giới thiệu với các bạn một công cụ khác tương đương với IDE – Wisard – có thể record với Internet Explorer hoặc Chrome bên cạnh Firefox, với một cách Record khác – không có Playback. Tuy nhiên, công cụ này chỉ có thể kết xuất ra mã nguồn với Java với JUnit Framework mà thôi.

  Firefox profile preferences để download file với Selenium Webdriver
  Các kiểu “đợi chờ” trong Selenium Webdriver: Implicit wait, Explicit wait và Fluent wait

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

Chào mừng các bạn đến với Wisard – Web Internal Structure Action RecorDer

Wisard là một phần mềm với nền tảng là Java, vậy nên đầu tiên, chúng ta cần có môi trường Java.

Sau đó, chúng ta chỉ cần download Wisard ở đây.

Sử dụng Wisard

Để Wisard có thể tương tác được với trình duyệt, chúng ta cần phải để tập tin thực thi Wisard cùng thư mục với Selenium Server. Các bạn có thể xem lại cách thiết lập và kiểm tra Selenium Server ở các bài trước.

Bước kế tiếp là khởi động chương trình Wisard

Wisard

Như hình trên, chúng ta có bốn chức năng chính trong Wisard: Elements, Generated Code, Settings, About.

Elements: Trong tab này, Wisard sẽ liệt kê toàn bộ các đối tượng UI hiện có trong trang web hiện hành.

Generated Code: Trong tab này, Wisard cho chúng ta mã nguồn Java với những hành động mà chúng ta tương tác trên browser.

Settings: Tab này dùng để thiết lập các thuộc tính cho chương trình Wisard.

About: Tab này đơn thuần chỉ là thông tin cơ bản của Wisard mà thôi.

Record với Wisard

Nhấn vào nút Inspect, Wisard sẽ bắt đầu bắt các đối tượng UI đang có trên trang hiện tại. Nhấn chuột phải vào từng đối tượng UI, một danh sách các hành động mà đối tượng UI có thể đáp ứng được.

Wisard Doi Tuong UI

Sau khi trang web mới được cập nhật, nhấn nút Refresh sẽ cho chúng ta một danh sách đối tượng UI mới trên trang mới.

Chúng ta tiếp tục tương tác với trang mới và cập nhận danh sách đối tượng UI với các trang tiếp theo.

Sao chép mã nguồn

Chuyển sang tab Generated Code, mã nguồn Java đã sẵn sàng cho chúng ta sao chép và sử dụng. Các bạn chú ý là mã nguồn này là ở JUnit Framework.

Wisard Source Code

Thiết lập hệ thống

Trong tab Settings, Wisard cho phép thiết lập trang mặc định ban đầu hay lựa chọn trình duyện để record.

Wisard Settings


Nhìn chung mà nói thì đây không hẳn là một công cụ hay để tạo lập một kịch bản kiểm thử tự động với Selenium. Bản thân công cụ này không hỗ trợ việc playback sau khi record là một thiếu sót khá rõ ràng. Bên cạnh đó, việc chỉ hỗ trợ ngôn ngữ Java làm mất đi tính đại chúng của Selenium. Dù cách tiếp cận để bắt và tương tác với đối tượng UI khá đặc biệt, nhưng số lượng cách tương tác với đối tượng UI lại quá ít ỏi.

Điểm sáng làm cho Wisard trở nên đặc biệt hơn IDE chính là khả năng record với Internet Explorer hoặc Chrome.

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

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

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

Promise, Async/Await và Map/Reduce

async

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

Có một cái sai mà người ta thường hay mắc phải khi làm việc với async/await, đó là khi kết hợp nó với các hàm Array.map/Array.reduce, họ hiểu sai tác dụng của async/await, dẫn tới việc kết quả trả về không như ý.

  6 lý do Async/Await của Javascript đánh bại Promises
  Lưu ý khi chạy async function với vòng lặp

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

Giả sử ta có hàm parseUrl(<url>) nhận vào một chuỗi (là địa chỉ của một RSS feed), và trả về danh sách các item có trong RSS feed đó, mà ở đây chúng ta biểu diễn bằng một mảng [article], kết quả trả về thông qua Promise, nội dung hàm này như nào thì không ảnh hưởng nhiều tới bài viết, nên chả cần ghi ra làm gì:

// parseUrl :: string -> Promise [article]

Khi sử dụng await, ta có thể lấy kết quả của Promise đó như thế này, có thể minh họa bằng sơ đồ:

await parseUrl("https://thefullsnack.com/rss.xml");

Tiếp theo, là hàm getArticles([urls]) nhận vào một mảng nhiều RSS feed URL, và trả về nhiều mảng chứa danh sách các item tương ứng với từng feed, theo như hiểu biết về cách làm việc với Promise thông qua hàm await như trên, mà await chỉ sử dụng được bên trong các hàm async, vậy thì thêm async vào, ta có thể dễ dàng implement như sau:

// getArticles :: [string] -> [[article]]
async function getArticles(sources) {
    return await sources.map(async (url) => {
        return await parseUrl(url);
    });
}

Chúng ta expect hàm getArticles hoạt động theo sơ đồ bên dưới:

Tuy nhiên, khi chạy, thì hàm trên không trả về kết quả như mong đợi:

getArticles([
    "https://thefullsnack.com/rss.xml",
    "https://news.ycombinator.com/rss"
])

// Output:

[ Promise { [ [Object] ] },
  Promise { [ [Object] ] } ]

Chúng ta tưởng rằng, sử dụng lệnh await sẽ giúp trả về kết quả được resolved của một Promise, mà cụ thể ở đây bên trong hàm source.map(), chúng ta có thể nhận được một mảng chứa kết quả của các promise parseUrl. Nhưng trong trường hợp này, kết quả trả về lại là các Promises, vậy chúng ta đã làm sai ở chỗ nào?


Hãy xem một hàm async hoạt động ra sao:

async function increase(a) {
    return a + 1;
}

increase(1);

// Output:

Promise { 2 }

Hàm async luôn trả về một Promise, viết type singature theo kiểu mấy ngôn ngữ functional là:

// async increase :: number -> Promise number

Còn từ khóa await thì có tác dụng dừng việc thực thi code lại và chờ lấy trực tiếp giá trị trả về trong một Promise:

// await Promise number -> number
let two = await increase(1);
// two = 2

Nếu không dùng await thì ta phải dùng .then(), là cách truyền thống để nhận giá trị trả về của một Promise, giá trị trả về chỉ có thể sử dụng được trong scope (phạm vi) của .then(), và không biết sử dụng nó trực tiếp ở scope hiện tại như thế nào luôn.

let two = increase(1).then(n => {
   // n = 2
   ...
})
// two = Promise

Quay trở lại ví dụ đầu bài, hãy cùng xem lại hàm getArticles trả về kết quả như thế nào. Chúng ta sẽ đi từ trong ra ngoài.

async function getArticles(sources) {
    return await sources.map(async (url) => {
        return await parseUrl(url);
    });
}

Đầu tiên là hàm xử lý dữ liệu trong khối lệnh sources.map():

// async f :: string -> Promise [article]
async (url) => {
    return await parseUrl(url);
}

Ngay tại đây chúng ta thấy, hàm callback của sources.map() trả về một Promise chứ không phải là một mảng các article như dự tính ban đầu.

■  Mặc dù chúng ta sử dụng await để lấy kết quả trả về từ parseUrl() (vốn là một Promise), tuy nhiên vì nằm trong một hàm async, kết quả này rốt cuộc cũng bị wrap lại vào bên trong một Promise. 

Điều này dẫn đến việc, kết quả của câu lệnh map là một mảng các Promises, thay vì là mảng của các mảng [article] như ta nghĩ.

Và kết quả là hàm getArticles() trả về một mảng các Promises, và mỗi một Promise trong mảng này lại chứa các [article] của chúng ta:

Vậy nên, cách để giải quyết vấn đề trên là, sử dụng Promise.all() để lấy toàn bộ kết quả trả về từ các Promise có trong sources.map(), sau đó mới đưa ra cho hàm getArticles():

async function getArticles(sources) {
    let promises = sources.map(async (url) => {
        return await parseUrl(url);
    });
    return await Promise.all(promises);
}

let url = await getArticles([
    "https://thefullsnack.com/rss.xml",
    "https://news.ycombinator.com/rss"
])

// Output:

[ [ { title: 'Giấy với bút',
      link:  'https://thefullsnack.com/posts/paper-and-pen.html' },
    { title: 'Vài ghi chép về V8 và Garbage Collection',
      link:  'https://thefullsnack.com/posts/javascript-v8-notes.html' },
    ...
  ],
  [ { title: 'Elon Musk Accused by SEC of Misleading Investors in August Tweet',
      link:  'https://news.ycombinator.com/item?id=18088099' },
    { title: 'People can die from giving up the fight',
      link:  'https://news.ycombinator.com/item?id=18083509' },
    ...
  ] ]

Bài học rút ra ở đây là gì? Đó là, luôn luôn đọc kĩ tài liệu trước khi cắm đầu sử dụng, và quan trọng nhất là không được đoán mò async/await, cũng giống như mọi khái niệm khác trong JavaScript, luôn cực kì rắc rối và khó hiểu cho tới chừng nào chúng ta… hiểu nó.

Mình biết điều này vì chính mình cũng đã lười đọc tài liệu, dẫn đến làm sai, nên mới có bài viết này 

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 CNTT hấp dẫn trên TopDev

Todo List: Cập nhật dữ liệu ứng dụng trong Laravel

cập nhật dữ liệu trong laravel
Todo List: Cập nhật dữ liệu ứng dụng trong Laravel

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

Video trong bài viết

Trong quá trình nhập liệu, rất có thể người dùng nhập sai do vậy việc chỉnh sửa, cập nhật lại bản ghi là cần thiết. Trong bài hôm nay chúng ta cùng tìm hiểu cách cập nhật các thông tin về công việc trong ứng dụng Todo List.

Hiển thị form cập nhật dữ liệu

Trong bài trước chúng ta có Quy trình 3 bước để tạo ra một chức năng, chúng ta sẽ áp dụng quy trình này cho chức năng cập nhật dữ liệu.

Đường dẫn chỉnh sửa dữ liệu cho từng todo cụ thể sẽ có dạng /todos/x/edit với x chính là ID của todo, ví dụ /todos/3/edit để chỉnh sửa dữ liệu cho todo có id = 3. Chức năng chỉnh sửa dữ liệu cũng giống chức năng tạo mới bản ghi, nó sẽ chia thành 2 phần việc:

  • Phần 1: Hiển thị form nhập liệu có dữ liệu có sẵn của todo
  • Phần 2: Cập nhật dữ liệu vào database

Chúng ta sẽ áp dụng quy trình 3 bước cho từng phần.

Xem thêm nhiều chương trình tuyển dụng Laravel hấp dẫn trên TopDev

Bước 1: Đăng ký đường dẫn

Mở routes/web.php và đăng ký đường dẫn mới:

Route::get('/todos/{todo}/edit', 'TodosController@edit');

Bước 2: Xử lý nghiệp vụ trong Controller

Trong TodosController chúng ta tạo ra một phương thức edit():

public function edit($todoId)
{
    return view('todos.edit')->with('todo', Todo::find($todoId));
}

Bước 3: Hành động kết thúc.

Ở phương thức edit() chúng ta không có một nghiệp vụ nào mà chỉ đơn giản là trả về một view tên là todos.edit. Chúng ta tạo view resources/views/todos/edit.blade.php:

@extends('layouts.app')

@section('content')
    <h1 class="text-center my-5">Edit Todos</h1>

    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card card-default">
                <div class="card-header">Edit todo</div>
                <div class="card-body">
                    @if($errors->any())
                        <div class="alert alert-danger">
                            <ul class="list-group">
                                @foreach($errors->all() as $error)
                                    <li class="list-group-item">
                                        {{ $error }}
                                    </li>
                                @endforeach
                            </ul>
                        </div>
                    @endif
                    <form action="/todos/{{ $todo->id }}/update-todos" method="POST">
                        @csrf
                        <div class="form-group">
                            <input type="text" class="form-control" placeholder="Name" name="name" value="{{ $todo->name }}">
                        </div>
                        <div class="form-group">
                            <textarea name="description" placeholder="Description" cols="5" rows="5" class="form-control">{{ $todo->description }}</textarea>
                        </div>
                        <div class="form-group text-center">
                            <button type="submit" class="btn btn-success">Update Todo</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
@endsection

Xem qua chúng ta thấy view edit này giống với view create, tuy nhiên có một số điểm khác như sau:

  • Dữ liệu form được gửi đến đường dẫn /todos/{{ $todo->id }}/update-todos với phương thức POST.
  • Các dữ liệu được mặc định lấy từ database và đưa vào các ô nhập liệu.

Vào thử một đường dẫn để xem form sửa đổi dữ liệu thế nào, ví dụ http://localhost:8000/todos/1/edit

Lưu dữ liệu thay đổi vào database

Chúng ta đã hoàn thành 1/2 công việc, phần còn lại chúng ta sẽ bắt dữ liệu gửi lên từ form, thực hiện kiểm tra dữ liệu và lưu chúng vào database. Công việc này cũng hoàn thành bởi quy trình 3 bước:

Bước 1: Đăng ký URL

Trong form thay đổi thông tin công việc, dữ liệu được gửi đến đường dẫn /todos/{{ $todo->id }}/update-todos với phương thức gửi là POST, ví dụ như /todos/1/update-todos, /todos/2/update-todos… Chúng ta đăng ký URL này vào routes/web.php:

Route::post('/todos/{todo}/update-todos', 'TodosController@update');

Bước 2: Xử lý nghiệp vụ trong TodosController

Tạo phương thức update() trong TodosController:

public function update($todoId)
{
    $this->validate(request(), [
        'name' => 'required|min:6|max:12',
        'description' => 'required'
    ]);

    $data = request()->all();

    $todo = Todo::find($todoId);
    $todo->name = $data['name'];
    $todo->description = $data['description'];

    $todo->save();
    return redirect('/todos');
}

Đoạn code kiểm tra dữ liệu nhập vào form đã được chúng ta tìm hiểu trong bài trước, tiếp đến với ID của Todo có được, chúng ta tìm Todo này và cập nhật dữ liệu mới vào đối tượng tìm được.

Hành động kết thúc

Ở đây chỉ đơn giản là chuyển hướng ứng dụng về trang danh sách todos với redirect().

Kết thúc

Bài học đã kết thúc, chúng ta có một bài khá đơn giản vì nó tương đối giống với bài tạo bản ghi dữ liệu. Một lần nữa, bạn sẽ thấy Quy trình 3 bước có tác dụng xuyên suốt trong Khóa học Laravel cơ bản này. Còn một việc mà chúng ta quên mất là thêm một nút Edit vào Todo cụ thể giúp chuyển hướng đến form chỉnh sửa dữ liệu cho Todo. Mở resources/views/todos/show.blade.php và thêm vào nút Edit:

<a href="/todos/{{ $todo->id }}/edit" class="btn btn-info my-2">Edit</a>

Ngay dưới phần mô tả về công việc.

Source code: Bài 13 – Cập nhật dữ liệu

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

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

Giới thiệu Json Web Token (JWT)

json web token
Giới thiệu Json Web Token (JWT)

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

Authentication là phần không thể thiếu trong bất kỳ hệ thống nào. Phương pháp authentication đơn giản và hay được sử dụng trong các ứng dụng web đó là user gửi thông tin username và password lên server. Sau khi server chứng thực thành công sẽ tạo ra một chuỗi session_id và lưu vào session hay database ở phía server. Sau đó, gửi session_id này về client và được client lưu trên cookie. Nhưng với các ứng dụng trên mobile và các ứng dụng web SPA (Single Page Application) thì cần có cơ chế authentication tốt hơn khi mà chúng ta phải thiết kế các RESTful api (stateless) thì server không thể đảm nhiệm việc lưu trạng thái phiên làm việc của user. Một trong những phương pháp tốt để giải quyết vấn đề này là sử dụng JSON Web Token (JWT).

JSON Web Token (JWT) là gì?

JSON Web Token (JWT) là 1 tiêu chuẩn mở (RFC 7519), định nghĩa cách thức truyền tin an toàn giữa các ứng dụng bằng một đối tượng JSON. Dữ liệu truyền đi sẽ được mã hóa và chứng thực, có thể được giải mã để lấy lại thông tin và đánh dấu tin cậy nhờ vào “chữ ký” của nó. Phần chữ ký của JWT sẽ được mã hóa lại bằng HMAC hoặc RSA.

  Cách tạo REST API với JSON Server
  Chuyển đổi JSON qua CSV sử dụng thư viện Jackson

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

JWT có 2 đặc điểm:

  • Gọn nhẹ (compact): JWT có thể được truyền đi thông qua URL, hoặc qua giao thức POST, hay gán vào bên trong phần HTTP Header. Kích thước nhỏ hơn ứng với công việc truyền tải sẽ nhanh hơn. Dưới đây là cách thức truyền token vào trong HTTP Header sử dụng Bearer Schema:

Authorization: Bearer <token>

  • Tự đóng gói (self-contained): Payload của JWT đã chứa các thông tin cần thiết về user (thay vì phải truy vấn cơ sở dữ liệu nhiều lần).

JSON Web Token (JWT) gồm những thành phần nào?

JWT bao gồm 3 phần, ngăn cách nhau bởi dấu “.“, gồm:

  • Header
  • Payload
  • Signature

Chuỗi JWT: header.payload.signature

Ví dụ chuỗi JWT:

Trong đó:

– Header: là Base64URL encoded của json.

– Payload: là Base64URL encoded của json.

– Signature: là kết quả của function Algorithm:

Header

Gồm 2 phần: type của token, giá tri là JWT, và phương thức mã hóa (HMAC SHA256 hay RSA).

JSON object này sau đó được mã hóa Base64URL.

Payload

Payload chức các thành phần gọi là claim. Các claim này chứa các thông tin về đối tượng (thường là user), và các meta data của token. Server sẽ dùng các thông tin từ các claim này để có thể thực hiện authentication. Có 3 loại claim: reserved, public và private.

Reserved claim

Là những thông tin đã được quy định ở trong IANA JSON Web Token Claims registry. Các claim này là không bắt buộc phải có trong token, nhưng có thể cung cấp các thông tin hữu ích. Các claim này chỉ gồm 3 ký tự vì mục đích giảm kích thước của Token.

  • iss (issuer): tổ chức phát hành token.
  • sub (subject): chủ đề của token.
  • aud (audience): đối tượng sử dụng token.
  • exp (expired time): thời điểm token sẽ hết hạn.
  • nbf (not before time): token sẽ chưa hợp lệ trước thời điểm này.
  • iat (issued at): thời điểm token được phát hành, tính theo UNIX time.
  • jti: JWT ID.

Public claim

Là những claim được định nghĩa 1 cách công khai bởi những bên sử dụng JWT. Chúng nên được quy định ở trong IANA JSON Web Token Registry hoặc là 1 URI có chứa không gian tên không bị trùng lặp. Ví dụ:

{
"https://gpcoder.com/jwt_clams/user_role" : "admin"
}

Private claim

Là các claim tự định nghĩa và chỉ các thành phần liên quan của hệ thống hiểu được. Ví dụ:

{
"email":"gpcodervn@gmail.com",
"name":"gpcoder"
}

Signature

Signature được tạo ra bằng cách dùng phương pháp mã hóa được chỉ định ở header để mã hóa nội dung encode của header, payload, cùng với chuỗi khóa bí mật.

Phương thức mã hóa có thể là HMAC hay RSA:

  • HMAC: đối tượng khởi tạo JWT (token issuer) và đầu nhận JWT (token verifier) sử dụng chung 1 mã bí mật để mã hóa và kiểm tra.
  • RSA: sử dụng 1 cặp key, đối tượng khởi tạo JWT sử dụng Private Key để mã hóa, đầu nhận JWT sử dụng Public Key để kiểm tra.

Như vậy với HMAC, cả 2 phía đều phải chia sẻ mã bí mật cho nhau, và đầu nhận JWT hoàn toàn có thể khởi tạo 1 mã JWT khác hợp lệ dựa trên mã bí mật đó. Còn với RSA, đầu nhận sử dụng Public Key để kiểm tra nhưng không thể khởi tạo được 1 JWT mới dựa trên key đó. Vì vậy mã hóa sử dụng RSA giúp cho việc bảo mật chữ ký tốt hơn khi cần chia sẻ JWT với nhiều đối tượng khác nhau.

Ví dụ: nếu header chỉ định dùng thuật toán mã hóa HMAC SHA256, và phía server dùng chuỗi khóa bị mật là ‘secret’ thì signature sẽ được tạo ra như sau:

HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload), "secret"
)

Signature được dùng để chứng thực là token JWT là đúng và nội dung của token đã không được thay đổi.

JSON Web Token (JWT) hoạt động như thế nào?

  • Application hoặc client gửi thông tin chứng thực lên server (có thể là username/ password).
  • Khi đã chứng thực thành công, authorization server sẽ trả về một access token (JWT) cho client.
  • Phía Client sẽ gửi token này kèm theo request để truy cập resource. Thông thường token được gửi trong header: Authorization: Bearer <token>

Khi nào nên dùng JSON Web Token?

Dưới đây là một vài kịch bản thích hợp với JWT:

  • Chứng thực: Đây là kịch bản phổ biến nhất cho việc sử dụng JWT. Một khi người dùng đã đăng nhập vào hệ thống thì những request tiếp theo từ phía người dùng sẽ chứa thêm mã JWT, cho phép người dùng quyền truy cập vào các đường dẫn, dịch vụ, và tài nguyên mà cần phải có sự cho phép nếu có mã Token đó. Phương pháp này không bị ảnh hưởng bởi Cross-Origin Resource Sharing (CORS) do nó không sử dụng cookie.
  • Trao đổi thông tin: JWT là 1 cách thức không tồi để truyền tin an toàn giữa các thành viên với nhau, nhờ vào phần “chữ ký” của nó. Phía người nhận có thể biết được người gửi là ai thông qua phần chữ ký. Ngoài ra, chữ ký được tạo ra bằng việc kết hợp cả phần header, payload lại nên thông qua đó ta có thể xác nhận được chữ ký có bị giả mạo hay không.

Tại sao sử dụng JWT?

  • Performance : JWT gọn nhẹ và đơn giản. Chuỗi token sinh ra ngắn gọn đủ để chứa các thông tin cần thiết (nếu session thì cần đọc ở storage hoặc database). Và bất kỳ ai cũng có thể dễ dàng hiểu, áp dụng được JWT
  • Stateless: Rất phù hợp để áp dụng xây dựng stateless API, bởi token đã chứa đầy đủ các thông tin cần thiết để thực thi authentication.
  • Việc apply ở phía client rất đơn giản, cho dù nền tảng web hay mobile.
  • Dễ dàng mở rộng server hơn. Điều này là nhờ tính stateless của JWT, server không cần phải lưu session state, nên trong trường hợp phía server sử dụng cơ chế load balancing, bất kỳ máy server nào cũng có thể handle request và vẫn có được state của user thay vì chỉ server mà user đã login.
  • Security: Vì JWT không sử dụng cookie nên ta không cần phải lo về vấn đề CSRF. Nhưng JWT tốt nhất cần được sử dụng kèm với HTTPS.
  • Reusable: Chúng ta có thể có nhiều system chạy các nền tảng khác nhau có thể cùng sử dụng 1 JWT để thực hiện authenticate user. Điều này là khó hoặc không thể nếu state được lưu giữ phía server.

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 việc làm IT hấp dẫn trên TopDev

TOP 7 Yêu Cầu Tuyển Dụng Của Các Công Ty Nước Ngoài

yêu cầu tuyển dụng của các công ty nước ngoài

Ai trong chúng ta cũng có những định hướng nghề nghiệp riêng, trong đó việc lựa chọn môi trường doanh nghiệp nào để gắn bó là một yếu tố cần xem xét kỹ càng. Công ty nước ngoài từ lâu đã trở thành môi trường làm việc lý tưởng cho những bạn trẻ năng động, muốn thử thách bản thân. Để có được môi trường làm việc mơ ước, bạn cần nắm vững và trang bị cho mình những yêu cầu tuyển dụng của các công ty nước ngoài.

yêu cầu tuyển dụng của các công ty nước ngoài
7 yêu cầu tuyển dụng của những công ty nước ngoài bạn cần biết

1. Nhân tố con người – một trong các yêu cầu tuyển dụng của các công ty nước ngoài

Đối với doanh nghiệp nước ngoài thì yếu tố con người là điều kiện tiên quyết trong quá trình tuyển dụng. Những yếu tố như tính cách, kỹ năng mềm, năng lực cá nhân, kinh nghiệm thực tế,… sẽ giúp bạn vượt qua những thử thách mà không trường lớp hay bằng cấp nào có thể dạy bạn. 

Đặc biệt, việc bạn thể hiện được cá tính, phẩm chất cá nhân của bản thân trước nhà tuyển dụng là vô cùng quan trọng. Bởi vì bằng cấp có thể học, kỹ năng có thể trau dồi, kinh nghiệm có thể trải nghiệm mà có,… nhưng phẩm chất mỗi người thì chỉ có một. Giữa muôn vàn ứng cử viên giống nhau, nếu bạn thành công tạo sự nổi trội, khác biệt hoá cá nhân với những phẩm chất tốt đẹp thì khả năng được lựa chọn và thành công tại công ty nước ngoài là rất lớn.

Xem thêm TOP Các Công Ty Phần Mềm Nước Ngoài Tại Việt Nam

2. Không quá chú trọng đến bằng cấp

Bằng cấp đôi khi bị đánh giá quá cao trong quá trình tuyển dụng khi nó được ví như tấm vé thông hành để bạn được nhận vào bất cứ công ty nào. Tuy nhiên, công ty nước ngoài lại không quá coi trọng vấn đề này, đối với họ năng lực con người mới là quan trọng nhất. Chính vì thế, đừng tự ti vì bằng cấp không cao mà ngay từ bây giờ, hãy trang bị cho bản thân những kĩ năng, phẩm chất phù hợp với yêu cầu tuyển dụng của các công ty nước ngoài.

yêu cầu tuyển dụng
Hãy trang bị những kĩ năng, phẩm chất phù hợp thay vì lo lắng về bằng cấp

3. Yêu cầu tuyển dụng của các công ty nước ngoài – Có mục tiêu nghề nghiệp rõ ràng

Mục tiêu nghề nghiệp rõ ràng chính là yếu tố giúp bạn biết mình cần làm gì, cố gắng như thế nào để đạt được mục tiêu đề ra. Bạn sẽ không thể thuyết phục nhà tuyển dụng khi bạn không có mục tiêu, mục đích để bám vào, đặc biệt trong môi trường đầy cạnh tranh, áp lực tại công ty nước ngoài. 

Hơn thế nữa, biết được mục tiêu nghề nghiệp rõ ràng còn giúp bạn quyết định công ty có thực sự phù hợp với những dự định tương lai của bản thân không.

  'Toát mồ hôi' phỏng vấn tuyển dụng vào Apple

4. Thể hiện được tố chất lãnh đạo

Mặc dù bạn ứng tuyển vị trí nhân viên trong công ty nước ngoài, nhưng tố chất lãnh đạo vẫn là yếu tố được nhà tuyển dụng ưu tiên tìm kiếm ở bạn. Một điều chắc chắn bạn sẽ phải làm việc nhóm, dự án, hoặc có những dự án bạn chính là người đề xuất và thực hiện. Vì thế, nếu không có tố chất lãnh đạo bạn sẽ rất khó làm tốt công việc của mình, luôn bị lép vế khi tham gia vào công việc tập thể.

Bên cạnh đó, tố chất lãnh đạo sẽ giúp bạn thể hiện bản thân tốt hơn, cơ hội thăng tiến và gắn bó đóng góp lâu dài cho công ty nhiều, lâu hơn. Ngoài ra, tố chất lãnh đạo còn cho thấy bạn có khả năng lãnh đạo chính bản thân luôn tiến về phía trước để đạt được mục đích đề ra. 

5. Sự quyết tâm và sẵn sàng học hỏi

Khi ứng tuyển vào công ty nước ngoài, bạn cần có 1 lòng quyết tâm lớn, ý chí thép trước những thử thách, khó khăn trước mắt, không ngại khó, ngại khổ. Đặc biệt, trong môi trường đa văn hoá, có rất nhiều rào cản về văn hoá, ngôn ngữ,… bạn cần mang tâm thế sẵn sàng học hỏi, tiếp nhận những điều mới. 

  3 chỉ số quan trọng đánh giá quy trình tuyển dụng

Luôn chịu khó học hỏi từ những đồng nghiệp và sếp, cởi mở với những góp ý đóng góp và nghiêm túc xem xét cải thiện bản thân từng ngày. Sẵn sàng nhận nhiệm vụ, đương đầu với khó khăn. Đây chính là những yếu tố giúp bạn trụ vững tại môi trường công ty đa quốc gia. 

6. Khả năng thích ứng nhanh

Trong thời đại ngày nay, mọi thứ đều thay đổi nhanh chóng. Khả năng thích ứng là kỹ năng giúp bạn hòa nhập, thích nghi nhanh chóng với sự thay đổi. Nếu bạn không có khả năng thích ứng nhanh với cái mới, bạn sẽ trở nên lạc hậu và bị đào thải ngay lập tức.

Điều này càng trở nên đúng với môi trường công ty đa quốc gia cực kỳ năng động, cạnh tranh cao. Ngay từ quá trình thực tập, công ty đã yêu cầu bạn khả năng thích ứng nhanh với công việc khi đảm nhận những công việc của một nhân viên thực thụ. Hơn nữa, những khác biệt trong ngôn ngữ, văn hoá, phong cách làm việc trong công ty đa quốc gia cũng buộc bạn phải có khả năng thích ứng nhanh.

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

7. Khả năng ngoại ngữ là yêu cầu tuyển dụng của các công ty nước ngoài

Một điều chắc chắn trong những công ty nước ngoài là bạn sẽ có sếp hoặc đồng nghiệp là người nước ngoài. Việc bạn có thể sử dụng tốt 1 ngôn ngữ trong giao tiếp là 1 yếu tố quan trọng vì nếu không thể giao tiếp, bạn sẽ không thể hiểu được người khác, cũng không thể thể hiện bản thân 1 cách tốt nhất.

 Chính vì thế, nếu bạn có mong muốn làm việc tại 1 công ty nước ngoài, ngay từ hôm nay hãy nỗ lực cải thiện khả năng tiếng anh của bản thân.

Để đáp ứng những yêu cầu tuyển dụng của các công ty nước ngoài, đòi hỏi từ bạn một quá trình chuẩn bị, rèn luyện bản thân chăm chỉ. TopDev đang cung cấp những jobs cực hot, cực xịn tại công ty nước ngoài mà bạn không thể bỏ lỡ. Click tại đây để xem chi tiết. Chúc bạn thành công!

Intern: Nguyễn Thị Ánh


Tuyển Dụng Nhân Tài IT Cùng TopDev
Đăng ký nhận ưu đãi & tư vấn về các giải pháp Tuyển dụng IT & Xây dựng Thương hiệu tuyển dụng ngay!
Hotline: 028.6273.3496 – Email: contact@topdev.vn
Dịch vụ: https://topdev.vn/page/products

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

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

cấu hình maven
Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Bài viết được sự cho phép của tác giả Trần Hữu Cương

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Để build các project Maven bằng Jenkins, ta cần cấu hình Maven, MAVEN_HOME cho Jenkins.

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Trên thanh menu bên trái, chọn Manage Jenkins > Global Tool Configuration

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Cuộn màn hình xuống dưới sẽ thấy phần Maven, Click vào nút Add Maven

  Hướng dẫn build java project, maven project trên Jenkins
  Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

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

Cách 1: Cấu hình Maven bằng Maven đã cài sẵn ở local

Cách này yêu cầu bạn đã cài maven trên máy.

(Xem lại: cài đặt maven trên ubuntu)

Để kiểm tra maven trên local, ta mở cửa sổ terminal (Ctrl + Alt + T) và gõ mvn --version

Ví dụ, mình đang cài maven ở folder /usr/share/maven

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Click Add Maven.

Bỏ chọn ô check box Install Automatically

Ô MAVEN_HOME nhập folder cài đặt maven, ví dụ /usr/share/maven

Đặt tên cho bản maven và click Save.

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Với cách này, Jenkins không cần cài đặt hoặc download mà sử dụng luôn Maven trên local nên bạn có thể build maven project ngay sau khi save.

Cách 2: Cài đặt Maven cho Jenkins tự động bằng cách download từ Apache

Click Add Maven

Chọn ô check box Install automatically

Chọn version của bản Maven muốn cài đặt.

Đặt tên cho bản maven và click nút Save.

Hướng dẫn cấu hình Maven cho Jenkins (Build Maven Project)

Với cách này, sau khi save, Jenkins sẽ phải download maven (quá trình này chạy ngầm) nên có thể bạn sẽ không thể build ngay được các project maven mà phải chờ quá trình download và cấu hình này chạy xong.

Okay, Done!

References: https://www.jenkins.io/doc/

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

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

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

Bàn về Problem Solving Skill

problem solving skill
Bàn về Problem Solving Skill

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

Hôm nay tình cờ lọt vào một cái động tào lao, bắt gặp một anh bạn nào đó, có lẽ là mới đi phỏng vấn một vị trí senior dev nào đó, rồi đăng lên cái dòng này:

Phỏng vấn thì tào lao hết sức, tuyển senior mà bắt test thuật toán, làm 1 đống bài test khó trong vòng 1h

Đoán chắc là anh bạn này phỏng vấn không được tốt cho lắm, điều này cũng dễ hiểu, vì chỉ cần đọc cái dòng trên thì cũng đủ thấy, anh bạn này đã apply nhầm vị trí. Với tất cả sự thiển cận, thiếu hiểu biết và non nớt cả về tư duy lẫn tư cách như thế, anh ta hoàn toàn không thể là một senior developer, hoặc nếu có đang mang danh senior ở một công ty nào đó, thì chẳng qua công ty đó đã dán cho cái mác senior, với một mục đích duy nhất là làm anh bạn đó trở nên có giá trị hơn trong mấy cái hợp đồng outsourcing của công ty. Thật là bất hạnh cho cả hai phía.

Tại vì sao, mà một senior developer lại có thể cảm thấy bị xúc phạm và phản ứng đến mức đó khi bị hỏi về thuật toán trong một cuộc phỏng vấn? Phải chăng là anh ta không hiểu lý do đằng sau của việc kiểm tra bằng thuật toán? Và chính xác thì anh bạn đó expect được phỏng vấn cái gì nếu không phải là thuật toán? Hay là muốn được hỏi về sự khác nhau về mặt cú pháp giữa ES6 và ES5 trong JavaScript??? Hỏi mấy cái đó để làm gì?

Đọc thêm một chút thì lại thấy:

Lúc gọi điện mình bảo không biết ruby on rails, Nhân sự khẳng định không quan trọng ngôn ngữ, quan trọng thuật toán =))

À, thì ra không những người đi phỏng vấn không biết mục đích, mà cả nhân sự cũng không rõ vì sao các engineer của công ty mình lại đặt ra các câu hỏi về thuật toán. Thế còn các bạn engineer đã và đang làm công việc phỏng vấn? Tại sao các bạn lại hỏi thuật toán?

  10 Kỹ năng quan trọng cần có của Front-end để tìm công việc dễ dàng hơn
  13 mẹo có thể giúp bạn rút ngắn thời gian phát triển kỹ năng lập trình

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


Lại nói về công việc hằng ngày của một developer, là gì? build thêm feature mới, hoặc build một sản phẩm hoàn toàn mới, nhưng cũng có thể là bảo trì (maintenance – tên gọi hoa mỹ của những hành động: fix bug, refactoring,…) sau khi sản phẩm được released. Dù bạn làm gì, thì cũng đều có thể gói gọn trong các bước sau:

  • Tiếp nhận và hiểu vấn đề: ở dây có thể là bug description, hoặc là yêu cầu của sản phẩm/feature,…
  • Phân tích vấn đề: nếu là bug, đây là bước điều tra các hiện tượng, tìm cách tái hiện (reproduce) lại bug đó, tìm hiểu nguyên nhân gốc rễ của vấn đề (root cause) và hướng giải quyết, nếu là build mới, đây là bước phân tích các yêu cầu kĩ thuật, search google để tìm các thư viện hỗ trợ nếu có (vì mình thường thấy nhiều bạn làm gì cũng search thư viện có sẵn, nên mình đoán cái đó nằm trong quy trình này, nên ghi vô luộn).
  • Lên phương án hành động: sau khi đã có đầy đủ các dữ kiện cần thiết, hiểu rõ được bản chất của vấn đề và hình dung được hướng giải quyết, thì chúng ta cần vạch ra kế hoạch từng bước để hiện thực hóa giải pháp đó, quan trọng hơn nữa, là phương án hành động này phải đảm bảo chỉ giải quyết vấn đề cần giải quyết  mà không làm ảnh hưởng đến những thứ khác.
  • Thực hiện: get  done
  • Rút kinh nghiệm: sau khi đã giải quyết xong vấn đề, đây là bước cực kì quan trọng, nhìn lại quá trình thực hiện và những sai lầm mắc phải, rút kinh nghiệm, nếu là vấn đề gì hay ho thì có thể viết thành blog cũng được :))

Nếu fail ở bất kì bước nào trong các bước trên đều dẫn tới thất bại trong việc giải quyết vấn đề.

If I had an hour to solve a problem I’d spend 55 minutes thinking about the problem and 5 minutes thinking about solutions.

Không đọc kĩ yêu cầu, bạn có thể sẽ giải quyết sai vấn đề. Không hiểu rõ vấn đề, bạn có thể sẽ chẳng bao giờ tìm ra nguyên nhân của nó, đừng nói tới hướng giải quyết. Không phân tích được đâu là nguyên nhân gốc rễ (root cause), giải pháp của bạn có thể sẽ là một giải pháp nửa vời, và tệ hơn là gây ra regression (có bao giờ bạn tự hỏi, tại sao fix bug hoài mà càng fix thì càng ra bug không?). Không có kế hoạch hành động cụ thể, bạn sẽ bị lệch hướng khi thực hiện và tai hại hơn là gây ảnh hưởng đến các thành phần khác của hệ thống. Giải quyết xong vấn đề là vứt nó qua một bên luôn không bao giờ nhìn lại, cứ thế bạn sẽ không bao giờ thu được tí kinh nghiệm gì ngoài việc làm nhiều rồi quen tay.

Một software engineer giỏi không phải là người biết nhiều kĩ thuật hay ngôn ngữ, mà là người có khả năng giải quyết vấn đề tốt.

Và muốn biết một người có khả năng giải quyết vấn đề tốt hay không, thì cách dễ nhất là giao cho họ một vấn đề nào đó để thử. Cũng giống như làm toán, việc giải các bài thuật toán là cơ hội tốt nhất để một người có thể phô diễn khả năng giải quyết vấn đề của mình, thông qua cách mà họ tiếp cận vấn đề, phân tích, giải thích và đưa ra phương hướng giải quyết, cuối cùng, là bước implement, test và đánh giá thuật toán.

Chắc chắn là có rất nhiều cách khác để thử kĩ năng giải quyết vấn đề, nhưng một bài thuật toán đơn giản là quá đủ cho một cuộc phỏng vấn chừng 45 phút.


Vậy mới thấy, không chỉ trong phỏng vấn, giải quyết vấn đề luôn là một kĩ năng cơ bản mà bất kỳ ai, dù làm bất kỳ công việc gì đều cần đến. Đối với nghề lập trình, chúng ta thường hay bị cuốn theo những thứ không cần thiết như vấn đề ngôn ngữ, công nghệ mà quên đi những thứ đáng ra rất cần thiết như là problem solving, hay những thứ công cụ không thể thiếu cho công việc giải quyết vấn đề (như là nền tảng kiến thức về computer science).

Và thật đáng buồn khi những bài viết như thế này, đưa ra lời khuyên về rèn luyện kĩ năng thuật toán, kiến thức nền tảng lại ít được like và share hơn những thứ vô bổ, đầy rẫy ngoài kia (như cái động tào lao có nói tới ở đầu bài)  đọc tới đây rồi cần làm gì thì tự hiểu đi nha

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

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

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

Import RAML vào Postman

import raml
Import RAML vào Postman

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

Sau khi định nghĩa API specs bằng RAML file, chúng ta có thể generate code để implement những API này sử dụng Spring framework và cũng có thể import nó vào Postman để thực hiện việc testing. Trong bài viết này, mình hướng dẫn các bạn cách import RAML vào Postman các bạn nhé!

  Chạy Postgresql trong Docker container
  Định nghĩa request body và response với RAML

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

Mình sẽ sử dụng tập tin RAML định nghĩa các API quản lý sinh viên như sau:

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

/students:
get:
responses: 
200:
body: 
application/json:
example: [{ "id":1, "code": "001", "name": "Khanh" }, { "id":2, "code": "002", "name": "Quan" }]
/{id}:
uriParameters:
id:
description: Id of the Student
type: string
example: "1"
get:
responses: 
200:
body: 
application/json:
example: { "id":1, "code": "001", "name": "Khanh" }
delete:
responses: 
200:
body: 
application/json:
example: { "message": "Student deleted!" }

Để import tập tin RAML này vào Postman, các bạn hãy nhấn nút Import trong cửa sổ Workspace chính của Postman:

Cửa sổ chọn tập tin cần import sẽ hiển thị ra như sau:

Có nhiều định dạng file được hỗ trợ bởi Postman như OpenAPI, RAML, GraphQL, cURL hay WADL như các bạn thấy. Để import, các bạn nhấp vào nút Upload Files. Lưu ý là nếu API specs của các bạn được định nghĩa trong nhiều tập tin khác nhau thì hãy chọn tab Folder trong cửa sổ trên thay vì tab File nhé.

Sau khi chọn tập tin student.raml chứa nội dung API specs mà mình định nghĩa ở trên, các bạn sẽ thấy kết quả như sau:

Nhấn Import các bạn nhé!

Nhấn Close.

Kết quả:

Các bạn có thể click các request để xem detail cho từng request, giống như thông tin chúng ta đã định nghĩa trong tập tin RAML.

Cho mỗi request, baseUrl sẽ được Postman cấu hình sử dụng sử dụng biến với giá trị ban đầu được chúng ta định nghĩa trong tập tin RAML như sau:

Tuỳ theo nhu cầu thì các bạn có thể thay đổi biến này trong phần Environment của Postman 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:

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

Sự Kiện Trực Tuyến DEV-EXPERIENCE | Headless Architecture: The Evolution Of Ecommerce Of SmartOSC

smartosc
Sự Kiện Trực Tuyến DEV-EXPERIENCE | Headless Architecture: The Evolution Of Ecommerce Of SmartOSC

Được thành lập vào năm 2006, SmartOSC là công ty tiên phong đi đầu trong việc cung cấp các giải pháp Thương mại điện tử đa kênh trên quy mô toàn cầu. Hiện công ty đã có mặt tại 6 quốc gia, bao gồm Mỹ, Úc, Singapore, Nhật Bản, Thái Lan và Việt Nam. 

Sự gia tăng mạnh mẽ của số lượng người dùng di động và các giải pháp công nghệ đã tạo nên những bước phát triển đột phá cho ngành TMĐT. Điều này cũng đòi hỏi các lập trình viên làm việc trong lĩnh vực e-Commerce phải thường xuyên cập nhật những kiến thức mới!

Từ góc nhìn công nghệ, với cấu trúc web truyền thống (monolithic), mỗi sự thay đổi về front-end đều dẫn đến sự thay đổi trong cấu trúc back-end và ngược lại. Bởi lẽ đó, mỗi quyết định về công nghệ đều trở nên rủi ro, phức tạp hơn khi ảnh hưởng trực tiếp đến toàn bộ hệ thống và cơ sở dữ liệu. Điều này gây ảnh hưởng không nhỏ đến bất kì website eCommerce nào khi bước vào giai đoạn tăng trưởng về sản phẩm lẫn traffic & người dùng.

Đây chính là thời điểm để Kiến trúc Headless xuất hiện và tỏa sáng. Với cấu trúc tích hợp các giao diện front-end đã tách rời vào các công cụ CMS thông qua API, khiến quá trình phát triển & triển khai diễn ra suôn sẻ và nhanh chóng hơn… kiến trúc Headless cung cấp giải pháp tối ưu hóa cho đội ngũ Lập trình viên – những người tạo ra các trải nghiệm kỹ thuật số. 

Tham gia ngay sự kiện trực tuyến “DEV EXPERIENCE | Headless Architecture: The Evolution of Ecommerce of SmartOSC” để tìm hiểu về mô hình kiến trúc Headless để giải quyết các khó khăn cũng như phục vụ tối đa cho nhu cầu của ngành eCommerce trong bối cảnh phát triển mạnh mẽ và sự gia tăng về số lượng người dùng như hiện nay.

Bạn nhận được gì khi tham gia sự kiện?

Sự kiện cung cấp những góc nhìn và kiến thức về xu hướng công nghệ mới mang tên “Headless”, giải đáp các nội dung về:

  • Kiến trúc Headless đã và đang làm thay đổi các dự án thương mại điện tử như thế nào? 
  • Những nền tảng TMĐT hàng đầu thế giới như Magento nhìn nhận về Kiến trúc Headless ra sao?
  • Kiến trúc Headless sử dụng các microservices trên nền điện toán đám mây thì cách thức để triển khai là gì? 
  • Bạn sẽ lựa chọn thích nghi với kiến trúc Headless hay sẽ trung thành với kiến trúc truyền thống? 
  • Những điểm khác biệt mà kiến trúc Headless có thể mang lại?
  • Kiến trúc Headless hoạt động như thế nào và có phù hợp với tất cả các loại hình dự án?

Diễn giả trong sự kiện

  • Chị Ngô Kiều Anh – Product Manager – Sutunam Vietnam

Với vốn kiến thức sâu rộng và thâm niên quản lý nhiều dự án đa dạng, chị Kiều Anh hiện sở hữu khối kinh nghiệm quan trọng ở hai mảng phát triển thị trường và nhân sự công nghệ. Với chị, việc tìm kiếm và cập nhật các giải pháp mới phù hợp cho từng loại hình luôn là ưu tiên hàng đầu của mỗi Project Manager. Headless eCommerce là xu hướng mà chị và đội ngũ Sutunam nhận định sẽ nâng cao giá trị cho các dự án thương mại điện tử và mang lại tỉ lệ chuyển đổi người dùng đáng kể trên mobile khi mà thị trường này ngày một trở nên cạnh tranh hơn.

  • Anh Tạ Hoàng Hải – Chief Executive Officer – SimiCart

Được biết đến với vai trò là CEO của SimiCart – đơn vị chuyên cung cấp các giải pháp Headless và sản phẩm lĩnh vực e-Commerce cho khách hàng. Dưới sự dẫn dắt của anh, đội ngũ Developers của Simicart đã chinh phục rất nhiều khách hàng đến từ nhiều quốc gia, lọt top 50 startup tiềm năng nhất Đông Nam Á và được lựa chọn vào chương trình Vườn ươm khởi nghiệp của Chính phủ Malaysia. 

  • Anh Nguyễn Quang Khuê – Senior Developer – SmartOSC

Trải qua nhiều năm chinh chiến trong lĩnh vực phát triển ứng dụng web thương mại điện tử cho các khách toàn cầu của SmartOSC, anh Khuê am hiểu rất sâu sắc về Quy trình phát triển cũng như phương pháp tiếp cận hợp lý để giải quyết các vấn đề ở góc nhìn công nghệ. Không những thế, anh còn là một trong những thành viên chủ chốt của Product Team trong các Dự án phát triển sản phẩm sử dụng kiến trúc Headless. 

  • Anh Phạm Long Quân – Panel Moderator

Hiện anh Quân đã có hơn 8 năm kinh nghiệm ở các vị trí Software Developer và Project manager tại các công ty lớn như: HiPT Group, FPT Software và hiện tại là SmartOSC. Anh đã từng đảm nhiệm các dự án trên multi-domain như: Ecommerce, Application Software, System software…

  • Chị Rosy Vũ – Panel Moderator

Chị Rosy Vũ có hơn 7 năm kinh nghiệm tại vị trí Project Manager tại các công ty lớn như: Jonckers, FPT Asia Pacific và hiện đang đảm nhiệm vị trí Delivery Success Manager tại SmartOSC. Bên cạnh đó, chị đã đạt được nhiều chứng chỉ Quốc tế danh giá như: Project Management Professional (PMP), Professional Scrum Product Owner I (PSPOI), AWS Certified Cloud Practitioner (CLF), Professional Scrum Master I.

  • Anh Trần Công Toàn – Senior Developer – SmartOSC

Đảm nhận vai trò kiến trúc sư giải pháp về eCommerce hơn 10 năm, anh Toàn có niềm đam mê trong việc xây dựng các sản phẩm thương mại điện tử có tính năng nhân rộng và hiệu quả cao.

  • Anh Dương Quốc Việt – Senior Developer – Magestore

Với hơn 7 năm kinh nghiệm trong lĩnh vực thương mại điện tử sử dụng Magento, hiện anh là thành viên chủ chốt trong team phát triển sản phẩm của Magestore, với sản phẩm  PWA POS theo kiến trúc Headless. 

Agenda sự kiện

DEV – EXPERIENCE | Headless Architecture: The Evolution of eCommerce là sự kiện dành cho các bạn Dev – đặc biệt là các bạn Web Developer đang sử dụng PHP/ Magento với Agenda cụ thể:

>>> Đăng ký ngay: https://topdev.vn/s/649HoiZW

REST Web service: Upload và Download file với Jersey 2.x

rest web service
REST Web service: Upload và Download file với Jersey 2.x

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, chúng ta đã cùng tìm hiểu cách xây dựng ứng dụng CRUD với RESTful Web service. Trong bài này, chúng ta cùng tìm hiểu cách upload/ download file với RESTful sử dụng Jersey version 2.x như thế nào.

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

Tạo Jersey project

Chúng ta sẽ sử dụng lại project Jersey 2.x ở bài trước.

Để có thể upload/ download file với Jersey, chúng ta khai báo thêm thư viện sau trong file pom.xml.

1
2
3
4
5
6
<!-- 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>

Đăng ký sử dụng MultiPartFeature: chúng ta có thể sử dụng một trong 2 cách sau:

  • Thêm cấu hình trong file JerseyServletContainerConfig.java
  • Thêm cấu hình trong file web.xml

JerseyServletContainerConfig.java

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;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
//Deployment of a JAX-RS application using @ApplicationPath with Servlet 3.0
//Descriptor-less deployment
import org.glassfish.jersey.server.ResourceConfig;

public class JerseyServletContainerConfig extends ResourceConfig {
public JerseyServletContainerConfig() {
// if there are more than two packages then separate them with semicolon
packages("com.gpcoder.api");
register(new LoggingFeature(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), Level.INFO,
LoggingFeature.Verbosity.PAYLOAD_TEXT, 10000));
register(JacksonFeature.class);
register(MultiPartFeature.class);
}
}

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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>RESTful CRUD Example by gpcoder</display-name>
    <servlet>
        <servlet-name>jersey2-serlvet</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>
        <init-param>
                    <param-name>jersey.config.server.provider.classnames</param-name>
                    <param-value>org.glassfish.jersey.filter.LoggingFilter;
                        org.glassfish.jersey.jackson.JacksonFeature;
                        org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
            </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jersey2-serlvet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

Tạo Java REST Web service cho phép upload/ download file với Jersey2

FileService.java

Tạo file FileUploadResponse.java, file này chứa thông tin trả về cho Client sau khi đã upload thành công:

package com.gpcoder.model;

import java.util.Date;

import lombok.Data;

@Data
public class FileUploadResponse {

private String fileName;
private Date createdDate;
private long fileSizeInByte;
}

Tạo REST web service cho phép upload/ download file:

package com.gpcoder.api;

import java.io.File;
import java.io.InputStream;
import java.util.Date;

import javax.activation.MimetypesFileTypeMap;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;

import com.gpcoder.model.FileUploadResponse;
import com.gpcoder.utils.FileUtils;

//URI:
//http(s)://:(port)/// //http://localhost:8080/RestfulWebServiceExample/rest/files
@Path("/files")
public class FileService {

public static final String BASE_FOLDER = "D:/WorkSpace/GPCoder/Java-Tutorial/Jersey2RestfulWebServiceExample/data/";

@GET
@Path("/download/{type}")
public Response downloadFile(@PathParam("type") String fileType) {

String fileName = "test." + fileType;
File file = new File(BASE_FOLDER + "download/" + fileName);

/* Finding MIME type for explicitly setting MIME */
String mimeType = new MimetypesFileTypeMap().getContentType(file);

ResponseBuilder responseBuilder = Response.ok(file, mimeType);
responseBuilder.header("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
return responseBuilder.build();
}

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response uploadFile( //
@FormDataParam("uploadFile") InputStream fileInputStream,
@FormDataParam("uploadFile") FormDataContentDisposition fileFormDataContentDisposition) {

String fileName = fileFormDataContentDisposition.getFileName();
File uploadedFile = FileUtils.storeFile(fileInputStream, BASE_FOLDER, fileName);

FileUploadResponse entity = new FileUploadResponse();
entity.setFileName(uploadedFile.getName());
entity.setFileSizeInByte(uploadedFile.length());
entity.setCreatedDate(new Date());
System.out.println("entity: " + entity);

return Response.ok("File uploaded successfully at " + uploadedFile.getPath()).entity(entity).build();
}
}

Lớp hỗ trợ lưu file trên server từ một InputStream:

FileUtils.java

package com.gpcoder.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Paths;

public class FileUtils {

private FileUtils() {
super();
}

public static File storeFile(InputStream inputStream, String baseFolder, String fileName) {
String extension = fileName.substring(fileName.lastIndexOf('.'));
String name = fileName.substring(0, fileName.lastIndexOf('.'));
String uploadedFilePath = baseFolder + "upload/" + name + "-" + System.currentTimeMillis() + extension;

File uploadedFile = new File(Paths.get(uploadedFilePath).toAbsolutePath().toString());
try (OutputStream outputStream = new FileOutputStream(uploadedFile)) {
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
return uploadedFile;
}
}

Test Upload/ Download file với RESTful web service sử dụng Postman

Test Upload/ Download file với RESTful web service sử dụng Postman

Test Upload/ Download file với RESTful web service sử dụng Postman

  • (1) : Chọn phương thức POST upload file mà REST service support.
  • (2) : Nhập địa chỉ resource.
  • (3) : Chọn tab Header.
  • (4) : Thêm header để chỉ định kết quả trả về là XML (Accept: application/xml). Nếu muốn kết quả trả về là json thì thay đổi giá trị Acccept: application/json.
  • (5) : Chọn tab Body.
  • (6) : Chọn enctype là multipart/form-data.
  • (7) : Nhập key uploadFile, key này tương ứng với đối số mà web service nhận dữ liệu.
  • (8) : Chọn loại control là Upload.
  • (9) : Chọn file để test.
  • (10) : Gửi request.
  • (11) : Kết quả trả về.
  Làm thế nào để Test Jersey Rest API với JUnit?
  Sử dụng Swagger UI trong jersey REST WS project

Tạo Java REST Client truy cập web servcie với Jersey2 Client

Tạo Jersey Client upload file

UploadFileWithJerseyRestClientExample.java

package com.gpcoder.client;

import java.io.File;
import java.io.IOException;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;

import com.gpcoder.model.FileUploadResponse;

public class UploadFileWithJerseyRestClientExample {

public static final String API_URL = "http://localhost:8080/RestfulWebServiceExample/rest/files/upload";

public static final String BASE_FOLDER = "D:/WorkSpace/GPCoder/Java-Tutorial/Jersey2RestfulWebServiceExample/data/";

public static void main(String[] args) throws IOException {

File testFile = new File(BASE_FOLDER + "test.jpg");
FileDataBodyPart filePart = new FileDataBodyPart("uploadFile", testFile);

try (FormDataMultiPart formDataMultiPart = new FormDataMultiPart();
MultiPart multipart = formDataMultiPart.bodyPart(filePart);) {

Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build();
WebTarget target = client.target(API_URL);
Response response = target.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.entity(multipart, multipart.getMediaType()));

FileUploadResponse result = response.readEntity(FileUploadResponse.class);
System.out.println("result: " + result);
}
}
}

Tạo Jersey Client download file

DownloadFileWithJerseyRestClientExample.java

package com.gpcoder.client;

import java.io.File;
import java.io.InputStream;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.media.multipart.MultiPartFeature;

import com.gpcoder.utils.FileUtils;

public class DownloadFileWithJerseyRestClientExample {

public static final String API_URL = "http://localhost:8080/RestfulWebServiceExample/rest/files/download/";

public static final String BASE_FOLDER = "D:/WorkSpace/GPCoder/Java-Tutorial/Jersey2RestfulWebServiceExample/data/";

public static void main(String[] args) {

Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build();
WebTarget target = client.target(API_URL + "docx");
Response resp = target.request().get();

if (resp.getStatus() == Response.Status.OK.getStatusCode()) {
InputStream is = resp.readEntity(InputStream.class);
File uploadedFile = FileUtils.storeFile(is, BASE_FOLDER, "test-download.docx");
System.out.println("uploadedFile: " + uploadedFile.getAbsolutePath());
} else {
System.out.println("Http Call failed. The response code is" + resp.getStatus() + //
". Error reported is" + resp.getStatusInfo());
}
}
}

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

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

Xem thêm công việc IT ngành CNTT hấp dẫn trên TopDev

Helm, Ripgrep và Emacs dùng… helm-ag

helm
Helm, Ripgrep và Emacs dùng... helm-ag

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

Có một thủ thuật khá là tiện dụng khi dùng helm trong Emacs đó là lệnh helm-resume giúp khôi phục lại lệnh đã chạy trước đó, lệnh này đặc biệt hữu dụng khi đang search trong toàn project.

Lại nói về search, thường chúng ta hay xài ag (hay còn gọi là the_silver_searcher) vì nó có performance rất tốt, nhưng còn có một công cụ xịn hơn cả ag đó là rg (gọi là ripgrep, build bằng Rust, tất nhiên ). Để tích hợp ag vào Emacs/Helm chúng ta có gói helm-ag, với rg thì chúng ta có helm-rg.

  8 thủ thuật khi làm việc với Object sử dụng resting và spreading
  Hiểu tất tần tật về UX Design với UX360: User Experience in a nutshell.

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

Nhưng helm-rg không play nice lắm với lệnh helm-resume, nó thường xảy ra lỗi đại loại là helm-rg--make-process should run a process sau khi resume, dẫn tới việc không jump tới buffer trong kết quả search được. Anh tác giả không chịu fix vì cho rằng đó là lỗi của helm hay helm-projectile gì đó, nói chung là đổ thừa, mà mình thì rất ghét dev có tính đổ thừa. Trong khi sự thật rõ rành rành, helm-ag vẫn chạy tốt khi resume! 

Mà ghét rồi thì sao? Không lẽ bỏ không xài rg nữa, về xài ag? Đời nào lại thế?

Hóa ra, tác giả của helm-ag đã rất tinh ý khi hướng dẫn cả cách xài rg thay thế cho ag, documented ngay trong code của helm-ag! Đó là:

  1. Vào màn hình tùy biến giá trị M-x customize-set-variable
  2. Chọn giá trị helm-ag-base-command
  3. Đặt giá trị mới thành: rg --vimgrep --no-heading --smart-case

Ba bước trên thì ai cũng biết rồi, không có gì đáng nói.

Cái đáng nói là khi kết hợp với helm-projectile (ai xài projectile thì sẽ gặp liền), rg sẽ phun ra một đống lỗi:

.#*: No such file or directory (os error 2)
*.o: No such file or directory (os error 2)
*~: No such file or directory (os error 2)
*.bin: No such file or directory (os error 2)
*.lbin: No such file or directory (os error 2)
*.so: No such file or directory (os error 2)
*.a: No such file or directory (os error 2)
*.ln: No such file or directory (os error 2)
*.blg: No such file or directory (os error 2)
*.bbl: No such file or directory (os error 2)
*.elc: No such file or directory (os error 2)
*.lof: No such file or directory (os error 2)
*.glo: No such file or directory (os error 2)
*.idx: No such file or directory (os error 2)
*.lot: No such file or directory (os error 2)
*.fmt: No such file or directory (os error 2)
*.tfm: No such file or directory (os error 2)
...

Nguyên nhân là vì helm-projectile quá thông minh và nhiệt tình, tới mức nhanh nhẩu, tự add thêm vài cái option kiểu --ignore *.o ... vô khi chạy helm-projectile-ag, và tất nhiên rg không có option này, nên nó hiểu nhầm các giá trị kia là đường dẫn cần search.

Đến đây có hai cách để giải quyết:

  1. Là sửa code của helm-projectile để nó khỏi thêm vào các option “lạ” nữa
  2. Là wrap cái lệnh rg trong máy lại thành một lệnh khác để loại bỏ thuộc tính “lạ” kia đi

Cách thứ 1 nếu làm theo thì sẽ ngầu hơn, nhưng khó maintain về sau, nếu không ngại việc tự xài một bản fork riêng cho mọi thứ mình dùng thì cứ chơi.

An toàn nhất là chọn cách 2. Wrap lệnh rg lại tức là tạo ra một lệnh mới, đặt ở, ví dụ /usr/local/bin/rgemacs, với nội dung như sau:

#!/usr/bin/env bash
set -euo pipefail

newargs="$(echo "$@" | sed 's/\-\-ignore .* //')"
rg $newargs

Đừng quên cấp quyền execute cho nó:

$ chmod +x /usr/local/bin/rgemacs

Rồi thay giá trị của helm-ag-base-command thành rgemacs --vimgrep --no-heading --smart-case, thế là xong.

Update 19/11/2018: Cách trên gây ra một vấn đề đó là khi trong search query có nhiều từ và khoảng trắng, thì chỉ có một từ duy nhất ở cuối query được ghi nhận, ví dụ, search với query: “day la con ga” thì chỉ có chữ “ga” là được search.

Cách tốt hơn đó là vẫn xài rg thay cho rgemacs trong helm-ag-base-command và ghi đè hàm helm-projectile-ag bằng cách chèn đoạn code sau vào Emacs config của bạn, tất nhiên là sau khi load helm-ag và projectile:

(defun helm-projectile-ag (&optional options)
  "Helm version of projectile-ag."
  (interactive (if current-prefix-arg (list (read-string "option: " "" 'helm-ag--extra-options-history))))
  (if (require 'helm-ag nil  'noerror)
      (if (projectile-project-p)
          (let ((helm-ag-command-option options)
                (current-prefix-arg nil))
            (helm-do-ag (projectile-project-root) (car (projectile-parse-dirconfig-file))))
        (error "You're not in a project"))
    (error "helm-ag not available")))

Biên lại dựa trên kinh nghiệm và hướng dẫn đọc được từ một cái gist.

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 CNTT hấp dẫn trên TopDev