Action Result trong ASP.NET Core

Bài viết này chúng ta sẽ tìm hiểu làm sao để định dạng một response trả về trong Action method. ASP.NET Core cung cấp một tập các API gọi là Action Result để tạo ra các định dạng response cho từng nhu cầu. Hãy cùng tìm hiểu về Action Result là gì vầ sự khác nhau giữa các loại Action Result có sẵn nhé.

Action Result là gì?

Controller Action sẽ trả về kết quả cho client. Client có thể mong muốn một kết quả đơn giản như là chuỗi hay số nguyên hoặc một kết quả phức tạp như là JSON hay HTML view hoặc file để download.

Controller trong ASP.NET Core chỉ đơn giản là các class C#. Nó không cần phải kế thừa từ bất cứ base class nào.Nhưng ASP.NET Core cung cấp một class Controller nó kế thừa từ một ControllerBase class. Điều này giúp class cung cấp rất nhiều các method hữu ích, nó giúp Controller làm việc dễ dàng hơn. Vì thế thông thường các controller của chúng ta đều kế thừa từ Controller class.

Controller Base class triển khai các loại Action Result khác nhau sẵn có giúp xây dựng các loại kết quả trả về khác nhau cho client. Ví dụ, ViewResult trả về một HTML response. Một RedirectResult chuyển hướng đến URL khác. Content Result trả về một chuỗi văn bản. Các kiểu trả về này được biết đến là Action Result.

IActionResult và ActionResult

IActionResult là một interface nó định nghĩa một khuôn mẫu cho toàn bộ các Action Result của một action method. ActionResult là một abstract base class triển khai interface IActionResult. Action result như ViewResult, PartialViewResult hay JsonResult...đều kế thừa từ ActionResult base class.

Tại sao lại dùng Action Result?

Không cần phải sử dụng Action Result trong ASP.NET Core. Các Controller không cần phải kế thừa từ Controller class. Bạn có thể thao tác trự tiếp với đối tượng HTTP Response trong Controller và quyết định xem trả về gì?

Ví dụ dưới đây mô tả cách inject đối tượng HttpContext vào Constructor của Controller và sau đó sử dụng nó để tạo ra Response:

public class HomeController {
   HttpContext ctx;
   public HomeController(IHttpContextAccessor _ctx) {
       ctx = _ctx.HttpContext;        
   }
    
   public async void Index() {
       //Set Status Code
       ctx.Response.StatusCode = 200;
       //Set Content Type
       ctx.Response.ContentType = "text/html";
       //Create Response
       ctx.Response.Headers.Add("SomeHeader", "Value");            
       byte[] content = Encoding.ASCII.GetBytes($"<html><body>Hello World</body></html>");
       //Send it to the Client
       await ctx.Response.Body.WriteAsync(content, 0, content.Length);                    
   }
}

Mặc dù cách tiếp cận này vẫn làm việc bình thường nhưng nó không phải là cách tốt nhất để tạo ra Response từ Controller. Xử lý tất cả các controller như thế này sẽ nhàm chán, dễ sinh lỗi và khó bảo trì. Action Result đóng gói tất cả các chi tiết vào trong nó. Nó có nhiều các tính năng hữu dụng giúp dễ dàng hơn trong việc tạo response.

Tạo ra Response trực tiếp từ Controller class như ví dụ trên. Nó sẽ làm khó cho Unit Test. Unit Test một Controller class sẽ cần phải mock các triển khai của Response object. Để test kế quả chúng ta lại cần chuyển nó sang HTML Response.

Action Result sẽ có tất cả những gì cần thiết để chạy Controller và Action method. Đối tượng context sẽ dễ dàng được giả lập thông qua base class của nó. Bạn không cần phải chuyển bất cứ kết quả sang HTML để kiểm trả kết quả của một action method. Bạn có thể kiểm tra ngay đối tượng ActionResult để đảm bảo rằng bạn nhận kết quả như mng muốn.

Làm sao để sử dụng Action Result?

Như đã nhắc đến ở trên, ActionResult là một abstract class base mà triển khai IActionResult. Nó được định nghĩa trong namespace: Microsoft.AspNetCore.Mvc. ContentResult là một trong số các Action Result trả về một chuỗi văn bản:

public class HomeController : Controller {
 
    public ContentResult Index()
    {
        ContentResult v = new ContentResult();
        v.Content = "Hello World";
        return v;
    }
}

Phương thức Index trả về một ContentResult. Đầu tiên chúng ta khởi tạo đối tượng của ContentResult sau đó gán giá trị "Hello World" cho thuộc tính Content của nó và trả về.

Chúng ta có một Helper method của Controller base class đó là View nó cũng làm việc tương tự nhưng code ngắn hơn nhiều:

public ContentResult Index()
{
    return Content("Hello");
}

Phương thức Content cũng gọi ContentResult ngầm định. Hầu hết các Action Result đều có helper method được định nghĩa trong Controller base class. Thường thì các phương thức này có từ "Result". Ví dụ: Content cho ContentResult, View cho ViewResult.

Kiểu trả về

Phương thức Index ở trên trả về một ContentResult. Cách ưa chuộng hơn là sử dụng ActionResult như là một kiểu trả về:

public ActionResult Index()
{
    return Content("Hello");
}

Trả về ActionResult thay vì kiểu thực tế giúp chúng ta sử dụng bất cứ Action Result nào:

public ActionResult Index(int id)
{
    if (id==0) {
       return NotFound();
    }
    else  {
       return Content("Hello");
    }
}

Phương thức Index trả về 2 kiểu Action Result là NotFoundResult và ContentResult phụ thuộc giá trị của tham số id.

Các loại Action Result

Có nhiều loại Action Result có sẵn trong Microsoft.AspNetCore.Mvc namespace. Nó được phân loại theo công năng sử dụng:

  1. Trả về HTML
  2. Chuyển hướng người dùng
  3. Trả về file
  4. Trả về nội dung văn bản
  5. Trả về lỗi và HTTP Code
  6. Kết quả liên quan đến bảo mật

Trả về HTML

Có 2 Action result trả về HTML Response. ViewResult và PartialViewResult.

ViewResult

Phương thức View() tìm kiếm View trong thư mục Views/<Controller> để tìm file .cshtml và chuyển nó cho Razor View Engine. Bạn có thể gán cho nó model dữ liệu. View sẽ trả về một ViewResult và kết quả là một HTML Response.

Mở HomeController và copy đoạn code sau:

public ActionResult Index()
{
    var movie = new Movie() { Name = "Avatar" };
    return View(movie);
}

Phương thức Index gọi View() sẽ trả về một ViewResult.

PartialViewResult

PartialView Result sử dụng model để tạo ra một phần của View. Chúng ta sử dụng ViewResult để tạo ra một view hoàn chỉnh còn PartialView trả về một phần của View. Kiểu trả về này hữu ích với Single Page Application (SPA) ki bạn muốn cập nhật một phần của View thông qua AJAX.

public ActionResult Index()
{
    var movie = new Movie() { Name = "Avatar" };
    return PartialView(movie);
}

Chuyển hướng người dùng

Redriect result được dùng khi bạn muốn chuyển hướng người dùng đến một URL khác. Có 4 loại redirect result có sẵn. RedirectResult, LocalRedirectResult, RedirectToActionResult và RedirectToRouteResult.

Mỗi một redirect này có thể trả về bất cứ mã trạng thái (status code) dưới đây:

  1. 302 Found (Chuyển tạp thời)   
  2. 301 Moved Permanently 
  3. 307 Temporary Redirect 
  4. 308 Permanent Redirect 

RedirectResult

RedirectResult sẽ trả về cho user bằng cách cung cấp đường dẫn tuyệt đối hoặc tương đối:

ACTION RESULT CONTROLLER METHOD STATUS CODE
RedirectResult Redirect 302 Found (Temporarily moved)   
RedirectPermanent 301 Moved Permanently
RedirectPermanentPreserveMethod 308 Permanent Redirect
RedirectPreserveMethod 307 Temporary Redirect 

Ví dụ:

Redirect("/Product/Index");
RedirectPermanent("/Product/Index");
RedirectPermanentPreserveMethod("/Product/Index");
RedirectPreserveMethod("/Product/Index");
 

Thay vào đó, bạn có thể sử dụng RedirectResult trực tiếp được định nghĩa trong Microsoft.AspNetCore.Mvc. Cú pháp là: RedirectResult(string url, bool permanent, bool preserveMethod)

return new RedirectResult(url:"/Product/Index", permanent: true, preserveMethod: true);

LocalRedirectResult

Action result này tương tự như RedirectResult nhưng chỉ khác một điều. Chỉ các local URL mới được chấp nhận. Nếu bạn cung cấp bất cứ một URL ngoài nào, phương thức này sẽ trả về một lỗi InvalidOperationException. Điều này tránh việc bị tất công open redirect attack.

ACTION RESULT CONTROLLER METHOD STATUS CODE
LocalRedirectResult LocalRedirect 302 Found (Temporarily moved)   
LocalRedirectPermanent 301 Moved Permanently
LocalRedirectPermanentPreserveMethod 308 Permanent Redirect
LocalRedirectPreserveMethod 307 Temporary Redirect 

Ví dụ:

LocalRedirect("/Product/Index");
LocalRedirectPermanent("/Product/Index");
LocalRedirectPermanentPreserveMethod("/Product/Index");
LocalRedirectPreserveMethod("/Product/Index");

RedirectToActionResult

Action result này chuyển client đến một action và controller cụ thể. Nó nhận một tên Action method, một tên controller và các giá trị tham số:

ACTION RESULT CONTROLLER METHOD STATUS CODE
RedirectToActionResult RedirectToAction 302 Found (Temporarily moved)   
RedirectToActionPermanent 301 Moved Permanently
RedirectToActionPermanentPreserveMethod 308 Permanent Redirect
RedirectToActionPreserveMethod 307 Temporary Redirect 

Ví dụ:

//Redirects to test action in AboutUsController
RedirectToAction(actionName: "Index", controllerName: "AboutUs");
 
//Redirects to Index action in the current Controller
RedirectToAction(actionName: "Index");

RedirectToRouteResult

Action result này chuyển khách hàng đến một route cụ thể. Nó nhận tên route, giá trị của route và chuyển chúng ta đến vị trí mà route cung cấp:

ACTION RESULT CONTROLLER METHOD STATUS CODE
RedirectToRouteResult RedirectToRoute 302 Found (Temporarily moved)   
RedirectToRoutePermanent 301 Moved Permanently
RedirectToRoutePermanentPreserveMethod 308 Permanent Redirect
RedirectToRoutePreserveMethod 307 Temporary Redirect 

Ví dụ: 

// Redirect using route name
return RedirectToRoute("default");
 
//Redirect using Route Value
var routeValue = new RouteValueDictionary(new { action = "Index", controller = "Home"});
return RedirectToRoute(routeValue);

 

Trả về file

FileResult là một Action result sử dụng bởi Controller action để trả về file cho người dùng.

FileResult

FileResult là một base class sử dụng để gửi file nhị phân về response. Nó là một abstract class được triển khai bởi FileContentResult, FileStreamResult, VirtualFileResult, và PhysicalFileResult. Các class này đảm nhiệm công việc gửi file về client.

FileContentResult

FileContentResult đọc một mảng byte và trả về như một file:

return new FileContentResult(byteArray, "application/pdf")

FileStreamResult

FileStreamResult đọc một luồng stream và trả về một file:

return new FileStreamResult(fileStream, "application/pdf");

VirtualFileResult

Action result này đọc nội dung của một file từ một đường dẫ ntuowng đối của ứng dụng trên hosting và gửi nội dung về client dưới dạng 1 file:

return new VirtualFileResult("/path/filename", "application/pdf");

PhysicalFileResult

Action result này đọc nội dung của một file từ một vị trí vật lý và gửi nội dung về client như một file. Chú ý là đường dẫ phải là đường dẫn tuyệt đối:

return new PhysicalFileResult("c:/path/filename", "application/pdf");

Content Result

JsonResult

Action result này trả về dữ liệu được định dạng JSON. Nó chuyển một object sang JSON và trả nó về client:

public JsonResult About()
{
    return Json(object);
}
 
or
 
public JsonResult About()
{
    return new JsonResult(object);
}

ContentResult

ContentResult ghi một nội dung cụ thể trực tiếp vào response như một chuỗi định dạng văn bản thuần.

public ContentResult About()
{
    return Content("Hello World");
}
 
hoặc
 
public ContentResult About()
{
    return new ContentResult() { Content = "Hello World" };
}

EmptyResult

EmptyResult giống như tên của nó không chứa cái gì cả. Sử dụng nó khi bạn muốn thực thi một số logic trong controller nhưng không muốn trả về gì.

return new EmptyResult();

Trả về lỗi và HTTP Code

Loại Action result này được dùng trong Web API Controller. Kết quả sẽ được gửi về kèm HTTP Status Code. Một trong số chúng thì có thể gửi một đối tượng vào response.

StatusCodeResult

StatusCodeResult gửi kết quả và chỉ ra một HTTP Status code:

return StatusCode(200);
hoặc
return new StatusCodeResult(200);

ObjectResult

Action result này sẽ trả về một đối tượng kèm một HTTP Status Code là 200. Nó là một overload của method StatusCode

return StatusCode(200, new { message = "Hello" });
hoặc
return new ObjectResult(new { message = "Hello" });

OkResult

Action result này trả về nguyên chỉ có HTTP Status code 200:

return Ok();
hoặc
return new OkResult();

OkObjectResult

Action result này trả về một HTTP Status code 200:

return Ok(new {message="Hello"});
hoặc
return new OkObjectResult(new { message = "Not found" });

CreatedResult

CreatedResult sử dụng khi một tài nguyên được tạo ra sau request Post. Nó gửi trạng thái 201 về kèm đối tượng vừa được tạo:

return Created(new Uri("/Home/Index", UriKind.Relative), new {message="Hello World"});
hoặc
return new CreatedResult(new Uri("/Home/Index", UriKind.Relative), new { message = "Hello World" });

CreatedAtActionResult

Cái này tương tự CreatedResult nhưng nó nhận vào Controller và Action thay vì URL:

return CreatedAtAction("Index", new {message="Hello World"});

CreateAtRouteResult

Action Result này nhận vào gái trị route và tương tự như CreatedResult và CreatedAtActionResult

CreatedAtRoute("default", new { mesage = "Hello World" });

BadRequestResult

Action result này gửi về một HTTP Status code 400 cho client. Sử dụng response status code này khi chỉ ra cú pháp không đúng hoặc một request không được rõ ràng.

return BadRequest()
 

BadRequestObjectResult

Action result này tương tự BadRequestResult. Khác nhau là bạn có thể gửi về một ModelStateDictionary (chứa chi tiết lỗi) và cũng là status 400:

var modelState = new ModelStateDictionary();
modelState.AddModelError("message", "errors found. Please rectify before continuing");
return BadRequest(modelState);

Phương thức BadRequest có một overload thứ 2, trả về một BadRequestObjectResult

NotFoundResult

Action result này trả về lỗi HTTP 404 cho client:

return NotFound();

NotFoundObjectResult

Action result này tương tự nuhw NotFoundResult nhưng trả về một đuối tượng kèm lỗi 404. Overload thứ 2 của NotFound giúp nhận một đối tượng làm tham số để trả về NotFoundObjectResult.

return NotFound( new { message = "Not found" });

UnsupportedMediaTypeResult

Action result này gửi về lỗi HTTP 415. Sử dụng action result này khi request với định dạng không được hỗ trợ bởi server.

return new UnsupportedMediaTypeResult();

NoContentResult

Action result này gửi lỗi HTTP 204 về. Sử dụng NoContentResult này khi request thành công nhưng không có nội dung nào được trả về

return NoContent();

Kết quả liên quan bảo mật

SignInResult

SignInResult là kết quả của hành động đăng nhập. SignInManager.SignInAsync hoặc PasswordSignInAsync trả về một SignInResult. Nó có 4 thuộc tính là Succeeded, IsLockedOut, IsNotAllowed và RequiresTwoFactor

Bạn có thể tham khảo về ASP.NET Identity Core.

SignOutResult

SignOutResult là kết quả của hành động logout

ForbitResult

ForbitResult trả về lỗi 403 tức là người dùng không được cấp quyền để thực hiện một hành động nào đó trên tài nguyên nào đó. ForbitResult không có nghĩa là người dùng chưa chứng thực. Người dùng chưa chứng thực nên trả về ChallengeResult hoặc UnAuthorisedResult.

return new ForbidResult();
 
hoặc
 
return Forbid();

Forbit là phương thức của Controller base class trả về thể hiện của ForbidResult. Thay vào đó bạn có thể trả về Status Code:

return StatusCode(403);

ChallengeResult

ChallengeResult trả về khi chứng thực thất bại. Kết quả sẽ không gọi đến baatscuws middleware nào để tạo response. Ví dụ trả về lỗi 401 (Unauthorized) hoặc 403 (Forbidden) hoặc chuyển hướng người dùng đến trang đăng nhập.

UnauthorizedResult

UnauthorizedResult trả về lỗi “401 – Unauthorized”. Controller sử dụng phương thức Unauthorized để trả về thể hiện của UnauthorizedResult.

return Unauthorized();
 
hoặc
 
return new UnauthorizedResult();

Khác nhau giữa UnauthorizedResult và ChallengeResult là một thằng trả về Status Code còn 1 thằng là không làm gì với nó.


Trích nguồn từ: (https://www.tektutorialshub.com/)

Lên trên