我希望能够直接在控制器的参数中向当前用户提出索赔。这样我就可以编写单元测试,而无需触碰ClaimPrincipal魔术。
像[FromUri]或[FromBody],也许是[FromClaim]?
我尝试实现Microsoft的本文档中指定的CustomModelProvider:https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-2.2
但是我不知道如何提供ClaimsPrincipal或List。 另外,ValueProvider返回一个字符串,因此我不确定这是否切实可行。
这是我对ClaimModelBinder的尝试
public class ClaimModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var modelName = bindingContext.ModelName;
// Try to fetch the value of the argument by name
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult == ValueProviderResult.None) return Task.CompletedTask;
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
var value = valueProviderResult.FirstValue;
// TODO: Unsure, how to continue after this.
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value)) return Task.CompletedTask;
int id = 0;
if (!int.TryParse(value, out id))
{
// Non-integer arguments result in model state errors
bindingContext.ModelState.TryAddModelError(
modelName,
"Author Id must be an integer.");
return Task.CompletedTask;
}
// Model will be null if not found, including for
// out of range id values (0, -3, etc.)
bindingContext.Result = ModelBindingResult.Success(null);
return Task.CompletedTask;
}
}
答案 0 :(得分:2)
您能提供“构建一个ClaimsPrincipal进行测试比您尝试做的要容易得多且正确得多”的来源吗?
消息来源是我。至于我为什么这么说,它是基于对how the ASP NET Core framework is written的理解,如下文所示。
要回答您的问题,Controller拥有一个User属性来访问声明,那么当一个User属性已经存在时,无需编写Model Binder来访问声明,除非您由于以下原因而无法从该User属性访问声明:您的主张逻辑不同。但是您还没有提及。
“我希望能够直接在控制器的参数中向当前用户提供索赔。这样我就可以编写单元测试,而无需触动ClaimPrincipal魔术。”
我解释为,
“我想为我的控制器编写单元测试,该控制器具有涉及Claims Principal的逻辑,但是我不知道如何提供伪造的Claims Principal,所以我将避免这种情况并改为传递方法参数”
ClaimsPrincipal如下所述。
Controller
有一个User
属性,但仅是Get。 魔术 HttpContext
具有一个User
属性,该属性为“获取并设置”(很好),而Controller.HttpContext
仅是Get属性(不太好 >)Controller
的{{1}}属性是Get and Set,ControllerContext
的{{1}}属性是Get and Set。 头奖! 这是ControllerContext
和HttpContext
衍生自ControllerBase
的{{3}}
Controller
正如您在此处看到的那样,您访问的用户是一个便捷的Getter,最终可以访问ApiController
。了解了这些信息后,您可以对使用ClaimsPrincipal的控制器进行单元测试,如下所示。
public abstract class ControllerBase
{
/* simplified below */
public ControllerContext ControllerContext
{
get => _controllerContext;
set => _controllerContext = value;
}
/* ... */
public HttpContext HttpContext => ControllerContext.HttpContext;
/* ... */
public ClaimsPrincipal User => HttpContext?.User;
}
这是每次收到实际的Web请求时ASP NET Core的工作方式。从字面上看,它可以使控制器正常工作并可以使用。
以上所有内容都是 public ASP NET Core API的一部分,在没有主要版本错误的情况下不会受到重大更改,因此可以安全使用。实际上,这是使ASP Net Core与旧的ASP NET MVC脱颖而出的原因之一,这是一个噩梦,因为它没有公开暴露任何上述内容。
由于种种原因,我已经忽略了所有这些内容,如果您确实需要编写模型绑定器来提供声明,请插入source code。但这需要您检查方法参数的类型和分支执行。一个分支将从值提供程序绑定属性,而另一个分支从HttpContext绑定。但是,当您可以使用0重构来完成上述操作时,为什么还要麻烦呢?