c# - ASP.NET Core 3.1:查询字符串参数确定正文的类
问题描述
我正在寻找一个带有两个曲折的 ASP.NET Core 复杂模型绑定的示例。一种是body的类由查询字符串参数决定,但查询字符串参数不决定类型。
[HttpPut("api/Certificates/activities/state")]
public IActionResult PutState([FromQuery] string stateId, [FromQuery] int activityId, [FromQuery] string agent, [FromQuery] Guid registration, [FromBody] object body = null)
{
...
return NoContent();
}
确定主体类的查询参数是stateId
,但其值不完全包含类型名称。相反,场景如下:
- 如果
stateId
等于"LMS.LaunchData"
,则应将主体验证为LaunchData
对象。 - 如果
stateId
等于"status"
,则应将主体验证为Status
对象。 - 否则,body 应该是一个普通的对象。
第二个转折是,将发布到此端点的已知主体类没有任何共同点。在应用程序的其他部分,它们服务于完全不同的目的。我在下面发布了我的两种特殊体型,让您看到它们没有任何共同点。
启动数据.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace EmploymentModules.Models
{
public class LaunchData
{
[JsonPropertyName("registration")]
public Guid Registration { get; set; }
[JsonPropertyName("contextTemplate")]
public Context ContextTemplate { get; set; } = new Context();
[JsonPropertyName("launchMode")]
public string LaunchMode { get; set; }
[JsonPropertyName("launchMethod")]
public string LaunchMethod { get; set; }
[JsonPropertyName("returnURL")]
public string ReturnURL { get; set; }
[JsonPropertyName("launchParameters")]
public string LaunchParameters { get; set; }
[JsonPropertyName("entitlementKey")]
public EntitlementKey EntitlementKey { get; set; } = new EntitlementKey();
[JsonPropertyName("moveOn")]
public string MoveOn { get; set; }
}
public class EntitlementKey
{
[JsonPropertyName("courseStructure")]
public string CourseStructure { get; set; }
}
}
Cmi5Result.cs
using EmploymentModules.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Dynamic;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using static EmploymentModules.Helpers.ScoreValidators;
namespace EmploymentModules.Models
{
public class Cmi5Result
{
[JsonPropertyName("score")]
[ScoreValidation]
public Score Score { get; set; }
[JsonPropertyName("success")]
public bool? Success { get; set; }
[JsonPropertyName("completion")]
public bool? Completion { get; set; }
[JsonPropertyName("duration"), RegularExpression("^(P((?<Years>\\d+)Y)?((?<Months>\\d+)M)?((?<Days>\\d+)D)?)(T((?<Hours>\\d+)H)?((?<Minutes>\\d+)M)?((?<Seconds>\\d+((.)?(\\d)?(\\d)?))S)?)$")]
public string Duration { get; set; }
[JsonPropertyName("extensions")]
public ResultExtensions Extensions { get; set; }
}
[JsonConverter(typeof(ScoreConverter))]
public class Score
{
[JsonPropertyName("scaled")]
public decimal? Scaled { get; set; }
[JsonPropertyName("raw")]
public int? Raw { get; set; }
[JsonPropertyName("min")]
public int? Min { get; set; }
[JsonPropertyName("max")]
public int? Max { get; set; }
}
public class ResultExtensions : DynamicObject
{
[Range(0, 100), JsonPropertyName("https://w3id.org/xapi/cmi5/result/extensions/progress")]
public int? Progress { get; set; }
[RegularExpression("^(Tested Out|Equivalent AU|Equivalent Outside Activity|Administrative)$"), JsonPropertyName("https://w3id.org/xapi/cmi5/result/extensions/reason")]
public string Reason { get; set; }
}
public class Status : Cmi5Result
{
[JsonPropertyName("launchModes")]
public List<string> LaunchModes { get; set; }
}
}
解决方案
这里有两个解决方案。
1.您可以反序列化对象,如下所示:
控制器:
public IActionResult PutState([FromQuery] string stateId, [FromQuery] int activityId,
[FromQuery] string agent, [FromQuery] Guid registration, [FromBody] object body)
{
var model = new Object();
switch (stateId)
{
case "LMS.LaunchData":
model = JsonConvert.DeserializeObject<LaunchData>(body.ToString());
break;
case "status":
model = JsonConvert.DeserializeObject<Status>(body.ToString());
break;
default:
model = body;
break;
}
return Ok(model);
}
测试机构:
{
"success": true,
"score":{
"raw":1,
"min":0,
"max":2
}
}
2.您也可以添加stateId
到Object body。并使用Model Binder。这样body的类由stateId
Object body中决定。这是一个演示:
控制器(添加[ModelBinder(typeof(DataBinder))]
前object body
):
//use ModelBinder to Bind object body
public IActionResult PutState([FromQuery] string stateId, [FromQuery] int activityId, [FromQuery] string agent, [FromQuery] Guid registration, [FromBody][ModelBinder(typeof(DataBinder))] object body)
{
return Ok(body);
}
数据绑定器:
public class DataBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model = new Object();
using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body))
{
var body = reader.ReadToEndAsync();
var mydata = JsonConvert.DeserializeObject<JObject>(body.Result);
var stateId = mydata["stateId"].ToString();
switch (stateId)
{
case "LMS.LaunchData":
model = mydata.ToObject<LaunchData>();
break;
case "status":
model = mydata.ToObject<Status>();
break;
default:
model = mydata.ToObject<Object>();
break;
}
}
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
测试正文(添加stateId
到正文):
{
"stateId":"status",
"success": true,
"score":{
"raw":1,
"min":0,
"max":2
}
}
推荐阅读
- php - preg_split 删除的空间
- javascript - 了解非捕获组的正则表达式
- java - 如何修复 java jar 文件资源的 FileNotFoundException?
- python - 当路径包含快捷方式时如何引用文件
- c# - 返回列表中包含的所有角色
- python - ValueError:无法将字符串转换为浮点数 - 在 google colab 中
- python - 如何在网页中输入
- r - 将汇总数据帧从长转换为宽(不使用 reshape、reshape2、tydr)
- reactjs - React componentWillUpdate 每秒运行一次并且条件不起作用
- firebase - 来自火力基地的警告