我在尝试使用我的Web API RESTful POST方法在控制器中使用ModelState验证模型时遇到了很多问题。
我this网站提到需要创建一个继承自ActionFilterAttribute
的自定义过滤器,而我似乎无法通过单元测试。单步执行代码时,它永远不会访问OnActionExecuting
方法,ModelState.IsValid
始终返回True
。我到处搜索代码示例并尝试了所有代码示例,但没有一个能够正常工作。我甚至引用了正确的命名空间,如下面的代码所示。
ValidationActionFilter.cs
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Linq;
using System.Diagnostics;
namespace OrbitWorksAPI.Filters
{
public class ValidationActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting( HttpActionContext actionContext )
{
Debug.WriteLine( "ACTION 1 DEBUG pre-processing logging" );
var modelState = actionContext.ModelState;
if ( actionContext.ActionArguments.Any( kv => kv.Value == null ) )
actionContext.Response = actionContext.Request
.CreateErrorResponse( HttpStatusCode.BadRequest, "Arguments cannot be null" );
if ( !modelState.IsValid )
actionContext.Response = actionContext.Request
.CreateErrorResponse( HttpStatusCode.BadRequest, modelState );
}
}
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register( HttpConfiguration config )
{
// Register routes
...
// Register filters
config.Filters.Clear();
config.Filters.Add( new Filters.ValidationActionFilter() );
}
}
我有这个控制器POST方法:
Controller.cs
[HttpPost]
[ResponseType( typeof( Contracts.Models.IInvoice ) )]
[ActionFilterAttribute]
public async Task<IHttpActionResult> Post( Contracts.Models.IInvoice invoice )
{
if ( !ModelState.IsValid )
BadRequest();
var newInvoice = await _repository.UpsertInvoice( invoice );
if ( newInvoice != null )
return CreatedAtRoute<Contracts.Models.IInvoice>( "invoice", new { newInvoice.Id }, newInvoice );
return Conflict();
}
我也有单元测试:
ControllerUnitTests.cs
[TestMethod]
public async Task POST_New_Invoice_With_Required_Fields_Missing()
{
// Arrange
Contracts.Models.IInvoice model = new Models.Invoice() { };
var config = new HttpConfiguration();
config.Filters.Add( new Filters.ValidationActionFilter() );
var repository = new Mock<Contracts.Repository.IInvoiceRepository>();
repository.Setup( x => x.UpsertInvoice( It.IsAny<Contracts.Models.IInvoice>() ) )
.Returns( Task.FromResult<Contracts.Models.IInvoice>( model ) );
// set up the controller instance.
var controller = new InvoiceController( repository.Object );
controller.Request = new HttpRequestMessage();
controller.Configuration = config;
// Act
var result = await controller.Post( model );
// Assert
var response = result as InvalidModelStateResult;
Assert.IsNotNull( response );
}
模特:
Invoice.cs
public class Invoice : IInvoice
{
[Key]
public int Id { get; set; }
[Required]
public int InvoiceNo { get; set; }
[Required]
public int CompanyId { get; set; }
[Required]
[MaxLength( 50 )]
public string CompanyName { get; set; }
[Required]
[MaxLength( 250 )]
public string InvoiceAddress { get; set; }
[Required]
[MaxLength( 250 )]
public string SiteAddress { get; set; }
[Required]
public string OrderDate { get; set; }
[Required]
[MaxLength( 10 )]
public string OrderNo { get; set; }
[Required]
public string OnHire { get; set; }
[Required]
public string InvoiceDate { get; set; }
[Required]
public decimal NettAmount { get; set; }
[Required]
public decimal VATAmount { get; set; }
[Required]
public decimal TotalAmount { get; set; }
[Required]
public bool IsCreditNote { get; set; }
[Required]
public float VATRate { get; set; }
}
答案 0 :(得分:0)
由于您将模型状态验证的行为与控制器中包含的逻辑分开,因此您还应该将用于验证其功能的单元测试分开。
如果您要验证过滤器会拒绝任何类型的无效模型,那么只需单独测试过滤器并确保无效模型在它们到达之前会被拒绝控制器动作。
查看Web API Action Filter Unit Tests有关单元测试操作过滤器的示例。
如果您要在config.Filters集合中全局添加[ActionFilterAttribute]
,还可以删除操作方法上的{{1}},并且还可以从操作中删除模型状态检查。