2010-02-22 67 views
10

Một số hành động điều khiển của tôi có một bộ hành vi xử lý lỗi tiêu chuẩn. Nói chung, tôi muốn:Tạo ViewResults ngoài Bộ điều khiển trong ASP.NET MVC

  • tải một đối tượng dựa trên dữ liệu Route (ID và những thứ tương tự)
    • Nếu dữ liệu Route không trỏ đến một đối tượng hợp lệ (ví dụ: thông qua URL hack) sau đó thông báo cho người sử dụng của vấn đề và trả về một HTTP 404 Not Found
  • Validate mà người dùng hiện tại có các điều khoản thích hợp trên đối tượng
    • Nếu người dùng không được phép, thông báo cho người dùng về sự cố và trả lại HTTP 403 Forbidden
  • Nếu điều này thành công, hãy làm điều gì đó với đối tượng cụ thể theo hành động (ví dụ: hiển thị trong chế độ xem).

Các bước này được chuẩn hóa đến mức tôi muốn có mã có thể sử dụng lại để thực hiện hành vi.

kế hoạch hiện tại của tôi về cuộc tấn công là để có một phương pháp helper để làm một cái gì đó như thế này:

public static ActionResult HandleMyObject(this Controller controller, 
    Func<MyObject,ActionResult> onSuccess) { 
    var myObject = MyObject.LoadFrom(controller.RouteData). 
    if (myObject == null) return NotFound(controller); 
    if (myObject.IsNotAllowed(controller.User)) return NotAllowed(controller); 
    return onSuccess(myObject); 
} 

# NotAllowed() is pretty much the same as this 
public static NotFound(Controller controller){ 
    controller.HttpContext.Response.StatusCode = 404 
    # NotFound.aspx is a shared view. 
    ViewResult result = controller.View("NotFound"); 
    return result; 
} 

Vấn đề ở đây là Controller.View() là một phương pháp bảo vệ và như vậy là không thể truy cập từ một helper . Tôi đã xem xét việc tạo một thể hiện ViewResult mới một cách rõ ràng, nhưng có đủ thuộc tính để thiết lập rằng tôi cảnh giác về việc làm như vậy mà không biết những cạm bẫy trước tiên.

Cách tốt nhất để tạo ViewResult từ bên ngoài một Bộ điều khiển cụ thể là gì?

Trả lời

4

Khi tôi viết bài này, tôi đã nghĩ về một cách.

Thay vì có mã trên trong trình trợ giúp, tôi có thể đặt nó vào một lớp con của Bộ điều khiển và sau đó phân lớp lớp này cho bộ điều khiển thực của tôi. Điều này sẽ cho phép tôi gọi phương thức View() được bảo vệ.

Tôi không thích điều này đặc biệt nhiều bởi vì nó requires inheritance to work, nhưng nó vẫn là một tùy chọn.

+0

+1: Đó là cách tôi sẽ làm. –

+0

Điều tôi không thích về điều này là nó có nghĩa là một bộ điều khiển chỉ có thể có một bộ hành vi hành vi được chia sẻ: mà nó (đơn lẻ) kế thừa. Đối với ví dụ cụ thể này, nó không phải là một vấn đề, nhưng tôi có thể thấy nó khó sử dụng hơn khi dự án phát triển. –

+0

Tôi nhận ra câu hỏi của bạn là năm cũ, nhưng tôi chỉ nghĩ rằng tôi muốn đề cập đến một khả năng khác. Bạn có thể trưng ra một phiên bản công khai của 'View()' được gọi là 'GetView()' trên bộ điều khiển cơ sở của bạn, sau đó bạn có thể giữ helper của bạn trong một lớp riêng biệt. Bạn vẫn sẽ cần phải vượt qua bộ điều khiển của bạn vào helper của bạn, đó là một chút khó xử, nhưng nó cho phép thành phần thay vì thừa kế. – devuxer

0

Một cách khác sẽ được sử dụng trang trí:

public class StatusCodeViewResultDecorator : ViewResult { 
    ViewResult wrapped; 
    int code; 
    string description; 

    public StatusCodeViewResultDecorator(ViewResult wrapped, int code, string description) { 
    this.wrapped = wrapped; 
    this.code = code; 
    this.description = description; 
    } 

    public override void ExecuteResult(ControllerContext context) { 
    wrapped.ExecuteResult(context); 
    context.RequestContext.HttpContext.Response.StatusCode = code; 
    context.RequestContext.HttpContext.Response.StatusDescription = description; 
    } 
} 

Và có lẽ một phương pháp mở rộng để làm cho nó sạch hơn:

public static class ViewResultExtensions { 
    public static ViewResult WithStatus(this ViewResult viewResult, int code, string description) { 
    return new StatusCodeViewResultDecorator(viewResult,code,description); 
    } 
} 

Bạn có thể sau đó chỉ cần nói:

return View("MyView").WithStatus(404,"Not found"); 

tại của bạn điều khiển.

+1

Điều này không thực sự hữu ích, bởi vì bạn vẫn phải tạo cơ sở ViewResult trong bộ điều khiển, đó là những gì tôi đang cố gắng tránh xa. –

2

Tôi đã có cùng một câu hỏi và trả lời một cách khác nhau. Tôi thực sự không muốn sử dụng thừa kế cho điều này, vì vậy tôi đã sử dụng một lambda thay thế.

Trước tiên, tôi có một đối tượng mà tôi vượt qua từ bộ điều khiển của tôi để phương pháp này tôi muốn quay trở lại quan điểm:

public struct MyControllerContext 
{ 
    public HttpRequestBase Request { get; set; } 
    public HttpResponseBase Response { get; set; } 
    public DocsController Controller { get; set; } 

    public Func<string, object, ViewResult> ViewResult; 
    public ViewResult View(string viewName, object model) 
    { 
     return this.ViewResult(viewName, model); 
    } 
} 

tôi tạo một thể hiện của này và vượt qua nó như là tham số để phương pháp đó sẽ trả lại kết quả:

// In the controller 
var context = new DocsControllerContext() 
{ 
    Request = Request, 
    Response = Response, 
    Controller = this, 
    ViewResult = (viewName, model) => 
    { 
     return View(viewName, model); 
    } 
}; 

var returnValue = methodInfo.Invoke(toInvoke, new object[] { context }); 
return returnValue; 

Sau đó, trong phương pháp tôi gọi, tôi có thể gọi context.View("ViewName", model);. Có thể có nhiều biến thể của điều này, ý tưởng cơ bản là sử dụng gọi lại.

6

Chỉ cần đọc bài đăng này vì tôi gặp sự cố tương tự từ bộ lọc hành động. Giải pháp của tôi đã tạo ra hành động xem một cách rõ ràng. Điều này dựa trên phương thức View() được bảo vệ theo nguồn MVC do đó nó nên điền các thuộc tính cần thiết. Dù sao, dường như làm việc mà không có vấn đề.

public static NotFound(Controller controller){ 
    controller.HttpContext.Response.StatusCode = 404; 

    ViewResult result = new ViewResult { 
      ViewName = "NotFound", 
      ViewData = controller.ViewData, 
      TempData = controller.TempData 
     }; 
    return result; 
} 

Một chút muộn trong ngày nhưng điều này làm việc cho tôi.

Các vấn đề liên quan