我正在尝试将我在所有控制器上执行的逻辑转移到一个类中,以遵循“不要重复自己”的原则。我正在努力的是如何优雅地返回错误代码。
以下是我目前在每个控制器中执行的操作的一些示例:
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);
然后让他们返回错误的请求或异常。
我应该如何做到这一点?
答案 0 :(得分: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; }
}
答案 1 :(得分: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;
}
}
因为我想多次重用此动作过滤器,所以我需要找出一种为空检查传递参数的方法(可能有许多不同的对象以“ request”的名义进入需要检查的对象) 。 This is the answer我将寻找解决方案的那部分。