【问题标题】:Net Core custom controllerNet Core 自定义控制器
【发布时间】:2019-06-08 02:06:45
【问题描述】:

目前我的代码如下所示:

public class UserController : ControllerBase
{

    [HttpPost]
    public async Task<ActionResult> Login(LoginCredentialsModel model)
    {
       if (someValidLogic){
           return Ok(new { message = "User login success.", 
                           additionalParameters = new { 
                                    param1 = "",
                                    param2 = ""
                            }
                          });
       }
        else {
           return BadRequest(new { errorMessage = "Username or password is incorrect.", 
                                   additionalParameters = {
                                       StatusCode = 400, 
                                       RetryLeftCount = 3 }
                                   });
        }  
    }

}

我正在手动创建 JSON 对象以在每个端点返回 UI,以便我可以进行一致的通信并允许 UI 在全局级别处理消息。 (在我的情况下使用角度拦截器)。我想创建一个自定义类,它可以由所有只有两种返回类型选项的控制器实现 - Success()、Fail()。

public class UserController : CustomControllerBase
{

    [HttpPost]
    public async Task<ActionResult> Login(LoginCredentialsModel model)
    {
       if (someValidLogic){
           return Success("User login success.", additionalParameters)
       }
       else {
           return Error("Username or password is incorrect.", additionalParameters);
       }  
    }

}

我的 CustomControllerBase 将负责以正确的形式格式化结果。我知道我可以通过中间件做到这一点,但我真的不想这样做,因为它仍然允许开发人员意外地发回一些无效的结果并且中间件不知道如何处理它。

【问题讨论】:

    标签: c# asp.net .net-core


    【解决方案1】:

    即使您制作了自定义基础控制器,您也必须让自定义基础控制器扩展 Microsoft.AspNetCore.Mvc.ControllerBase,否则您将无法获得所有自动路由、[HttpPost] 类型属性等. 一旦你扩展了 ControllerBase,所有像 Ok() 这样的方法都将提供给你的其他开发人员。我真的只是尝试与开发人员沟通不要使用 Ok() 等。

    这很老套,但您可以通过在您的 CustomControllerBase 中覆盖其他方法并抛出异常来“阻止”其他方法。这不会产生编译时错误,但至少会产生浅运行时异常。还有很多这些方法,您必须全部覆盖它们。

    public class CustomControllerBase : ControllerBase
    {
        public ActionResult Success(string message)
        {
            return base.Ok(message);
        }
        public new ActionResult Ok(object value)
        {
            throw new Exception("don't use this");
        }
    }
    
    public class UserController : CustomControllerBase
    {
    
        public async Task<ActionResult> Hello()
        {
            return Ok("hello"); // throws runtime exception
        }
    }
    

    或者使用样式检查器并禁止所有使用 Ok() 和类似方法。您还必须禁止使用像 return new OkObjectResult("hello"); 这样的行。要做到这一点真的会很痛苦。

    【讨论】:

      【解决方案2】:

      根据您的要求,为了有一个自定义类,您可以在将响应消息返回给调用请求时重复使用,我创建了一个继承自 ActionResult 的自定义类,我们可以返回它。

      在有了这个类之后,我们将使用它来创建一个基本/自定义控制器,我们将在其中添加我们的 SuccessFail 方法,任何控制器都可以从这些方法继承以使用扩展方法。

      自定义操作结果

      using Microsoft.AspNetCore.Mvc;
      using Newtonsoft.Json;
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Net;
      using System.Text
      
      /// <summary>
      /// Customized <see cref="ActionResult"/> that allows easily setting <see cref="HttpStatusCode"/>
      /// and data result object to be returned for a request.
      /// </summary>
      public class CustomActionResult : ActionResult
      {
          private static UTF8Encoding utf = new UTF8Encoding();
      
          /// <summary>
          /// Http response code.
          /// </summary>
          public HttpStatusCode StatusCode { get; set; }
          /// <summary>
          /// Data to return back to the user as an <see cref="object"/> of any <see cref="Type"/>
          /// </summary>
          public object Data { get; set; }
          /// <summary>
          /// Parameterless contructor that initializes the ActionResult with
          /// <see cref="HttpStatusCode.OK"/> as the default Response Code.
          /// </summary>
          public CustomActionResult()
          {
              StatusCode = HttpStatusCode.OK;
              Headers = new Dictionary<string, string>();
          }
          /// <summary>
          /// Constructor that initializes the ActionResult with a specified <see cref="HttpStatusCode"/>
          /// </summary>
          /// <param name="statusCode">
          /// Http response code to set for this ActionResult.
          /// </param>
          public CustomActionResult(HttpStatusCode statusCode)
              :this()
          {
              StatusCode = statusCode;
          }
          /// <summary>
          /// Constructor that initializes the ActionResult with a specified <see cref="HttpStatusCode"/>
          /// </summary>
          /// <param name="statusCode">
          /// Http response code to set for this ActionResult.
          /// </param>
          /// <param name="message">Reason phrase</param>
          public CustomActionResult(HttpStatusCode statusCode, string message)
              :this()
          {
              StatusCode = statusCode;
              Data = message;
          }
      
      
          private string Json
          {
              get
              {
                  if(Data != null)
                  {
                      if (Data.GetType() == typeof(string))
                      {
                          return Data.ToString();
                      }
                      return JsonConvert.SerializeObject(Data);
                  }
                  return string.Empty;
              }
          }
      
          public byte[] GetBuffer() => utf.GetBytes(Json);
      
          public Dictionary<string,string> Headers { get; private set; }
      
          public override void ExecuteResult(ActionContext context)
          {
              if (Headers.Count > 0)
              {
                  for (int i = 0; i < Headers.Count; i++)
                  {
                      var item = Headers.ElementAt(i);
                      context.HttpContext.Response.Headers.Add(item.Key, item.Value);
                  }
              }
      
              if (!string.IsNullOrWhiteSpace(Json))
              {
                  context.HttpContext.Response.ContentType = "application/json";
              }
      
              context.HttpContext.Response.StatusCode = (int)StatusCode;
              context.HttpContext.Response.Body.Write(GetBuffer(), 0, GetBuffer().Length);
          }
      }
      

      CustomBaseController

      public class CustomBaseController : ControllerBase
      {
          public ActionResult Success(object value)
          {
              return new CustomActionResult(){ Data = value };
          }
          public ActionResult Success(string message, params object[] additionalParams)
          {
              if(additionalParams.Length > 0){
                  return new CustomActionResult(){
                      Data = new { message, additionalParams }
                  };
              }else{
                  return new CustomActionResult() { Data = message };
              }
          }
          public ActionResult Fail(object value)
          {
              return new CustomActionResult(HttpStatusCode.BadRequest){ Data = value };
          }
          public ActionResult Fail(string message, params object[] additionalParams)
          {
              if(additionalParams.Length > 0){
                  return new CustomActionResult(HttpStatusCode.BadRequest){
                      Data = new { ErrorMessage = message, additionalParams }
                  };
              }else{
                  return new CustomActionResult(HttpStatusCode.BadRequest){
                      Data = new { ErrorMessage = message }
                  };
              } 
          }
      }
      

      用法

      public class UserController : CustomBaseController
      {
          [HttpPost]
          public async Task<ActionResult> Login(LoginCredentialsModel model)
          {
              if(!ModelState.IsValid)
                  return this.Fail(ModelState);
      
              // add your other custom logic here
              if(someLogic){
                  return this.Success("your-message", additionalParams);
              } else {
                  return this.Fail("custom-error-message", additionalParams);
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2023-01-09
        • 2021-06-25
        • 2018-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-13
        • 2021-03-28
        相关资源
        最近更新 更多