从Controller外部的类返回状态代码

问题描述 投票:0回答:2

我试图将我在所有控制器上执行的逻辑转移到一个类中,以遵循“不要重复自己”的原则。我正在努力的是如何优雅地返回错误代码。

以下是我目前在每个控制器中执行的操作的一些示例:

public class SomethingRequest
{
    public SomethingModel Something { get; set; }


    public string Token { get; set; }
}

public ActionResult GetSomething(SomethingRequest request)
{

    var something = request.Something;

    var token = request.Token;

    if (something == null)
    {        
        return BadRequest("Something object is null. You may have sent data incorrectly");
    }

    if (token == null || token != "1234")
    {
        return Unauthorized("Token object is null");
    }
}

现在我想要做的是将其最后两部分移动到他们自己的类中:

public class RequestValidation
{

    public void TokenCheck(string token)
    {
        if (token == null || token != "1234")
        {
            // doesn't work
            return Unauthorized("Token object is null");
        }
    }

    public void DataCheck(object someObject)
    {
        if (someObject == null)
        {
            // doesn't work
            return BadRequest("Object is null. You may have sent data incorrectly");
        }
    }       
}

然后我想从SomethingController中调用它们

RequestValidation.TokenCheck(token);

RequestValidation.DataCheck(something);

然后让他们返回错误的请求或异常。

我该怎么做到这一点?

c# .net asp.net-core-mvc http-status-codes
2个回答
1
投票

执行此操作的常用方法是使用一个帮助程序类,将验证和/或操作的结果返回给Controller:

public class ValidationResult
{
    public bool Succeeded { get; set; }
    public string Message { get; set; }
    public int StatusCode { get; set; }
}

由于问题是使用ASP.NET Core标记的,因此正确的方法是首先创建接口:

public interface IRequestValidationService
{
    ValidationResult ValidateToken(string token);
    ValidationResult ValidateData(object data);
}

然后,创建实现:

public class RequestValidationService : IRequestValidationService
{
    public ValidationResult ValidateToken(string token)
    {
        if (string.IsNullOrEmpty(token) || token != "1234")
        {
            return new ValidationResult
            {
                Succeeded = false,
                Message = "invalid token",
                StatusCode = 403
            };
        }

        return new ValidationResult { Succeeded = true };
    }

    ...
}

将它添加到DI容器中(在Startup类中):

services.AddScoped<IRequestValidationService, RequestValidationService>();

将它注入SomethingController:

public SomethingController(IRequestValidationService service)
{
    _requestValidationService = service;
}

最后使用它:

public IActionResult GetSomething(SomethingRequest request)
{
    var validationResult = _requestValidationService.ValidateToken(request?.Token);

    if (!validationResult.Succeeded)
    {
        return new StatusCode(validationResult.StatusCode, validationResult.Message);
    }
}

请注意,对于像验证某些内容不为null一样微不足道的东西,您应该使用模型验证:

public class SomethingRequest
{
    [Required(ErrorMessage = "Something is required, check your data")]
    public SomethingModel Something { get; set; }

    [Required(ErrorMessage = "Token is required!")]
    public string Token { get; set; }
}

0
投票

@CamiloTerevinto的想法让我走上了正确的道路。他的方法可行,但从我读到的in the documentation,正确的方法是“Action Filters”。

我使用this article作为额外的灵感。

这是我的过滤器,我命名为ValidationFilterAttribute

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Routing;
using System.Diagnostics;
using Microsoft.Extensions.Logging;

namespace Name_Of_Project.ActionFilters
{   
    // This filter can be applied to classes to do the automatic token validation.
    // This filter also handles the model validation.
    // inspiration https://code-maze.com/action-filters-aspnetcore/
    public class ValidationFilterAttribute: IActionFilter
    {
        // passing variables into an action filter https://stackoverflow.com/questions/18209735/how-do-i-pass-variables-to-a-custom-actionfilter-in-asp-net-mvc-app    

        private readonly ILogger<ValidationFilterAttribute> _logger;
        public ValidationFilterAttribute(ILogger<ValidationFilterAttribute> logger)
        {
            _logger = logger;
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            //executing before action is called

            // this should only return one object since that is all an API allows. Also, it should send something else it will be a bad request
            var param = context.ActionArguments.SingleOrDefault();
            if (param.Value == null)
            {
                _logger.LogError("Object sent was null. Caught in ValidationFilterAttribute class.");
                context.Result = new BadRequestObjectResult("Object sent is null");
                return;
            }

            // the param should be named request (this is the input of the action in the controller)
            if (param.Key == "request")
            {
                Newtonsoft.Json.Linq.JObject jsonObject = Newtonsoft.Json.Linq.JObject.FromObject(param.Value);

                // case sensitive btw
                string token = jsonObject["Token"].ToString();

                // check that the token is valid
                if (token == null || token != "1234")
                {
                    _logger.LogError("Token object is null or incorrect.");
                    context.Result = new UnauthorizedObjectResult("");
                    return;
                }
            }

            if (!context.ModelState.IsValid)
            {
                context.Result = new BadRequestObjectResult(context.ModelState);
            }
        }


        public void OnActionExecuted(ActionExecutedContext context)
        {
            // executed after action is called
        }
    }
}

然后我的Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    // Adding an action Filter
    services.AddScoped<ValidationFilterAttribute>();

}

然后我可以将它添加到我的控制器。


using Name_Of_Project.ActionFilters;

namespace Name_Of_Project.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class SomethingController : ControllerBase
    {

        // POST api/something
        [HttpGet]
        [ServiceFilter(typeof(ValidationFilterAttribute))]
        public ActionResult GetSomething(SomethingRequest request)
        {
            var something= request.Something;

            var token = request.Token;
    }
}

因为我想多次重用这个动作过滤器,所以我需要找到一种方法来传入一个参数进行空检查(可能有许多不同的对象以“请求”的名义进入需要检查)。 This is the answer我将寻求解决方案的那一部分。

© www.soinside.com 2019 - 2024. All rights reserved.