Angular2 HTTP Post ASP.NET MVC Web API

11

如何使用Angular2创建包含复杂对象或多个参数的Web API POST请求?

我在Angular2中有一个服务组件,如下所示:

public signin(inputEmail: string, inputPassword: string): Observable<Response> {
    return this.http.post('/api/account/signin', JSON.stringify({ Email: inputEmail, Password: inputPassword}), this.options);
}

目标 Web API 如下:

[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(string email, string password)
{
       ....
}

这种方法行不通,因为我需要将 Web API 的参数转换成一个仅包含 Email 和 Password 属性的 POCO 类实体,并加上 [FromBody] 特性:Signin([FromBody] Credential credential)

如果不使用 [FromURI](带有查询字符串的 POST 请求?),如何进行多个参数或复杂对象的 POST 请求,而不将这些参数转换为单个 POCO 类?

因为如果我有许多 Web API POST 操作,参数类似于 (string sensitiveInfo1, string name, int sensitiveInfo2)(ClassifiedInfo info, string sensitiveInfo1, string sensitiveInfo2),我是否需要将它们全部转换为 POCO 类并始终使用 [FromBody]?

PS. 在此之前我使用过 RestangularJS ,可以不需要我的 Web API 操作具有 [FromBody] 特性就可以发布任何内容(多个基元对象和复杂对象)。 接下来我将调查 RestangularJS 如何实现。


1
既然您已经在操作中使用小写字母ep定义了emailpassword参数,我认为JavaScript代码也应该是相同的,例如:JSON.stringify({ email: inputEmail, password: inputPassword}) - Felipe Oriani
1
@FelipeOriani 这并不适用于使用JSON.Net作为JSON序列化器的.NET。默认情况下,它是不区分大小写的。 - David L
7个回答

11
不使用[FromURI](带查询字符串的POST请求?),如何进行多个参数或复杂对象的POST,而无需将这些参数转换为单个POCO类?我知道这不是你想听到的,但是开箱即用的情况下这是不可能的。这不是发出请求的浏览器代码的限制。这意味着无论您使用Angular、JQuery、直接JavaScript甚至RestangularJS,都没有关系。这是Web API(任何版本)的限制(我使用这个词很宽泛,因为我确定这是有意设计的)。这是Mike Wasson的文档ASP.NET Web API中的参数绑定

At most one parameter is allowed to read from the message body. So this will not work:

// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }

因此问题变成了,你有哪些选择?

创建模型

这是你试图避免的事情,但我将其列为第一项,因为这就是Web API的预期行为。我还没有听到过不这样做的令人信服的理由。这种方法允许您轻松扩展模型,而无需更改方法签名。它还允许在模型本身上进行模型验证。个人而言,我非常喜欢这种方法。

public class SignInModel{
    public string Email {get;set;}
    public string Password {get;set;}
}

[HttpPost]
[Route("signin")]
public async Task<IActionResult> Signin(SignInModel signInModel)
{
       // ....
}

我没有重复你现有的JavaScript代码,因为你已经拥有的代码可以与上面的Web API代码一起正常工作。

URL

再次强调,这是你试图避免的。但这确实使得你想要的功能成为可能,只不过你必须使用URL的查询字符串来传递这些参数。JavaScript代码会改变,但Web API方法的签名不会改变。

public signin(inputEmail: string, inputPassword: string): Observable<Response> {
    return this.http.post('/api/account/signin/?email=inputEmail&password=inputPassword', null, this.options);
}

我没有重复你现有的Web API代码,因为在上述Web JavaScript代码中,您所拥有的代码已经起作用(默认情况下,FromUri被认为是可行的)

自定义模型绑定器

请参见将多个POST参数传递到Web API Controller方法 by Rick Strahl。此选项允许您创建一个自定义模型绑定器,可以实现您要求的功能。但是这需要大量额外的代码,换取的收益不大,只是我的个人意见。也许在某些情况下会有用,尽管我真的想不出头脑之中有什么情况。

动态

最后,您还可以将一个dynamic对象作为Web API的参数传递。这本质上与接收JSON字符串并使您的控制器代码负责内容反序列化相同。同样,我认为在大多数情况下,这将使您的代码变得更糟,因为您必须实现自定义验证和类型检查。此答案是之前由Bes Ley在SO上提出的。也许有一些情况会有用,尽管我真的想不到任何情况。

这是非常准确的。无论你使用哪个JavaScript框架,它们都使用相同的协议(http),该协议具有其规则和限制。 - Marcus Höglund

4
如果你从Angular 2类型脚本调用Web API 2.2 post方法,请不要忘记添加以下标题内容和参数对象。
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }); 
 var params = new URLSearchParams();
        params.set('userid', '102');
        params.set('username', 'foo');

 return this._http.post('http://localhost:6579/api/PostUser', params.toString(), { headers: headers }).map(res => res.json());

2
也许你应该考虑使用选项来发布文章:
{ 
   headers: new Headers({
   'Content-Type': 'application/x-www-form-urlencoded'
   })
}

并编码像这样的数据

jQuery.param({user:'bla', password: 'bla'});

2

WebAPI本身并没有提供这个功能。如果你试图了解Web API绑定的工作原理,你可能可以弄清为什么。

我认为这篇文章可能会有所帮助。

通用规则是:

- 简单、可转换为字符串的参数(值类型、字符串、Guid、DateTime等)默认从URI中读取

- 复杂类型默认从请求体中读取

- 简单参数的集合也默认从请求体中读取

- 无法根据来自URI和请求体的输入组合单个模型,必须选择URI或请求体其中之一


1
我已经解决了Angular2 HTTP Post ASP.NET MVC Web API的问题。
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
let params: URLSearchParams = new URLSearchParams();
params.set('value', '2');

let options = new RequestOptions({
    headers: headers//,
    //search: params
});
let content = new URLSearchParams();
content.set('StudentName', 'Inderjit Singh';
content.set('Mobile', '+919041165398');            
content.set('Nationality', 'Indian');
content.set('AdmissionNo', '6');
content.set('SectionCode', '1');
content.set('Gender', 'Male');
content.set('RegNo', '18585');
content.set('ClassCode', '1');
this.http.post('YOUR_URL', content.toString(), { headers: headers }).map((res: Response) => { console.log("data is==>" + res.text()); }).subscribe();

我修复了Angular2 HTTP Post ASP.NET MVC Web API的问题。 - InderMoga

0
尝试将一个复杂的类对象传递到单个数据参数中。
var SearchQuery = function () {
    this.Alphabet = null;
    this.Search = false;
    this.Keyword = null;
    this.RegionList = null;
};
var para = new SearchQuery();
{ data: JSON.stringify(para) } - Post Data

你可以在API控制器中使用JObject来接收它,并根据你的类进行反序列化。


0

如果JSON对象具有相同的字段名称(我不确定大小写是否正确,所以您可能是正确的),WebApi将能够反序列化您提供的凭据对象。您似乎在Angular2组件的post调用中缺少标题。

您可以使用Chrome Debugger或Fiddler检查Content-Type吗?它应该是application/json。


我在选项参数上添加了标题。我的问题是,如果您的帖子参数只有三个字符串,您是否仍应始终将它们转换为单个对象? - raberana
在帖子上的查询字符串通常用于指定如何处理发布的正文。如果您正在服务器上保留任何内容或突变任何内容,请使用正文和poco。我会使用多个轻量级pocos,这被认为是良好的设计。例如,ServiceStack实际上将路由映射到Pocos。https://github.com/ServiceStack/ServiceStack/wiki/Routing这有帮助吗? - KnowHoper
有点类似,但我见过其他第三方JS框架可以向Web API发布具有多个复杂参数的内容,而不需要使用[FromBody](例如Restangular)。 - raberana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接