将JSON数据发布到Dotnet Core Webapi

时间:2018-08-23 18:06:41

标签: json asp.net-core asp.net-core-webapi

我正在使用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方法中将它们作为单独的参数接受?

3 个答案:

答案 0 :(得分:2)

  1. 如果在JSON文档中传递param1,则必须在TokenRequest类中定义param1。您已经得到的错误就是这个意思。

  2. 您不必在JSON文档中传递param1。您可以通过查询参数或Http标头

  3. 传递param1
  4. 我更喜欢将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”。