我正在使用aspnet核心开发WebApi。我已经能够设置一个基本项目,并获得很好的请求。
现在,我正在尝试使用邮递员将复杂的JSON对象发布到API。 API代码类似于:
//controller class
public class DemoController : ControllerBase {
[HttpPost]
public IActionResult Action1([FromBody]TokenRequest request) {
/* This works. I get request object with properties populated */
}
[HttpPost]
public IActionResult Action2(TokenRequest request) {
/* The request is received. But properties are null */
}
}
//TokenRequest Class
public class TokenRequest {
public string Username { get; set; }
public string Password { get; set; }
}
试图与邮递员进行同样的测试。
请求1 :(动作1和动作2均失败)
POST /Demo/Action2 HTTP/1.1
Host: localhost:5001
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 42642430-bbd3-49ca-a56c-cb3f5f2177cc
{
"request": {
"Username": "saurabh",
"Password": "******"
}
}
请求2 :(操作1成功,操作2失败)
POST /User/Register HTTP/1.1
Host: localhost:5001
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 94141e01-7fef-4847-953e-a9acb4e6c445
{
"Username": "saurabh",
"Password": "******"
}
由于[FromBody [标记],这些东西都可以在Action1中使用。但是,如果我需要接受多个参数该怎么办?喜欢
public IActionResult Action1(int param1, TokenRequest request)
选项1:使用包装器类(由Francisco Goldenstein建议)
[FromBody]不能与两个不同的参数一起使用。有什么合适的解决方案可以在Action方法中将它们作为单独的参数接受?
答案 0 :(得分:2)
如果在JSON文档中传递param1,则必须在TokenRequest类中定义param1。您已经得到的错误就是这个意思。
您不必在JSON文档中传递param1。您可以通过查询参数或Http标头
我更喜欢将param1作为查询参数传递。
POST /用户/注册?param1 = Param1
POST /User/Register?param1=Param1 HTTP/1.1
Host: localhost:5001
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 94141e01-7fef-4847-953e-a9acb4e6c445
{
"Username": "saurabh",
"Password": "******"
}
AND
public IActionResult Action1([FromQuery] int param1, [FromBody] TokenRequest request)
答案 1 :(得分:2)
发布多个对象的另一种解决方案是使用Form并将每个json对象发布在表单的单独字段中。将JsonBinder
应用于它,我们可以像使用[FromBody]
一样在参数中使用模型。
public class FormDataJsonBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
// Fetch the value of the argument by name and set it to the model state
string fieldName = bindingContext.FieldName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(fieldName);
if (valueProviderResult == ValueProviderResult.None) return Task.CompletedTask;
else bindingContext.ModelState.SetModelValue(fieldName, valueProviderResult);
// Do nothing if the value is null or empty
string value = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(value)) return Task.CompletedTask;
try
{
// Deserialize the provided value and set the binding result
object result = JsonConvert.DeserializeObject(value, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
catch (JsonException)
{
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
操作将是:
public IActionResult Action1(
[FromForm][ModelBinder(BinderType = typeof(FormDataJsonBinder))] TokenRequest request,
[FromForm][ModelBinder(BinderType = typeof(FormDataJsonBinder))] TokenRequest request1)
{
return Ok();
}
尽管我会选择包装类选项。
答案 2 :(得分:1)
如果需要接受更多参数,则需要创建另一个包含所需所有内容的类。 包装器类,如下所示:
public class X
{
public TokenRequest TokenRequest { get; set; }
public int Param1 { get; set; }
}
控制器的动作如下:
[HttpPost] 公共IActionResult Action1([[FromBody] X请求){ //您的代码 }
在ASP.NET Core中,您需要指出请求的值是否是主体,表单等的一部分。这与ASP.NET MVC的工作方式不同,其中每个部分(主体,表单等)被考虑。
作为旁注:
您还可以考虑使用继承,定义一个从TokenRequest继承的新类,但这不是一个好习惯,因为新类型不尊重“是TokenRequest”。最好说“有一个TokenRequest”。