Home Blog Page 71

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ể.

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:

Đ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:

À, 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

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

các công ty 3pl ở việt nam
3PL Là Gì? Top Các Công Ty 3PL Ở Việt Nam

Thị trường logistics ngày càng phát triển mang đến cho các công ty nhiều cơ hội và dư địa cho một thị trường mới. Để đáp ứng nhu cầu của các công ty từ trong đến ngoài nước, các dịch vụ 3PL đã sớm được triển khai để phục vụ thị trường. Vậy 3PL thực chất là gì? Các công ty 3PL ở Việt Nam đang phát triển gồm những thương hiệu nào?

các công ty 3pl ở việt nam

3PL là gì?

3PL – Third Party Logistics: hậu cần bên thứ 3, có thể hiểu một cách đơn giản là một đơn vị chuyên cung cấp các dịch vụ logistics sẽ được công ty có nhu cầu thuê để đảm nhận các nhiệm vụ liên quan đến việc vận hành một mảng nhất định trong chuỗi cung ứng của doanh nghiệp đó. Việc thuê ngoài các đơn vị 3PL giúp doanh nghiệp có thể tiết kiệm tối đa nhân lực của mình, sử dụng các dịch vụ bên ngoài với chuyên môn cao trong lĩnh vực đó sẽ giúp doanh nghiệp đạt hiệu quả công việc cao hơn.

Các dịch vụ do 3PL cung cấp rất đa dạng liên quan đến các vấn đề về logistics và chuỗi cung ứng. Có thể kể đến như lưu kho và vận chuyển hàng hóa, soạn hàng, đóng gói, giao hàng, thực hiện đơn hàng,… Tùy theo nhu cầu cụ thể của doanh nghiệp mà họ có thể thuê nhiều 3PL cùng lúc để thực hiện mỗi chức năng khác nhau.

Xem thêm Tổng Hợp Các Công Ty Outsourcing Lớn Nhất Việt Nam

Những lợi ích khi sử dụng 3PL đối với các doanh nghiệp

  • Giảm chi phí cho doanh nghiệp: Thực tế việc thuê một công ty ngoài để thực hiện 3PL sẽ tiết kiệm khá nhiều chi phí so với việc một công ty chưa có chuyên môn trong mảng này triển khai thực hiện. Dựa trên khối lượng công việc, 3PL có thể thương lượng với doanh nghiệp và đưa ra mức giá tốt nhất để hoàn thành những công việc đó.
  • Giúp doanh nghiệp tập trung phát triển các khía cạnh chuyên môn của mình.
  • Cung cấp trải nghiệm tốt hơn cho người tiêu dùng: Các doanh nghiệp thực hiện 3PL có chuyên môn sâu rộng trong lĩnh vực của mình, mạng lưới vận chuyển rộng lớn, do vậy họ hoàn toàn có thể thực hiện việc này tốt hơn so với doanh nghiệp mới bắt đầu.
  • Linh hoạt trong việc tăng giảm quy mô hoạt động: Nhu cầu tiêu dùng tùy theo thời điểm khác nhau mà sẽ có những biến động nhất định. Vậy nên việc sử dụng 3PL cho phép các doanh nghiệp dựa vào tình hình thực tế để tăng giảm tần suất hoạt động, mà không cần cam kết vốn khi không cần thiết.
  • Chủ động trong các vấn đề liên quan đến logistics quốc tế: Đối với các công ty hoạt động buôn bán trên thị trường quốc tế, 3PL sẽ giúp giải quyết hiệu quả các vấn đề liên quan đến giấy tờ, thuế, hải quan hay các vấn đề phát sinh ở biên giới,… Và giúp các công ty tránh gặp phải các rắc rối liên quan đến vấn đề giữa các quốc gia mà 3PL nắm rất rõ.
dịch vụ 3pl
Sử dụng 3PL mang lại rất nhiều lợi ích cho các doanh nghiệp

Các công ty 3PL ở Việt Nam

1. Tập đoàn Deutsche Post DHL Group

DHL là một thương hiệu có tuổi đời lâu năm trên thế giới, chuyên về hoạt động hậu cần xuất hiện ở hơn 220 quốc gia và vùng lãnh thổ. Các hoạt động của DHL được đánh giá cao về chất lượng và tiết kiệm chi phí.

2. Tập đoàn A.P. Moller-Maersk

A.P. Mller-Maersk được đánh giá là công ty vận tải biển lớn nhất thế giới. Với hơn 100 năm kinh nghiệm trong ngành vận tải hàng hóa, Maersk Việt Nam đã trở thành công ty vận chuyển hàng đầu với các dịch vụ linh hoạt, chất lượng và đáng tin cậy.

3. Công ty Vận Tải và Logistics Viettel Post

Là doanh nghiệp Việt Nam, sau nhiều năm hoạt động, đến nay Viettel Post đã vươn lên trở thành một trong những thương hiệu chuyển phát hàng đầu cả nước, với mạng lưới bưu cục, trung tâm điều hành, mạng lưới mở rộng phủ khắp các tỉnh, thành.

4. Công Ty Vận Tải và Logistics Gemadept

Gemadept tự hào là doanh nghiệp tiên phong trong lĩnh vực khai thác cảng và hậu cần tại thị trường Việt Nam, với mạng lưới hoạt động rộng lớn trải dài ở khắp các tỉnh thành trên cả nước.

5. Công Ty Vận Tải – Logistics TRANSIMEX

TRANSIMEX cung cấp các giải pháp logistics tổng thể cho khách hàng với nhiệm vụ của một 3PL, bao gồm giao nhận hàng hóa quốc tế, kho bãi, vận tải nội địa và phân phối.

6. Dịch Vụ Vận Tải và Logistics của FedEX

Có trụ sở đặt tại Hoa Kỳ, FedEx Express đến nay đã tận dụng mạng lưới đường hàng không và đường bộ toàn cầu để xúc tiến vận chuyển nhanh chóng, hiệu quả và chất lượng các lô hàng nhận được.

7. Công Ty Vận Tải và Logistics – Indo Trans Logistics Corporation (ITL)

ITL là thương hiệu công ty 3PL nổi bật trong cả nước, nhiều nằm liền đều nằm trong top các doanh nghiệp 3PL hoạt động hiệu quả nhất.

Các công ty 3PL cung cấp những giải pháp hậu cần, vận chuyển và giao nhận hàng hóa đầy hiệu quả và tối ưu chi phí. Nếu tận dụng tốt điều này cũng như có thể tìm được công ty đáp ứng yêu cầu đề ra, các doanh nghiệp chắc chắn sẽ đạt kết quả tốt hơn trong hoạt động hậu cần. Mong rằng các công ty 3PL ở Việt Nam được đề cập trong bài viết này sẽ giúp ích cho người đọc.


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

Phần 2: Giới thiệu xung quanh Cơ Sở Dữ Liệu (CSDL)

cơ sở dữ liệu
Phần 2: Giới thiệu xung quanh Cơ Sở Dữ Liệu(CSDL)

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

Kiến trúc hệ quản trị cơ sở dữ liệu

  1. Mô hình kiến trúc hệ quản trị CSDL gồm 3 mức:

    • Mức ngoài (External Level) hoặc mức khung nhìn (View Level): xác định các giao diện như những ứng dụng, tương tác và hiển thị cho người sử dụng.
    • Mức quan niệm (Conceptual Level) hoặc mức logic: còn gọi là mô hình quan niệm của dữ liệu (MQD) hoặc mô hình logic của dữ liệu (MLD). Nó xác định cách sắp xếp thông tin bên trong CSDL;
    • Mức trong (Internal Level) hoặc mức vật lý (Physical Level): xác định cách thức lưu trữ dữ liệu và các phương pháp truy cập vào đó;
      Giữa ba mức này có hai ánh xạ(mapping):Ánh xạ giữa mức ngoài và mức quan niệmÁnh xạ giữa mức trong và mức quan niệmMô phỏng cho mô hình kiến trúc hệ quản trị CSDL là mô hình ANSI/SPARC ra đời năm 1975 đã xác định một kiến trúc trừu tượng phục vụ cho phân tích và thiết kế các hệ quản trị cơ sở dữ liệu (DBMS)Mô hình kiến trúc ANSI/SPARC cho phép tạo ra sự độc lập giữa bản thân dữ liệu và việc xử lý dữ liệu. Sơ đồ ở hình bên cho thấy sự triển khai kiến trúc vật lý của một hệ quản trị CSDL là như thế nào.
  Chuyển đổi hệ cơ số trong Python
  26 công cụ và kỹ thuật trong Big Data có thể bạn chưa biết

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

2. Điểm ưu việt của hệ quản trị CSDL

Nói chung một hệ quản trị CSDL có những đặc trưng ưu việt sau đây:

    • Tính độc lập vật lý: mức vật lý có thể thay đổi mà không bị phụ thuộc vào mức quan niệm. Điều đó có nghĩa rằng những người sử dụng không cần nhìn thấy các khía cạnh vật chất của CSDL. Nói cách khác, cấu trúc thể hiện thông tin là trong suốt đối với những người sử dụng;
    • Tính độc lập logic: mức quan niệm có thể được hiệu chỉnh mà không phụ thuộc vào mức vật lý, nghĩa là người quản trị CSDL có thể phát triển nó mà không làm phiền gì đến người sử dụng;
    • Có thể thao tác được: những người không rành về CSDL cũng có thể mô tả được các yêu cầu của họ mà không cần biết tới các thành tố kỹ thuật của CSDL;
    • Tốc độ truy cập nhanh: hệ thống phải có khả năng đáp ứng (trả lời) các yêu cầu một cách nhanh nhất có thể có và điều đó đòi hỏi áp dụng các giải thuật tìm kiếm nhanh;
    • Tính quản trị tập trung: hệ quản trị CSDL phải cho phép người quản trị có khả năng thao tác các dữ liệu, chèn vào các phần tử và xác minh tính toàn vẹn của dữ liệu theo một cách tập trung;
    • Hạn chế sự dư thừa: hệ quản trị CSDL phải có khả năng lưu trữ tối thiểu những thông tin dư thừa, vừa để chống lãng phí bộ nhớ vừa để tránh các lỗi;
    • Kiểm tra tính toàn vẹn: các dữ liệu phải nhất quán giữa chúng với nhau, hơn nữa khi các phần tử này tham chiếu đến các phần tử khác thì những phần tử khác phải có mặt;
    • Dữ liệu có thể chia sẻ: hệ quản trị CSDL phải cho phép nhiều người sử dụng truy cập đồng thời đến CSDL;
    • An ninh dữ liệu: hệ quản trị CSDL phải có các cơ chế cho phép quản lý quyền truy cập vào dữ liệu tùy theo từng người sử dụng.

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

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

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

REST Web service: JWT – Token-based Authentication trong Jersey 2.x

rest web service
REST Web service: JWT – Token-based Authentication trong Jersey 2.x

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

Trong bài trước, chúng ta đã cùng tìm hiểu về xác thực và phân quyền ứng dụng sử dụng cơ chế Basic Authentication trong Jersey 2.x. Trong bài này, chúng ta cùng tìm hiểu về cơ chế Token-based Authentication sử dụng tiêu chuẩn JSON Web Token (JWT).

Giới thiệu Token-based Authentication trong Jersey REST API

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. Chi tiết các bạn xem lại bài viết “Giới thiệu Json Web Token (JWT)“.

Token-based Authentication là cơ chế xác thực người dùng dựa trên việc tạo ra token – một chuỗi ký tự (thường được mã hóa) mang thông tin xác định người dùng được server tạo ra và lưu ở client. Server sau đó có thể không lưu lại token này.

Giới thiệu Token-based Authentication trong Jersey REST API

Tương tự như cơ chế Basic Authentication, với Token-based Authentication chúng ta cũng sẽ tạo ContainerRequestFilter để verify access của một user.

Token-based Authentication trong Jersey Server

Tạo Jersey project

Chúng ta sẽ tạo project mới tương tự project Basic Authentication ở bài viết trước.

cung cấp các REST API

Khai báo thư viện hỗ trợ JWT

Có nhiều thư viện Java hỗ trợ tạo JWT và verify JWT. Chúng ta có thể sử dụng một trong hai thư viện phổ biến sau:

Trong bài này, chúng ta sẽ sử dụng thư viện io.jsonwebtoken để minh họa cho Token-based Authentication trong Jersey REST API.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.10.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>

Tạo các data model class

Order.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.gpcoder.model;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement
public class Order {
    private Integer id;
    private String name;
}

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.gpcoder.model;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String username;
    private String password;
    private List<String> roles;
}

Role.java

1
2
3
4
5
6
7
package com.gpcoder.model;
public interface Role {
    String ROLE_ADMIN = "Admin";
    String ROLE_CUSTOMER = "Customer";
}

Tạo service để hỗ trợ lấy thông tin User.

UserService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.gpcoder.service;
import java.util.ArrayList;
import java.util.Arrays;
import com.gpcoder.model.Role;
import com.gpcoder.model.User;
/**
 * This is a dummy class to get the user information
 */
public class UserService {
    public User getUser(String username) {
        User user = new User();
        user.setUsername(username);
        user.setPassword("gpcoder");
        if ("admin".equals(username)) {
            user.setRoles(Arrays.asList(Role.ROLE_ADMIN));
        } else if ("customer".equals(username)) {
            user.setRoles(Arrays.asList(Role.ROLE_CUSTOMER));
        } else if ("gpcoder".equals(username)) {
            user.setRoles(Arrays.asList(Role.ROLE_ADMIN, Role.ROLE_CUSTOMER));
        } else {
            user.setRoles(new ArrayList<>());
        }
        return user;
    }
}

Tạo SecurityContext class

SecurityContext : chứa thông tin chứng thực của một request. Sau khi chứng thực thành công, chúng ta cần cung cấp thông tin chứng thực user, role cho context class này. Các phương thức của SecurityContext sẽ được triệu gọi trước khi các resource method đã đánh dấu với các Annotation @RolesAllowed hoặc @PermitAll hoặc @DenyAll được thực thi.

BasicSecurityConext.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.gpcoder.model;
import java.security.Principal;
import javax.ws.rs.core.SecurityContext;
/**
 * The SecurityContext interface provides access to security related
 * information. An instance of SecurityContext can be injected into a JAX-RS
 * resource class field or method parameter using the @Context annotation.
 *
 * @see https://jersey.github.io/documentation/latest/security.html
 * @see https://docs.oracle.com/javaee/7/api/javax/ws/rs/core/SecurityContext.html
 */
public class BasicSecurityConext implements SecurityContext {
    private User user;
    private boolean secure;
    public BasicSecurityConext(User user, boolean secure) {
        this.user = user;
        this.secure = secure;
    }
    @Override
    public Principal getUserPrincipal() {
        return () -> user.getUsername();
    }
    @Override
    public boolean isUserInRole(String role) {
        return user.getRoles().contains(role);
    }
    @Override
    public boolean isSecure() {
        return secure;
    }
    @Override
    public String getAuthenticationScheme() {
        return SecurityContext.BASIC_AUTH;
    }
}

Tạo REST API

Tạo rest api để chứng thực user và cung cấp web token.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.gpcoder.api;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.gpcoder.helper.JwTokenHelper;
import com.gpcoder.model.User;
import com.gpcoder.service.UserService;
@Path("/auth")
public class AuthService {
    /**
     * Authenticating a user with their username/ password and issuing a token
     *
     * @param username
     * @param password
     * @return JSON Web Token (JWT)
     */
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response authenticateUser(@FormParam("username") String username, @FormParam("password") String password) {
        // Authenticate the user using the credentials provided
        UserService userService = new UserService();
        User user = userService.getUser(username);
        if (user == null || !user.getPassword().equals(password)) {
            return Response.status(Response.Status.FORBIDDEN) // 403 Forbidden
                    .entity("Wrong username or password") // the response entity
                    .build();
        }
        // Issue a token for the user
        String token = JwTokenHelper.createJWT(user);
        // Return the token on the response
        return Response.ok(token).build();
    }
}

Chúng ta cung cấp các REST API như sau:

cung cấp các REST API

OrderService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.gpcoder.api;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import com.gpcoder.model.Order;
import com.gpcoder.model.Role;
// URI:
// http(s)://<domain>:(port)/<YourApplicationName>/<UrlPattern in web.xml>/<path>
// http://localhost:8080/RestfulWebServiceExample/rest/orders
@Path("/orders")
@PermitAll
public class OrderService {
    @GET
    @Path("/{id}")
    public Response get(@PathParam("id") int id) {
        System.out.println("OrderService->get()");
        return Response.ok("OrderService->get()").build();
    }
    @RolesAllowed(Role.ROLE_CUSTOMER)
    @POST
    public Response insert(Order order, @Context SecurityContext securityContext) {
        System.out.println("User: " + securityContext.getUserPrincipal().getName());
        System.out.println("OrderService->insert()");
        return Response.ok("OrderService->insert()").build();
    }
    @RolesAllowed({ Role.ROLE_ADMIN, Role.ROLE_CUSTOMER })
    @PUT
    public Response update(Order order) {
        System.out.println("OrderService->update()");
        return Response.ok("OrderService->update()").build();
    }
    @RolesAllowed(Role.ROLE_ADMIN)
    @DELETE
    @Path("/{id}")
    public Response delete(@PathParam("id") int id) {
        System.out.println("OrderService->delete()");
        return Response.ok("OrderService->delete()").build();
    }
}

Tạo class helper để hỗ trợ tạo Json Web Token (JWT), kiểm tra tính hợp lệ của token và extract thông tin user cần thiết.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.gpcoder.helper;
import java.security.Key;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import com.gpcoder.model.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JwTokenHelper {
    // The privateKey is only valid for the given minutes
    private static final long EXPIRATION_LIMIT_IN_MINUTES = 30;
    // The JWT signature algorithm we will be using to sign the token
    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
    // Keys used with HS256 MUST have a size >= 256 bits
    private static final String SECRET_KEY = "gpcoderdotcom-token-base-authentication-with-jwt-example";
    private static final String ISSUER = "https://gpcoder.com";
    private JwTokenHelper() {
        super();
    }
    public static String createJWT(User user) {
        // Get the current time
        long currentTimeInMillis = System.currentTimeMillis();
        Date now = new Date(currentTimeInMillis);
        // The privateKey is only valid for the next EXPIRATION_LIMIT_IN_MINUTES
        long expirationTimeInMilliSeconds = TimeUnit.MINUTES.toMillis(EXPIRATION_LIMIT_IN_MINUTES);
        Date expirationDate = new Date(currentTimeInMillis + expirationTimeInMilliSeconds);
        // Will sign our JWT with our ApiKey secret
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, SIGNATURE_ALGORITHM.getJcaName());
        // Sets the JWT Claims sub (subject) value
        Claims claims = Jwts.claims().setSubject(user.getUsername());
        claims.put("roles", user.getRoles());
        // Let's set the JWT Claims
        JwtBuilder builder = Jwts.builder() // Configured and then used to create JWT compact serialized strings
                .setClaims(claims).setId(UUID.randomUUID().toString()) // Sets the JWT Claims jti (JWT ID) value
                .setIssuedAt(now) // Sets the JWT Claims iat (issued at) value
                .setIssuer(ISSUER) // Sets the JWT Claims iss (issuer) value
                .setExpiration(expirationDate) // Sets the JWT Claims exp (expiration) value
                .signWith(signingKey, SIGNATURE_ALGORITHM);
        // Builds the JWT and serializes it to a compact, URL-safe string
        return builder.compact();
    }
    /**
     * Get User from the given token
     */
    public static User getUserFromToken(String token) {
        final Claims claims = decodeJWT(token);
        User user = new User();
        user.setUsername(claims.getSubject());
        user.setRoles((List<String>) claims.get("roles"));
        return user;
    }
    /**
     * Check if the token was issued by the server and if it's not expired
     */
    public static Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }
    private static Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }
    private static <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = decodeJWT(token);
        return claimsResolver.apply(claims);
    }
    private static Claims decodeJWT(String jwt) {
        // This line will throw an exception if it is not a signed JWS (as expected)
        return Jwts.parser() // Configured and then used to parse JWT strings
                .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY)) // Sets the signing key used to verify
                                                                                // any discovered JWS digital signature
                .parseClaimsJws(jwt) // Parses the specified compact serialized JWS string based
                .getBody();
    }
}

Tạo ContainerRequestFilter để chứng thực user

Chúng ta sử dụng ContainerRequestFilter để thực hiện chứng thực user trước khi truy cập resource method.

  • Lấy thông tin Authorization từ header ở phía client gửi lên.
  • Tách lấy token từ Authentication header.
  • Kiểm tra tính hợp lệ của token.
  • Thực hiện chứng thực user.
  • Lấy thông tin user từ token.
  • Lưu thông tin chứng thực lại để các request sau có thể kiểm tra chứng thực mà không cần truy xuất database, chẳng hạn @RolesAllowed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.gpcoder.filter;
import java.io.IOException;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.ext.Provider;
import com.gpcoder.helper.JwTokenHelper;
import com.gpcoder.model.BasicSecurityConext;
import com.gpcoder.model.User;
@Provider
@Priority(Priorities.AUTHENTICATION) // needs to happen before authorization
public class AuthFilter implements ContainerRequestFilter {
    private static final String REALM = "gpcoder";
    private static final String AUTHENTICATION_SCHEME = "Bearer";
    /**
     * Extracting the token from the request and validating it
     *
     * The client should send the token in the standard HTTP Authorization header of
     * the request. For example: Authorization: Bearer <token-goes-here>
     *
     * Finally, set the security context of the current request
     */
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        // (1) Get Token Authorization from the header
        String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
        // (2) Validate the Authorization header
        if (!isTokenBasedAuthentication(authorizationHeader)) {
            return;
        }
        // (3) Extract the token from the Authorization header
        String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
        try {
            // (4) Validate the token
            if (JwTokenHelper.isTokenExpired(token)) {
                abortWithUnauthorized(requestContext);
                return;
            }
            // (5) Getting the User information from token
            User user = JwTokenHelper.getUserFromToken(token);
            // (6) Overriding the security context of the current request
            SecurityContext oldContext = requestContext.getSecurityContext();
            requestContext.setSecurityContext(new BasicSecurityConext(user, oldContext.isSecure()));
        } catch (Exception e) {
            abortWithUnauthorized(requestContext);
        }
    }
    private boolean isTokenBasedAuthentication(String authorizationHeader) {
        // Check if the Authorization header is valid
        // It must not be null and must be prefixed with "Bearer" plus a whitespace
        // The authentication scheme comparison must be case-insensitive
        return authorizationHeader != null
                && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
    }
    private void abortWithUnauthorized(ContainerRequestContext requestContext) {
        // Abort the filter chain with a 401 status code response
        // The WWW-Authenticate header is sent along with the response
        Response respone = Response.status(Response.Status.UNAUTHORIZED) // 401 Unauthorized
                .header(HttpHeaders.WWW_AUTHENTICATE, AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
                .entity("You cannot access this resource") // the response entity
                .build();
        requestContext.abortWith(respone);
    }
}

Chi tiết về Filter các bạn xem lại bài viết “Filter và Interceptor với Jersey 2.x“.

Đăng ký RolesAllowedDynamicFeature

Để có thể áp dụng các Annotation @RolesAllowed, @PermitAll hoặc @DenyAll, chúng ta cần đăng ký sử dụng tính năng này với Jersey.

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

Tạo custom exception để xử lý kết quả trả về Client khi có ngoại lệ xảy ra

GenericExceptionMapper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.gpcoder.exception.mapper;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
    @Override
    public Response toResponse(Throwable ex) {
        return Response.status(getStatusType(ex))
                .entity(ex.getMessage())
                .type(MediaType.TEXT_PLAIN) // "text/plain"
                .build();
    }
    
    private Response.StatusType getStatusType(Throwable ex) {
        if (ex instanceof WebApplicationException) {
            return((WebApplicationException)ex).getResponse().getStatusInfo();
        } else {
            // 500, "Internal Server Error"
            return Response.Status.INTERNAL_SERVER_ERROR;
        }
    }
}

Chi tiết về xử lý ngoại lệ với Jersey, các bạn xem lại bài viết “HTTP Status Code và xử lý ngoại lệ RESTful web service với Jersey 2.x“.

Test ứng dụng với Postman

Test @GET http://localhost:8080/RestfulWebServiceExample/rest/orders/1

sử dụng token này để truy cập resource.vvvvvv

Như bạn thấy, chúng ta vẫn truy xuất được resource method với @PermitAll.

Test @DELETE http://localhost:8080/RestfulWebServiceExample/rest/orders/1

sử dụng token này để truy cập resource.

Chúng ta nhận được thông báo lỗi 403, do chưa được chứng thực với user có role Admin.

Bây giờ, chúng ta đăng nhập với user Admin để lấy token:

@POST http://localhost:8080/RestfulWebServiceExample/rest/auth

sử dụng token này để truy cập resource.

Tiếp theo, chúng ta sẽ sử dụng token này để truy cập resource.

sử dụng token này để truy cập resource.

Chúng ta đã truy xuất thành công resource với user có role Admin.

Tương tự các bạn test với @POST, @PUT lần lượt với các username là customer, admin, test và password là gpcoder để xem kết quả.

JWT – Token-based Authentication trong Jersey Client

Ví dụ: tạo ứng dụng REST Client truy cập @DELETE resource.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.gpcoder.client;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.logging.LoggingFeature;
public class OrderServiceClient {
    public static final String AUTH_URL = "http://localhost:8080/RestfulWebServiceExample/rest/auth";
    public static final String API_URL = "http://localhost:8080/RestfulWebServiceExample/rest/orders";
    public static void main(String[] args) {
        // (1) Authenticate and get token
        final String token = getToken();
        // (2) Call @DELETE API
        Client client = createJerseyRestClient();
        WebTarget target = client.target(API_URL).path("1");
        Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON_TYPE);
        invocationBuilder.header("Authorization", "Bearer " + token);
        final Response response = invocationBuilder.delete();
        // (3) Handle result
        System.out.println("Call delete() successful with the result: " + response.readEntity(String.class));
    }
    private static String getToken() {
        Form formData = new Form();
        formData.param("username", "admin");
        formData.param("password", "gpcoder");
        Client client = createJerseyRestClient();
        WebTarget target = client.target(AUTH_URL);
        Response response = target.request(MediaType.APPLICATION_JSON_TYPE).post(Entity.form(formData));
        return response.readEntity(String.class);
    }
    private static Client createJerseyRestClient() {
        ClientConfig clientConfig = new ClientConfig();
        // Config logging for client side
        clientConfig.register( //
                new LoggingFeature( //
                        Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), //
                        Level.INFO, //
                        LoggingFeature.Verbosity.PAYLOAD_ANY, //
                        10000));
        return ClientBuilder.newClient(clientConfig);
    }
}

Lưu ý: ngoài cách thêm thông tin header thủ công trong mỗi request, chúng ta có thể tạo một ClientRequestFilter để tự động thêm thông tin header như đã giới thiệu trong bài viết “Filter và Interceptor với Jersey 2.x“.

Các bước tiến hành Kiểm Thử Tự Động

kiểm thử tự động
Các bước tiến hành Kiểm Thử Tự Động

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

Với sự phát triển không ngừng của ngành công nghệ phần mềm và ứng dụng chu trình phát triển Agile, kiểm thử tự động (Automated Testing) đã trở thành một đề tài nóng bỏng trong giới kiểm thử và quản trị chất lượng phần mềm. Nhưng, làm thế nào để có thể tự động hoá việc kiểm thử cho một sản phẩm phần mềm thì không phải ai cũng có thể áp dụng hiệu quả. Đây chỉ là các bước mà mình, qua quá trình học tập và làm việc với kiểm thử tự động đúc kết được. Hy vọng sẽ giúp ích được phần nào cho các bạn trên con đường kiểm thử tự động này…

Cac buoc

Bước 1: Phân tích khả năng áp dụng kiểm thử tự động.

Hiển nhiên, chúng ta không thể tự động hoá mọi việc trong kiểm thử phần mềm được. Có những phần mềm mới hay công nghệ viết ra phần mềm mà những công cụ kiểm thử tự động hiện tại chưa hỗ trợ hoặc chỉ hỗ trợ một phần. Ví dụ rõ ràng nhất là khi chúng ta kiểm thử một trang Web trên một trình duyệt mới, và lúc đó, công cụ kiểm thử tự động chưa có phiên bản mới hỗ trợ trên trình duyệt đó. Hay, một ví dụ khác về chương trình SAP, các nhà phát triển SAP đã đưa ra một lựa chọn ngăn chặn việc chạy script tự động trên nó, và để có thể kiểm thử tự động trên SAP, chúng ta cần yêu cầu các nhà phát triển chương trình SAP gỡ bỏ lựa chọn này.

  10 lý do kiểm thử phần mềm trở thành một nghề thời thượng
  5 điều cần lưu ý trước khi bắt đầu kiểm thử di động

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

Bước 2: Lựa chọn công cụ kiểm thử tự động thích hợp.

Sau khi xác định được sản phẩm hiện tại có thể làm Kiểm Thử Tự Động hay không, bước kế tiếp, chúng ta cần xác định nên sử dụng công cụ kiểm thử tự động nào. Công cụ nào hỗ trợ kiểm thử tự động cho công nghệ mà sản phẩm sử dụng? Ưu nhược điểm của từng công cụ? Ngôn ngữ kịch bản nào mà công cụ kiểm thử sử dụng? Nhân sự hiện tại có quen thuộc với công cụ đó hay không?

Bước 3: Xây dựng môi trường làm việc.

Môi trường làm việc bao gồm các khái niệm, chu trình, thủ tục và môi trường mà kịch bản kiểm thử tự động được thiết kế và viết ra. Bên cạnh đó, nó cũng nên bao gồm luôn cấu trúc thư mục, lưu trữ các kịch bản kiểm thử cũng như các mối quan hệ logic giữa các thành phần.

Bước 4: Viết kịch bản kiểm thử, thực thi và phân tích kết quả.

Dựa trên các kịch bản kiểm thử đã được tạo ra bằng kiểm thử thủ công, dựa vào ngôn ngữ kịch bản mà công cụ kiểm thử tự động hỗ trợ, chúng ta viết các đoạn mã tương tác với sản phẩm phần mềm trên các môi trường và thực thi nó. Sau khi thực thi các đoạn mã, chúng ta cần phân tích các kết quả đạt được và ghi lại các vấn đề của sản phẩm, nếu có.

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

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

Đừng đánh nhau với borrow checker

borrow checker

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

TL;DR: Đừng bao giờ đánh nhau với borrow checker, nó được sinh ra để bạn phải phục tùng nó 

Một trong những cơn ác mộng của lập trình viên khi làm việc với Rust đó là chuyện đập nhau với borrow checker, ví dụ như những lúc bị compiler chửi vào mặt như thế này.

fn main() {
    let a = vec![5];
    let b = a;
    println!("{:?}", a);
}
error[E0382]: use of moved value: `a`
 --> borrowchecker.rs:4:22
  |
3 |     let b = a;
  |         - value moved here
4 |     println!("{:?}", a);
  |                      ^ value used here after move
  |
  = note: move occurs because `a` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait

Chỉ với một câu lệnh gán thông thường, chúng ta đã bị ăn lỗi.

Và trong hầu hết mọi trường hợp thì kẻ có lỗi chính là các lập trình viên  Borrow checker cũng chưa bao giờ đập ai (nó chỉ đứng đó và chửi vô mặt người ta thôi). Lỗi ở đây chính là vì chúng ta không hiểu về mô hình quản lý bộ nhớ của Rust, và các quy tắc mà ngôn ngữ lập trình này đặt ra.

  CSS Box Model và box-sizing: border-box là gì vậy?
  Mediator Design Pattern – Collaborate via me

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

Vậy Rust kiểm soát bộ nhớ như thế nào? Và làm sao để chúng ta có thể làm vừa lòng Rust compiler trong những trường hợp như thế này?

Bài viết này sẽ giúp các bạn hiểu được chuyện đó thông qua hai vấn đề chính: Ownership (quyền sở hữu của một biến) và Borrowing (vay mượn/tham chiếu giữa các biến).

Ownership

Trong máy tính, bộ nhớ (memory) là từ dùng để chỉ các ô nằm liền kề nhau để lưu trữ thông tin.

Khi chúng ta khai báo một biến mới trong Rust, một vùng nhớ sẽ được cấp phát trong bộ nhớ, và nó sẽ mọc thêm mắt mũi chân càng như thế này:

Just kididng  một biến a khi được khai báo, máy tính sẽ cấp phát một vùng nhớ trên stack, với giá trị mặc định là giá trị được truyền vào khi khai báo. Địa chỉ của vùng nhớ này sẽ được gán cho biến a.

Khi đó ta có thể coi là: vùng nhớ này thuộc về biến a, và a có quyền sở hữu (ownership) đối với vùng nhớ đó.

Một vùng nhớ tại một thời điểm chỉ có thể thuộc về duy nhất một biến. Và điều gì xảy ra nếu ta gán một biến vào một biến khác?

Quyền sở hữu vùng nhớ của biến này sẽ được chuyển sang biến khác, ở ví dụ trên ta khai báo biến a là một vector, sau đó gán biến a cho b, lúc này vùng nhớ mà biến a sở hữu đã được chuyển (move) sang cho b.

Nếu ngay sau đó chúng ta tìm cách đọc biến a thì sẽ gặp lỗi, vì không còn mang giá trị nào để có thể đọc được nữa.

error[E0382]: use of moved value: `a`
  |
3 |     let b = a;
  |         - value moved here
4 |     println!("{:?}", a);
  |                      ^ value used here after move
  |

Để tránh việc giá trị của một biến bị moved sau khi thực hiện phép gán, ta có thể implement trait Copy cho kiểu dữ liệu của biến đó.

#[derive(Copy, Clone)]
struct Point {
  x: i32,
  y: i32
}

Chức năng của Copy trait đúng với tên gọi của nó, khi phép gán xảy ra, thì thay vì move giá trị của biến này sang biến khác, Rust runtime sẽ copy giá trị đó. Và sau phép gán, cả 2 biến vẫn có thể được sử dụng một cách bình thường.

Một số kiểu dữ liệu như i32 thường được implement sẵn trait này. Để biết kiểu dữ liệu mình tính xài được implement sẵn những trait nào thì bạn có thể xem trong phần Trait Implementations của kiểu dữ liệu đó.

Borrowing

Sao Rust compiler khó tánh dữ vậy? Giá trị của một biến thì thuộc quyền sở hữu của biến đó à? Vậy làm sao để truyền dữ liệu qua lại giữa các biến? Lỡ kiểu dữ liệu tui xài hổng có implement trait Copy thì làm sao?

Đến đây thì chúng ta bắt gặp một vấn đề rất là đời thường, xin nhắc lại, mặc dù Rust là một ngôn ngữ lập trình nhưng nó luôn phản ánh đúng với thực trạng của xã hội, đây là vấn đề thường ngày trong chuyện giao tiếp giữa người với người, vâng, rất nhân văn: Muốn xài đồ của người khác thì tất nhiên phải đi mượn (borrow).

Để mượn một giá trị trong Rust rất dễ, chúng ta chỉ cần đặt dấu & vào trước biến cần mượn. Ví dụ:

let b = &a;

Và tất nhiên khi đem cho mượn, giá trị của một biến vẫn thuộc quyền sở hữu của biến đó, Rust chỉ tạo một tham chiếu (references) đến vùng nhớ chứa giá trị này, chứ không move nó đi chỗ khác. Chính vì vậy, một biến có thể cho mượn bao nhiêu lần tùy ý. Nhưng với một điều kiện, đó là các tham chiếu đến giá trị của biến đó là readonly. Tức là không ai có thể thay đổi được giá trị của biến, trừ chính biến đó.

Cũng giống như khi bạn cho ai mượn ví tiền của bạn chỉ để xem cái ví như thế nào thôi, thì người ta không có quyền lấy tiền từ ví của bạn. Nhưng với vợ bạn thì sẽ khác… 

Vợ bạn có thể dùng &mut để vừa mượn vừa thay đổi được số tiền trong ví của bạn.

Lưu ý: Có thể bạn biết rồi, trong Rust, mọi giá trị được khai báo đều là immutable, nghĩa là không thể thay đổi được. Vì thế một biến được khai báo theo cách thông thường thì cũng immutable nốt. Nếu ta mượn một biến immutable để ghi (mutate nó) thì sẽ bị ăn lỗi:

error[E0596]: cannot borrow immutable local variable `a` as mutable
 --> borrowchecker.rs:3:18
  |
2 |     let a = 10;
  |         - consider changing this to `mut a`
3 |     let b = &mut a;
  |                  ^ cannot borrow mutably

Để không phải đau đầu với chuyện mượn/trả, moving của các biến, chúng ta có các quy tắc cần nhớ sau:

  • Một biến sau khi được gán thì không dùng được nữa (bị moved)
  • Muốn dùng một biến sau khi gán thì có thể borrow nó, hoặc implement Copy trait cho kiểu dữ liệu của nó
  • Một biến có thể được mượn để đọc (immutable borrow) không giới hạn số lần
  • Chỉ có duy nhất một lần mượn để ghi (mutable borrow) tại một thời điểm

Tạm thời viết chừng này đã, bài này nằm trong thư mục draft lâu quá rồi . Ở phần tiếp theo chúng ta sẽ tiếp tục tìm hiểu sang khái niệm còn lại, đó là Lifetime.

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