【问题标题】:Angular JS MVC Web API Model/ Parameter not binding .NET CoreAngular JS MVC Web API 模型/参数未绑定 .NET Core
【发布时间】:2016-12-31 15:27:53
【问题描述】:

我正在使用带有 TypeScript 和 ASP.NET Core MVC/API 的 Angular JS。

我有一个apiService,它处理对服务器的所有POST 和GET 请求,如下所示:

module TBApp {

    export class apiService {

        static $inject = ['$http', 'notificationService'];

        constructor(private $http, private notificationService: notificationService) {

        }

        get(url, config, success, failure) {

            return this.$http.get(url, config)

                .then(result => { this.handleResponse(result, success); }, result => { this.handleError(result, failure) });
        }

        post(url, data, success, failure) {

            return this.$http.post(url,data)
                .then(result => { this.handleResponse(result, success); }, result => { this.handleError(result, failure) });
        }

        handleResponse(result, success) {

            alert('success');
            success(result);

        }

        handleError(result, failure) {

            if (result.status === '401') {

                this.notificationService.displayError('Authentication required.');
                //this.$rootScope.previousState = this.$location.path();
                //this.$location.path('/login');

            }
            else if (failure !== null) {
                failure(result);
            }
        }
    }
}

现在当我发送这个请求时:

    onCompanyChanged(selectedCompany, model, companyName) {

        this.apiService.post('/api/Dashboard/GetAssetListByCompany', { companyId: selectedCompany.id },

            response => {

                this.assetListViewModel = response.data.data;


            }, response => {
            this.notificationService.displayError(response.data.message);
        });
    }

它没有绑定控制器中的companyId

这里是控制器:

   [Route("api/[controller]")]
    public class DashboardController : BaseController
    {
        [HttpPost]
        [Route("GetAssetListByCompany")]
        public IActionResult GetAssetListByCompany([FromBody]int companyId)
        {
            return CreateJsonResult(() =>
            {
                if (companyId == 0) { return new xPTJsonResult(null, xPTStatusCodesEnum.Success, "Company Id is 0"); }

               //var treeModel = _dashboardProvider.GetTreeModelByCompany(companyId, userModel);

                return new xPTJsonResult(null, xPTStatusCodesEnum.Success, "Loaded assets successfully");

            });
        }

}

即使我在浏览器中检查请求时,显示 companyId 在有效负载中。

注意:当我发布 ViewModel 时,同样的功能会起作用

编辑

在上述场景中,我只将一个参数传递给控制器​​,但在某些情况下,我希望能够在不使用 ViewModel 的情况下传递 2 或 3 个参数。

例如

public IActionResult GetAssetListByCompany([FromBody]int companyId, [FromBody]int assetId)
{....

或

public IActionResult GetAssetListByCompany([FromBody]int companyId, [FromBody]int assetId, [FromBody]bool canEdit = false)
    {.....

然后在客户端我可以这样做:

this.apiService.post('/api/Dashboard/GetAssetListByCompany', { companyId: selectedCompany.id, assetId: 123 }.....

或

this.apiService.post('/api/Dashboard/GetAssetListByCompany', { companyId: selectedCompany.id, canEdit: true, assetId: 22 }....

【问题讨论】:

  • 不要使用名称 MVC6,它被称为“ASP.NET Core MVC”,版本号是 1.0.0,而不是 6。
  • 有理由不使用视图模型吗?
  • @ademcaglin 因为这个方法只有一个参数 - 我不想为所有采用一两个参数的方法创建 ViewModel - 在旧的 MVC/API 中可以发布{variableName: Value}

标签: angularjs asp.net-core asp.net-core-mvc asp.net-core-webapi


【解决方案1】:

对获取请求执行 GET 而不是 POST 总是更好。如果您想将其更改为 GET 方法,则制作 $http.get 并在 api 属性中,如下所示:

    [HttpGet] //not required, this will be get as per naming convention
    [Route("GetAssetListByCompany/{companyId}")]
    public IActionResult GetAssetListByCompany(int companyId)

但是如果你仍然需要做一个 POST,那么你在 $http.post 中的数据应该看起来像 data: '"123456"'

$http.post("/api/Dashboard/GetAssetListByCompany", '"123456"' )

当您将原始数据传递给 api 时需要上述解决方法,这解释了为什么在您传递视图模型时它可以正常工作。

更新:经过进一步讨论,OP 需要 POST 多个原始类型到 api,并且需要使用密钥访问数据,而不依赖于模型绑定的顺序。 p>

一种选择是使用字典作为输入参数类型

 [HttpPost]
 [Route("GetAssetListByCompany")]
 public IHttpActionResult GetAssetListByCompany(Dictionary<string,int> data)
        {
            return Ok();
        }

在客户端:

    var data = {};
    data["CustomerId1"] = 123;
    data["CustomerId2"] = 345;
    data["CustomerId3"] = 1623;
    data["CustomerId4"] = 7655;

    $http.post("/api/Dashboard/GetAssetListByCompany", data);

结果

【讨论】:

  • 我必须使用 POST - 你的答案不起作用 - 但即使它做了如果我们想在没有视图模型的情况下发布 3 个变量怎么办?例如{companyId: 1, newCompanyId: 2, newwCompanyId: 4}
  • @Dawood - 上面提到的东西适用于 $.ajax,我可能会在用 $http 测试后更新。另一种方法是通过路由参数 [Route("GetAssetListByCompany/{companyId1}/{companyId2}")] 发送它并相应地更新操作。确保删除 [FromBody] 内容
  • 如果我们将 $.ajax 与 angular 一起使用, - 我们必须在 angular 控制器中的模型上添加 $watch 或 $apply 才能在视图中进行更新 - 否则不会自动更改时使用最新的视图模型更新视图 - 我不想使用 jQuery -
  • 使用 [Route("GetAssetListByCompany/{companyId1}/{companyId2}")] 意味着公司 ID 的顺序必须正确——这似乎也不是一个好的解决方案——也就是说为什么我要使用这种方法: {companyId: 1, newCompanyId: 2, newwCompanyId: 4} ,并且参数的顺序无关紧要 - 所以绑定是按名称进行的
  • @DawoodAwan - 请找到更新的答案。 1. 数据:'"123456"' - 有效。 2. 发送多个项目,使用 Dictionary
【解决方案2】:

这里最好的方法是遵循 HTTP 指南并将您的操作从 POST 更改为 GET,因为您不会修改任何数据。这相当简单,并且仍然能够使用 URI 随请求一起发送数据。

MVC 变化

请参阅Model Binding 了解各种选项,这里最好的方法是基于查询字符串进行绑定,因为您只需要一个原始类型。如果您有一个原始类型数组,您仍然可以绑定到查询字符串,查询字符串变量名称将针对每个值重复一次。

所以我们所做的唯一更改是指定参数来自 Query 字符串,并且它与 Http Get 请求而不是 Post 相关联。

[Route("api/[controller]")]
public class DashboardController : BaseController
{
    [HttpGet] // change to HttpGet
    [Route("GetAssetListByCompany")]
    public IActionResult GetAssetListByCompany([FromQuery]int companyId) // use FromQuery
    {
        return CreateJsonResult(() =>
        {
            if (companyId == 0) { return new xPTJsonResult(null, xPTStatusCodesEnum.Success, "Company Id is 0"); }

           //var treeModel = _dashboardProvider.GetTreeModelByCompany(companyId, userModel);

            return new xPTJsonResult(null, xPTStatusCodesEnum.Success, "Loaded assets successfully");

        });
    }
}

AngularJS 变化

我们扩展了 apiService 以允许为使用 HttpGet 的调用传递数据。这可以使用params on the $http call 来完成,它会根据传入的数据动态创建URL,使用名称作为查询字符串值名称,值作为值部分。

export class apiService {
    /* all other code is left as is, just change the get method to also accept data via the params. If null is passed in then it is ignored. */
    get(url, config, data, success, failure) {
        return this.$http({
            url: url,
            config: config,
            params: data,
            method: "GET"
            })
            .then(result => { this.handleResponse(result, success); }, result => { this.handleError(result, failure) });
    }
}

在通话中,我们只需将 post 更改为 get 即可。

// only change from post to get
onCompanyChanged(selectedCompany, model, companyName) {
    this.apiService.get('/api/Dashboard/GetAssetListByCompany', { companyId: selectedCompany.id },
        response => {
            this.assetListViewModel = response.data.data;
        }, response => {
        this.notificationService.displayError(response.data.message);
    });
}

编辑 - 这很灵活

更重要的一点是,这种设计在角度方面很灵活。如果您扩展您的 MVC 操作或具有采用其他参数的各种操作,则无需实现任何其他更改即可工作。示例:

[HttpGet]
[Route("GetSomethingElseFromServer")]
public IActionResult GetSomethingElseFromServer([FromQuery]int companyId, [FromQuery]string assetName, [FromQuery]string companyModelNumber) // use FromQuery

对您的 Angular api 的调用将是

this.apiService.get('/api/Dashboard/GetSomethingElseFromServer', { companyId: companyId, assetName: somePassedInAssetNameVar, companyModelNumber: somePassedInModelNumber }

编辑 - 你也可以发送数组

要回答有关如何将多个原始类型作为数组发送的问题,您可以这样做。再次假设它不是您发送的复杂类型,而是例如公司 ID 列表。

c#代码

[HttpGet]
[Route("GetAssetListByCompany")]
public IActionResult GetAssetListByCompany([FromQuery]int[] companyIds) // use an array of int ie. int[]. i changed the variable name to make it clear there can be more than 1

Angular调用,注意不需要改服务

onCompanyChanged(selectedCompany, model, companyName) {
    this.apiService.get('/api/Dashboard/GetAssetListByCompany', { "companyIds[]": [id1, id2, id3] }, // note the name is now enclosed in quotes, made plural, and includes []. The value is an array
        response => {
            this.assetListViewModel = response.data.data;
        }, response => {
        this.notificationService.displayError(response.data.message);
    });
}

编辑 - 如果你仍然想发布

您目前只发送一个原始字段,因此 MVC 框架不会在 POST 中正确反序列化该字段。您需要将参数包装在视图模型中,将其作为查询字符串部分发送,或者将其作为表单字段值发送。这是带有查询字符串部分的 POST,它工作得很好。

选项 1

将其附加到 URL

[HttpPost] // change to HttpGet
[Route("GetAssetListByCompany")]
public IActionResult GetAssetListByCompany([FromQuery] int companyId) // use FromQuery

角度调用

this.apiService.post('/api/Dashboard/GetAssetListByCompany/?companyId=' + selectedCompany.id + , null, // the rest of the code remains unchanged so I did not include it

选项 2

扩展 apiService 以获取 params 对象,以便它可以构建您的查询。无论哪种方式,调用者都必须对正在进行的 http 调用有所了解。

this.apiService.post('/api/Dashboard/GetAssetListByCompany', null, {companyId: selectedCompany.id}, null, // the rest of the code remains unchanged so I did not include it

post(url, config, data, params, success, failure) {
    return this.$http({
        url: url,
        config: config,
        data: data,
        params: params,
        method: "POST"
        })
        .then(result => { this.handleResponse(result, success); }, result => { this.handleError(result, failure) });
}

选项 3

更新您的视图模型以采用复杂类型,这不需要更改您的角度代码。

public class ListByCompanyModel {
    public int CompanyId {get;set;}
}

[HttpPost] // change to HttpGet
[Route("GetAssetListByCompany")]
public IActionResult GetAssetListByCompany([FromBody] ListByCompanyModel model) // use FromQuery

【讨论】:

  • 你是对的。但 OP 正在寻找更多的东西。请查看我的回答中的讨论以获取更多信息
  • @Igor 在这种情况下我必须使用 POST。如果我从客户端传递这个: this.apiService.post('/api/Dashboard/GetAssetListByCompany', selectedCompany, null null) - 参数似乎绑定,但我需要 { parameter_name : value } 语法,所以我可以发布两个不同的参数以及
  • @DawoodAwan - 我不明白你为什么要发帖。 params 接受任意数量的参数。我可以为 Post 更新它,它是一个类似的概念,但所有内容都是使用消息正文传输的。查看我的更新,Get 适用于简单/原始类型以及查询字符串不超过 2k 个字符的情况(如果我没记错的话)。请稍等,我将添加一个 Post 示例。
  • @DawoodAwan - 如果你想使用 POST,我用 3 个选项更新了答案。
  • @Igor - 在选项 2 中,我认为应该是 POST 而不是 GET - 干杯我会尝试这些并回复你
【解决方案3】:

您发送了{ companyId: selectedCompany.id },这是一个对象,其字段companyId 的值为selectedCompany.id;

要么切换到 GET 请求作为上述答案,要么创建一个具有一个整数字段 companyId 的包装类,并将其用作服务器端函数的输入。

你可以试试其他的:

  • 只发送整数而不是对象
  • 将整数作为字符串发送,negine 可能会将其解析为 int。

【讨论】:

    猜你喜欢
    • 2021-06-30
    • 1970-01-01
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 2016-08-16
    • 2021-03-13
    • 1970-01-01
    • 2020-11-05
    相关资源
    最近更新 更多