Home Blog Page 72

Promise, Async/Await và Map/Reduce

async

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

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

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

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

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

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

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

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

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

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

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

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

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

// Output:

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

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


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

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

increase(1);

// Output:

Promise { 2 }

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

// async increase :: number -> Promise number

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Output:

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

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

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

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

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

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

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

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

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

Video trong bài viết

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@extends('layouts.app')

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Kết thúc

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

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

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

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

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

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

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

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

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

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

JSON Web Token (JWT) là gì?

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

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

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

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

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

Authorization: Bearer <token>

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

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

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

  • Header
  • Payload
  • Signature

Chuỗi JWT: header.payload.signature

Ví dụ chuỗi JWT:

Trong đó:

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

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

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

Header

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

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

Payload

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

Reserved claim

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

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

Public claim

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

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

Private claim

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

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

Signature

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

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

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

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

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

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

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

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

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

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

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

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

Tại sao sử dụng JWT?

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

Tài liệu tham khảo:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Intern: Nguyễn Thị Ánh


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Click Add Maven.

Bỏ chọn ô check box Install Automatically

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

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

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

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

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

Click Add Maven

Chọn ô check box Install automatically

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

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

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

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

Okay, Done!

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

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

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

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

Bàn về Problem Solving Skill

problem solving skill
Bàn về Problem Solving Skill

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

Import RAML vào Postman

import raml
Import RAML vào Postman

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

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

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

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

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

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

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

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

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

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

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

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

Nhấn Close.

Kết quả:

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

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

Tuỳ theo nhu cầu thì các bạn có thể thay đổi biến này trong phần Environment của Postman các bạn nhé!

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

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

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

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

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

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

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

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

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

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

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

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

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

Diễn giả trong sự kiện

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

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

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

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

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

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

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

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

  • Chị Rosy Vũ – Panel Moderator

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

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

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

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

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

Agenda sự kiện

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

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

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

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

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

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

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

Tạo Jersey project

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

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

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>${jersey.version}</version>
</dependency>

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

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

JerseyServletContainerConfig.java

package com.gpcoder.config;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
//Deployment of a JAX-RS application using @ApplicationPath with Servlet 3.0
//Descriptor-less deployment
import org.glassfish.jersey.server.ResourceConfig;

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

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
    <display-name>RESTful CRUD Example by gpcoder</display-name>
    <servlet>
        <servlet-name>jersey2-serlvet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.gpcoder.config.JerseyServletContainerConfig</param-value>
        </init-param>
        <init-param>
                    <param-name>jersey.config.server.provider.classnames</param-name>
                    <param-value>org.glassfish.jersey.filter.LoggingFilter;
                        org.glassfish.jersey.jackson.JacksonFeature;
                        org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
            </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jersey2-serlvet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

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

FileService.java

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

package com.gpcoder.model;

import java.util.Date;

import lombok.Data;

@Data
public class FileUploadResponse {

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

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

package com.gpcoder.api;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

FileUtils.java

package com.gpcoder.utils;

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

public class FileUtils {

private FileUtils() {
super();
}

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

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

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

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

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

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

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

Tạo Jersey Client upload file

UploadFileWithJerseyRestClientExample.java

package com.gpcoder.client;

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

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

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

import com.gpcoder.model.FileUploadResponse;

public class UploadFileWithJerseyRestClientExample {

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

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

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

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

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

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

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

Tạo Jersey Client download file

DownloadFileWithJerseyRestClientExample.java

package com.gpcoder.client;

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

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

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

import com.gpcoder.utils.FileUtils;

public class DownloadFileWithJerseyRestClientExample {

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

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

public static void main(String[] args) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Tạo ứng dụng Java RESTful Client không sử dụng 3rd party libraries

java restful client
Tạo ứng dụng Java RESTful Client không sử dụng 3rd party libraries

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

Trong bài này tôi sẽ giới thiệu với các bạn cách gọi Restful web service sử dụng thư viện chuẩn java.net của Java, không sử dụng bất kỳ 3rd party libraries nào khác.

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

Để gọi restful web service thông qua lớp java.net chúng ta lần lượt thực hiện các bước sau:

  • Tạo 1 java.net.URL object.
  • Mở HttpURLConnection từ URL trên.
  • Set các Request property cần thiết.
  • Gửi Request data lên server (nếu có).
  • Nhận Response từ server gửi về (nếu có).
  10 lý do cho thấy tại sao bạn nên theo học ngôn ngữ lập trình Java
  10 tips để trở thành Java Developer xịn hơn

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

Ví dụ tạo ứng dụng Java RESTful Client sử dụng java.net

Trong ví dụ này, chúng ta sẽ gọi lại các Restful API chúng ta đã tạo ở bài viết trước “JWT – Token-based Authentication trong Jersey 2.x“.

Đầu tiên, chúng ta cần gọi API /auth để lấy token và sau đó chúng ta sẽ attach token này vào mỗi request để truy cập resource.

package com.gpcoder;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpClientExample {

public static final String BASE_URL = "http://localhost:8080/RestfulWebServiceExample/rest";
private static String token;

public static void main(String[] args) throws IOException {
token = getToken();
System.out.println("token: " + token);

createOrder();
retrieveOrder();
updateOrder();
deleteOrder();
}

/**
* @POST http://localhost:8080/RestfulWebServiceExample/rest/auth
*/
private static String getToken() throws IOException {
// Create A URL Object
URL url = new URL(BASE_URL + "/auth");

// Open a Connection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Set the Request Content-Type Header Parameter
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

// Set Response Format Type
connection.setRequestProperty("Accept", "application/json");

// Set the Request Method
connection.setRequestMethod("POST");

// Create the Request Body and Send post request
String urlParameters = "username=gpcoder&password=gpcoder";
sendRequest(connection, urlParameters);

// Read the Response from Input Stream
return getResponse(connection);
}

private static void sendRequest(HttpURLConnection connection, String data) throws IOException {
// Ensure the Connection Will Be Used to Send Content
connection.setDoOutput(true);

// Create the Request Body and Send post request
try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
wr.writeBytes(data);
wr.flush();
}
}

private static String getResponse(HttpURLConnection connection) throws IOException {
int responseCode = connection.getResponseCode();
System.out.println("Response Code : " + responseCode);

StringBuilder response = new StringBuilder();
try (InputStream is = connection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));) {
String line;
while ((line = rd.readLine()) != null) {
response.append(line);
}
}
return response.toString();
}

/**
* @POST http://localhost:8080/RestfulWebServiceExample/rest/orders
*/
private static void createOrder() throws IOException {
// Create A URL Object
URL url = new URL(BASE_URL + "/orders");

// Open a Connection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Set Authorization header
connection.setRequestProperty("Authorization", "Bearer " + token);

// Set the Request Content-Type Header Parameter
connection.setRequestProperty("Content-Type", "application/json");

// Set the Request Method
connection.setRequestMethod("POST");

// Create the Request Body and Send post request
String data = "{\"id\" : 1, \"name\": \"gpcoder\"}";
sendRequest(connection, data);

// Read the Response from Input Stream
String response = getResponse(connection);
System.out.println("createOrder: " + response);
}

/**
* @GET http://localhost:8080/RestfulWebServiceExample/rest/orders/1
*/
private static void retrieveOrder() throws IOException {
// Create A URL Object
URL url = new URL(BASE_URL + "/orders/1");

// Open a Connection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Set Authorization header
connection.setRequestProperty("Authorization", "Bearer " + token);

// Set the Request Method
connection.setRequestMethod("GET");

// Read the Response from Input Stream
String response = getResponse(connection);
System.out.println("retrieveOrder: " + response);
}

/**
* @PUT http://localhost:8080/RestfulWebServiceExample/rest/orders
*/
private static void updateOrder() throws IOException {
// Create A URL Object
URL url = new URL(BASE_URL + "/orders");

// Open a Connection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Set Authentication header
connection.setRequestProperty("Authorization", "Bearer " + token);

// Set the Request Content-Type Header Parameter
connection.setRequestProperty("Content-Type", "application/json");

// Set the Request Method
connection.setRequestMethod("PUT");

// Create the Request Body and Send post request
String data = "{\"id\" : 1, \"name\": \"gpcoder\"}";
sendRequest(connection, data);

// Read the Response from Input Stream
String response = getResponse(connection);
System.out.println("updateOrder: " + response.toString());
}

/**
* @DELETE http://localhost:8080/RestfulWebServiceExample/rest/orders/1
*/
private static void deleteOrder() throws IOException {
// Create A URL Object
URL url = new URL(BASE_URL + "/orders/1");

// Open a Connection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Set Authorization header
connection.setRequestProperty("Authorization", "Bearer " + token);

// Set the Request Method
connection.setRequestMethod("DELETE");

// Read the Response from Input Stream
String response = getResponse(connection);
System.out.println("deleteOrder: " + response);
}
}

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

Response Code : 200
token: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJncGNvZGVyIiwicm9sZXMiOlsiQWRtaW4iLCJDdXN0b21lciJdLCJqdGkiOiIwNWQwZTE5ZS1lODYzLTQ5MGQtOGE0Yi1mZWE4NWQ0OTZhYzIiLCJpYXQiOjE1NjE4MTgxMTgsImlzcyI6Imh0dHBzOi8vZ3Bjb2Rlci5jb20iLCJleHAiOjE1NjE4MTk5MTh9.fhhogziLVLMGz_nZjpFy9N0-MG2t4fOYKl6LSU7tLxo
Response Code : 200
createOrder: OrderService->insert()
Response Code : 200
retrieveOrder: OrderService->get()
Response Code : 200
updateOrder: OrderService->update()
Response Code : 200
deleteOrder: OrderService->delete()

Lưu ý: toàn bộ các API trả về kết quả là plain/text nên tôi không cần xử lý gì thêm. Nếu API các bạn nhận được là một chuỗi json, các bạn có thể sử dụng các thư viện như Gson hay Jackson để convert json về java object.

Trên đây là ví dụ đơn giản để gọi các Restful API sử dụng thư viện chuẩn java.net. Trong các dự án người ta thường ít sử dụng cách này do viết khá dài dòng và phức tạp. Trong các bài viết tiếp theo, tôi sẽ giới thiệu với các bạn các thư viện rất đơn giản và hiệu quả để tạo ứng dụng Java RESTful Client như Jersey client, OkHttp, Retrofit, Feign.

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

Làm việc với Apache Kafka Topic sử dụng CLI

apache kafka topic
Làm việc với Apache Kafka Topic sử dụng CLI

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

Sau khi cài đặt Apache Kafka, các bạn có thể sử dụng công cụ Apache Kafka CLI để làm việc với topic trong Apache Kafka server.

  Apache Kafka là gì?
  Cài đặt Apache Kafka sử dụng Docker Compose

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

Tạo mới topic

Chúng ta sẽ sử dụng tập tin kafka-topics.sh của Apache Kafka CLI để làm việc với topic trong Apache Kafka server.

Để tạo mới một topic, các bạn có thể sử dụng câu lệnh với cú pháp như sau:

kafka-topics.sh --create --topic <topic_name> --bootstrap-server <kafka_server> --partitions <partition_number> --replication-factor <replication_number>

Trong đó:

  • topic_name là tên topic mà chúng ta cần tạo
  • kafka_server là địa chỉ Apache Kafka server với định dạng host:port
  • partition_number là số lượng partitions mà chúng ta cần tạo
  • replication_number là số lượng replicate mà chúng ta muốn cho mỗi partition. Giá trị của tham số này sẽ phụ thuộc vào số lượng Apache Kafka server mà chúng ta đang có các bạn nhé!

Ví dụ, mình chạy câu lệnh sau:

kafka-topics.sh --create --topic huongdanjava --bootstrap-server localhost:9092 --partitions 2 --replication-factor 1

Kết quả sẽ như sau:

Để kiểm tra topic mà chúng ta vừa tạo ở trên trong Apache Kafka server, các bạn có thể chạy câu lệnh sau:

kafka-topics.sh --describe --topic <topic_name> --bootstrap-server <kafka_server>

Ý nghĩa của các tham số như mình đã đề cập ở trên.
Ví dụ của mình như sau:

kafka-topics.sh --describe --topic huongdanjava --bootstrap-server localhost:9092

Kết quả:

Như các bạn thấy, topic huongdanjava của mình có 2 partitions và số lượng ReplicationFactor là 1 như câu lệnh mình đã chạy ở trên.

Xem tất cả các topic

Để kiểm tra tất cả các topic có trong một Apache Kafka server, các bạn có thể chạy câu lệnh sau:

kafka-topics.sh --list --bootstrap-server localhost:9092

Các bạn sẽ thấy topic mà chúng ta vừa tạo ở trên như sau:

Xoá một topic

Để xoá một topic trong Apache Kafka server, chúng ta sẽ sử dụng câu lệnh sau:

kafka-topics.sh --list --bootstrap-server localhost:9092

Ví dụ của mình như sau:

kafka-topics.sh --delete --topic huongdanjava --bootstrap-server localhost:9092

Kết quả:

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

FPT Techday 2021 – nơi quy tụ các xu hướng công nghệ mới

Ngày 3/12, Diễn đàn Công nghệ FPT Techday 2021 diễn ra theo hình thức trực tuyến với chủ đề “Tái thiết toàn diện, bứt phá trong bình thường xanh”.

Diễn đàn đã thu hút hơn 100.000 người tham dự trực tiếp trên nhiều nền tảng khác nhau. Sự kiện một lần nữa giúp FPT khẳng định vai trò tiên phong chuyển đổi số hướng đến mục tiêu giúp các tổ chức, doanh nghiệp tái thiết toàn diện, bứt phá trong bình thường xanh, đồng thời tạo sân chơi trí tuệ cho các tài năng công nghệ trẻ.

Chia sẻ tại Diễn đàn Công nghệ FPT Techday 2021, ông Trương Gia Bình, Chủ tịch HĐQT FPT đã đặt lại bài toán chung về sứ mệnh của công nghệ trong bình thường xanh.

“COVID-19 ập đến như một tai họa. Doanh nghiệp đóng cửa, người lao động mất việc, cuộc sống đảo lộn. Chính trong thời điểm Covid-19 này chúng ta nhận ra rằng công nghệ đang mang một sứ mạng mới, công nghệ vị nhân sinh. Công nghệ vì cuộc sống của con người. Bằng công nghệ, chúng ta có thể chiến thắng COVID-19. Bằng công nghệ, các doanh nghiệp tái thiết mọi hoạt động quản trị, vận hành, kinh doanh để tăng tốc, bứt phá. Bằng công nghệ, người dân có thể thích ứng an toàn với dịch bệnh. Bằng công nghệ Việt Nam sẽ trở thành một điểm sáng phát triển kinh tế của thế giới, sẽ là một địa điểm hấp dẫn đầu tư nước ngoài và trở thành một quốc gia cường thịnh vào năm 2045” – ông Trương Gia Bình nhấn mạnh.

FPT Techday 2021 - nơi quy tụ các xu hướng công nghệ mới - Ảnh 1.

Tại sự kiện FPT Techday 2021, FPT đã ra mắt bộ giải pháp công nghệ gồm 6 sản phẩm giúp doanh nghiệp đảm bảo nguồn lực, vận hành linh hoạt, tăng tốc kinh doanh.

FPT eCovax nhân lực giúp doanh nghiệp đảm bảo nguồn lực dựa trên công nghệ AI và Big Data, thông qua việc số hóa toàn bộ quy trình tuyển dụng, hỗ trợ phỏng vấn sàng lọc, định hướng công việc phù hợp bằng Bot AI, lưu trữ – phân tích – dự báo nhu cầu của ứng viên.

FPT.AI Advisory Virtual Assistant – trợ lý ảo ứng dụng AI thế hệ thứ hai và giải pháp ký điện tử FPT.eContract và FPT.CA giúp doanh nghiệp tăng tốc kinh doanh ngay trong đại dịch. Còn với FPT AI Reader Flex và Ubot, mọi tài liệu văn bản với cấu trúc thông tin đa dạng, mọi nghiệp vụ trong doanh nghiệp đều được số hóa một cách dễ dàng và tự động hóa một cách trơn tru, giúp doanh nghiệp vận hành linh hoạt.

Cũng tại sự kiện, lần đầu tiên FPT trình diễn mô hình triển lãm thành phố xanh thông minh (Green Smart City) gồm 6 cấu phần chính: chính quyền xanh, doanh nghiệp xanh, giáo dục xanh, y tế xanh, cuộc sống xanh, lưu chuyển xanh. Triển lãm mang đến trải nghiệm đầy đủ về một thế giới bình thường xanh được tái thiết, vận hành, kết nối linh hoạt dựa trên nền tảng cốt lõi là công nghệ với gần 50 giải pháp, sản phẩm công nghệ của FPT, giúp vừa đảm bảo an toàn trong mọi tình huống vừa linh hoạt phát triển kinh tế.

FPT Techday 2021 - nơi quy tụ các xu hướng công nghệ mới - Ảnh 2.

Trong đó, chính quyền xanh – trái tim của thành phố xanh thông minh, được vận hành dựa trên các giải pháp công nghệ tiên tiến nhất giúp các nhà chức trách có được dữ liệu cập nhật theo thời gian thực về mọi hoạt động, từ đó ra quyết định kịp thời, chặt chẽ và ngay lập tức thấy được kết quả của sự thay đổi. Đặc biệt, trong bối cảnh COVID-19 diễn biến phức tạp trung tâm chỉ đạo xanh sẽ là nơi trao đổi và kết nối liên thông với các bộ chỉ huy để cùng bàn thảo và đưa ra lời giải cho các bài toán lớn mà không cần phải gặp trực tiếp.

Doanh nghiệp xanh, với sự hỗ trợ của công nghệ, có thể chủ động kiểm soát được tỷ lệ nhân sự xanh; quản trị, vận hành, làm việc từ xa. Hay khu vực y tế xanh mô phỏng một nền y tế không giấy tờ, với sự kết hợp nhịp nhàng của y bác sĩ với các hệ thống bệnh án điện tử, sổ khám bệnh điện tử… giúp công việc được thông suốt, giảm thời gian chờ đợi. Giáo dục xanh mang tới trải nghiệm học tập, giảng dạy giữa thầy và trò được kết nối thông suốt dựa trên công nghệ trí tuệ nhân tạo, phương pháp đào tạo kiến tạo xã hội giúp cho người học tích cực, chủ động và thích thú tham gia, gia tăng hiệu quả học tập.

Ông Nguyễn Văn Khoa, Tổng Giám đốc FPT chia sẻ: “COVID-19 đã khiến các doanh nghiệp nhìn nhận lại vai trò của công nghệ. Rất nhiều các doanh nghiệp đã chi hàng triệu USD để hoàn thiện khả năng quản trị và vận hành như Kim Tín, Boston Pharma, Tân Hoàng Minh, AceCook… Với hệ thống quản trị này, họ xác định rằng không để vuột mất cơ hội và bước vào kỷ nguyên số một cách vững chắc. Với năng lực về công nghệ, sự thấu hiểu và kinh nghiệm triển khai thành công rất nhiều dự án cho các chính phủ, doanh nghiệp trên thế giới và tại Việt Nam, chúng tôi tự tin và cam kết sẽ là một đối tác cùng đồng hành với các doanh nghiệp để tái thiết an toàn – linh hoạt – thần tốc trong bình thường xanh”.

Cùng với việc ra mắt các giải pháp công nghệ mới, ngay tại sự kiện, Giám đốc Công nghệ của FPT, ông Vũ Anh Tú cam kết: “Tập đoàn sẽ tiếp tục nghiên cứu và ứng dụng những công nghệ mới để giúp doanh nghiệp giải những bài toán một cách nhanh hơn, chính xác hơn và ưu việt hơn. Đồng thời tập trung vào việc kiến trúc lại các sản phẩm sẵn có để tạo ra một nền tảng mở để cộng đồng cùng tham gia phát triển​, có tính tin cậy và linh hoạt, có khả năng thấu hiểu và tự học hỏi để thông minh hơn, có tính bảo mật cao”.

Trong thời gian tới, FPT sẽ tập trung và phát triển 4 nền tảng công nghệ lõi: Hyper Automation; AI; Cloud và Blockchain.​

Với công nghệ Hyper Automation, các giải pháp của FPT sẽ giúp doanh nghiệp tự động hóa thông minh cho nhiều quy trình có giá trị lớn nhưng dữ liệu phi cấu trúc, phân mảnh, chẳn hạn như: phân tích tình trạng nợ công ty vay vốn; đối soát hóa đơn; tổng đài ảo gọi điện nhắc nhở tự động về khoản vay, tình trạng hồ sơ…​

Với công nghệ trí tuệ nhân tạo (AI), FPT sẽ tạo ra những trợ lý ảo thế hệ mới có khả năng suy luận gần như con người trên cơ sở những thông tin tiếp nhận từ các giác quan và xây dựng hệ sinh thái giải pháp ứng dụng AI phục vụ nhiều ngành công nghiệp khác nhau.

Trong lĩnh vực Cloud, FPT sẽ đẩy mạnh mô hình cung cấp dịch vụ PaaS (cung cấp nền tảng như một dịch vụ) và đặc biệt là SaaS (cung cấp phần mềm như một dịch vụ). Qua đó, công nghệ và sản phẩm công nghệ Made by FPT không chỉ phục vụ cho việc phát triển nội tại của FPT mà còn phụng sự cho bền vững, đột phá của khách hàng.​

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

Screencast cho các thiết bị iOS

screencast trên ios
Screencast cho các thiết bị iOS

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

Làm sao để cho Dev thấy màn hình của phone mình đang gặp 1 con bug? Cách đơn giản nhất là mang cái phone tới bàn làm việc của Dev. Nhưng nếu Dev không làm việc chung tòa nhà với Tester hoặc ở một quốc gia hay châu lục khác thì sao? Có một cách khá đơn giản đó là hiển thị màn hình của thiết bị trên PC và share màn hình PC với họ.

Đối với Android, việc này khá đơn giản vì có hằng tá các công cụ hỗ trợ, đặc biệt là AndroidScreenCast (hàng chính hãng & miễn phí luôn nhá.)

  5 bài học quí giá về việc phát triển ứng dụng iOS
  Build một ứng dụng Chat cho Android & iOS bằng Contus Fly như thế nào?

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

Với iOS thì hơi khó khăn hơn một chút nhưng không phải là không làm được. Dưới đây là vài bước cấu hình để bạn đạt được mục đích

  1. Đầu tiên các bạn cần cài iTunes
  2. Tiếp theo các bạn cần download và cài đặt iTool
  3. Coi như các bước chuẩn bị đã xong, các bạn gắn thiết bị iOS như iPhone, iPad vào PC thông qua cable
  4. Mở iTools & chọn Desktop, tiếp theo chọn “Live Desktop” và thế là bạn có thể thấy màn hình iOS trên PC.

iTools Desktop

iTools Live Desktop

Bạn biết những cách khác hoặc tool khác, hãy chia sẻ tiếng nói của mình :). Mình rất mong nhận được những đóng góp, gợi ý cũng như những lời đề nghị để nội dung của các bài viết trên VNTesters ngày càng phong phú và hữu ích cho mọi người, đặc biệt là các bạn Tester (kỹ sư kiểm thử phần mềm) chuyên nghiệp.

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

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

Paper Review: Why Functional Programming Matters

paper review

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

Như đã có lần mình đề cập, việc đọc paper cũng khá là quan trọng, vì bên cạnh việc được đọc từ những nguồn kiến thức “sạch”, và chất lượng, chúng ta còn sẽ xây dựng được cho mình một kĩ năng khác, là kĩ năng đọc sâu và kĩ năng nghiên cứu, khá là quan trọng trong thời đại thông tin tràn lan trên Internet, khi mà chúng ta tiếp xúc với một lượng lớn thông tin nhưng không có thời gian để lãnh hội hết, dẫn tới một thói quen hết sức tai hại đó là đọc một cách hời hợt (shallow reading).

Nằm trong hoạt động nghiên cứu của nhóm rbvn/algorithm, tuần này mình đọc một paper khá thú vị đó là Why Functional Programming Matters của John Hughes. Dưới đây là một vài ghi chép của mình trong quá trình đọc, share lên để giúp cho các bạn có cái nhìn toàn cảnh về nội dung của paper, giúp cho việc đọc trở nên dễ dàng hơn, và hoàn toàn không có mục đích tổng hợp, tóm tắt.

  5 điều NÊN và KHÔNG NÊN khi review tăng lương mà lập trình viên nào cũng nên biết!
  Những gì tôi học được từ 1000 code reviews

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

■  Trong paper này cũng có vài đoạn đọc vào khá là thú vị, mình sẽ không ghi ra đây mà để cho các bạn tự đọc và tự thấy. Ông tác giả cũng khá là hài hước mà không kém phần nghiêm túc.

Các bạn có thể tìm đọc paper này tại: https://academic.oup.com/comjnl/article/32/2/98/543535

Đây là một trong số những paper mà bất cứ ai quan tâm đến computer science đều phải đọc ít nhất một lần trong đời, và khá là dễ đọc, trừ vụ code minh họa được viết bằng Miranda (a.k.a daed lang ), trông khá giống với Haskell.

Quan điểm mà tác giả John Hughes đưa ra trong bài là: Một dự án phần mềm cần phải được tổ chức một cách tốt nhất có thể, để dễ viết hơn, dễ debug hơn, reuse code tốt hơn. Và yếu tố quan trọng nhất để có một cấu trúc project tốt là Modularity (chia project thành nhiều module nhỏ).

Tác giả viết:

Modular design brings with it great productivity improvements. First of all, small modules can be coded quickly and easily. Secondly, general purpose modules can be re-used, leading to faster development of subsequent programs. Thirdly, the modules of a program can be tested independently, helping to reduce the time spent debugging.

Vậy modularize là làm gì? Là chia nhỏ nó ra thành nhiều bài toán con, giải từng bài con đó rồi tổng hợp chúng lại:

When writing a modular program to solve a problem, one first divides the problem into sub- problems, then solves the sub-problems and combines the solutions.

Và các functional programming languages cung cấp cho chúng ta 2 features quan trọng để phục vụ cho việc modularize, đó là:

  • Higher-order Function
  • Lazy Evaluation

Đối với higher-order function, tác giả đưa ra một loạt ví dụ minh họa, như là xây dựng một hàm reduce, nhận vào một hàm bất kỳ (thể hiện đặc tính của higher-order function) để thực hiện tính toán trên một list, từ đó thực hiện việc tính toán các phép toán bất kỳ chỉ bằng việc kết hợp (compose) các chương trình nhỏ hơn (module) với hàm reduce:

reduce :: (a -> a -> a) -> a -> [a] -> a
reduce f a [] = a
reduce f a (x:xs) = f x (reduce f a xs)

-- tính tổng một list
add a b = a + b
sum = reduce add 0
sum [1, 2, 3, 4] == 10

-- tính tích một list
multiply a b = a * b
product = reduce multiply 1
product [1, 2, 3, 4] == 24

Về lazy evaluation, tác giả giải thích bằng cách đưa ra một ví dụ hết sức đơn giản nhưng mà đọc lâu thiệt lâu mới hiểu: Giả sử có hai chương trình con (hoặc là hàm) f và g, chúng ta có thể compose chúng như sau:

(g . f) input
-- tương đương với
g (f input)

Có nghĩa là, ta sẽ gọi hàm f với tham số là input, đồng thời lấy kết quả trả về để làm tham số cho câu lệnh gọi hàm g.

Đặc tính laziness của Haskell và các functional programming languages nằm ở chỗ, việc thực thi f và g diễn ra một cách đồng bộ và chặt chẽ (ai dịch chữ strict synchronisation cho tui với), f chỉ được thực thi và trả về giá trị khi g thực hiện đọc tham số đầu vào của nó, và f cũng sẽ bị dừng khi g dừng lại.

The two programs f and g are run together in strict synchronisation. F is only started once g tries to read some input, and only runs for long enough to deliver the output g is trying to read. Then f is suspended and g is run until it tries to read another input. As an added bonus, if g terminates without reading all of f’s output then f is aborted.

Và trong trường hợp f là một hàm sinh ra một tập giá trị có số lượng cực kì lớn, nếu implement trên các ngôn ngữ thông thường (thực thi xong f, lấy kết quả, truyền vào g) thì có thể sẽ cần một lượng bộ nhớ cực kì lớn, mà có khi là không đủ, ví dụ:

-- trả về danh sách các số từ input đến +∞
f input = [input..]

Như thế, nhờ vào đặc tính lazy evaluation, chúng ta có thể dễ dàng phân tách chương trình thành nhiều module nhỏ mà không cần quá bận tâm đến time và space complexity. Có thể thấy rõ qua ví dụ tính căn bậc 2 dùng phương pháp Newton-Raphson.

■  Trước khi đọc tiếp, các bạn nên dừng lại và tìm đọc về thuật toán tìm căn bậc hai theo phương pháp Newton-Raphson trước nhé.
import Prelude hiding (sqrt)

next N x = (x + N/x)/2

within eps (a:b:rest) =
  if abs (a - b) <= eps then
    b
  else
    within eps (b:rest)

sqrt N = within 0.0001 (iterate (next N) 1.0)
■  Đoạn code đã được convert sang Haskell để dễ chạy, không kiếm ra cái Miranda compiler nào cả, lệnh repeat chính là iterate trong Haskell.

Ở đây, output của câu lệnh iterate (next N) 1.0 là một list dài vô hạn, nhưng trong câu lệnh within 0.0001 (iterate (next N) 1.0), nó sẽ dừng lại ngay khi chúng ta tìm được hai giá trị xấp xỉ nhau. Thế mới thấy được ưu điểm của lazy evaluation.

Tác giả kết thúc phần kĩ thuật bằng ví dụ ở mục 5: thuật toán Alpha-beta để implement trò chơi tic-tac-toe, minh họa một cách đầy đủ cả hai ứng dụng của higher-order function và lazy evaluation để định nghĩa gametree và sinh ra các nước đi cho cây trò chơi (vốn là một thao tác cực kì tốn kém).

Thông qua paper này, tác giả đã khẳng định được tầm quan trọng của functional programming, cũng như trả lời được câu hỏi mà có lẽ nhiều người cũng hay hỏi: “Why Functional Programming?”, thông qua việc làm rõ 3 vấn đề sau:

  • Modularity là yếu tố quan trọng để phát triển một phần mềm tốt.
  • Higher-order function và lazy evaluation là hai feature quan trọng của các functional programming languages giúp cho việc modularize hiệu quả hơn, giúp cho việc phát triển được productive hơn.
  • Suy ra Functional Programming đóng vai trò quan trọng trong việc bảo đảm chất lượng và productivity của các dự án phần mềm.

Hy vọng những ghi chép trên sẽ có ích cho các bạn nào đang có ý định tự mình đọc paper này. Hoặc giúp các bạn phần nào trả lời cho câu hỏi liệu mình có nên dấn thân vào tìm hiểu FP hay không.

Còn bây giờ thì mình phải tạm gác khoa học sang một bên, vợ bắt đi ngủ rồi, hẹn gặp lại các bạn trong các bài review paper sắp tới.

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