【问题标题】:Ember.js POST requests return 400 from the server (Grape API) but are stored successfully into local storageEmber.js POST 请求从服务器(Grape API)返回 400,但已成功存储到本地存储中
【发布时间】:2014-11-10 20:43:42
【问题描述】:

几个小时以来,我一直试图让一个简单的 Ember.js 应用程序发布到 Grape API 后端,但我似乎无法让它工作。我知道 API 有效,因为我可以通过 Swagger 文档向它发布新记录,并且它们会被持久化。我知道 API 和 Ember 说得很好,因为我可以从服务器获取所有记录并在页面上与它们进行交互,而且我知道 Ember 在真空中运行良好,因为我的记录保存在本地存储中。

但是,我似乎无法让 POST 请求正常工作。它总是返回 400。我已经正确配置了 Rack-Cors,并且我在前端设置了一个 ActiveModelAdapter,在后端设置了一个 ActiveModelSerializer。

这是模型

Contact = DS.Model.extend {
  firstName: DS.attr('string'),
  lastName:  DS.attr('string'),
  email:     DS.attr('string'),
  title:     DS.attr('string'),
  createdAt: DS.attr('date'),
  updatedAt: DS.attr('date')
}

和控制器

ContactsNewController = Ember.ObjectController.extend(
  actions:
    save: ->
      @get('model').save()
    cancel: ->
      true
)

API 的相关部分如下所示

desc 'Post a contact'
  params do
    requires :first_name, type: String, desc: 'First name of contact'
    requires :last_name , type: String, desc: 'Last name of the contact'
    requires :email     , type: String, desc: 'Email of the contact'
    requires :title     , type: String, desc: 'Title of the contact'
  end
  post do
    Contact.create!(
      first_name: params[:first_name],
      last_name:  params[:last_name],
      email:      params[:email],
      title:      params[:title]
    )
  end

我使用的表格是……

<form {{action 'save' on='submit'}}>
  <h2>{{errorMessage}}</h2>

  <label>First Name</label>
  {{input value=firstName placeholder='First Name' type='text' autofocus=true}}

  <label>Last Name</label>
  {{input value=lastName placeholder='Last Name' type='text'}}

  <label>Email</label>
  {{input value=email placeholder='Email' type='text'}}

  <label>Job Title</label>
  {{input value=title placeholder='Job Title' type='text'}}

  <hr>

  <input type='submit' value='Save'>
</form>

我得到的回应是……

{"error":"first_name is missing, last_name is missing, email is missing, title is missing"}

有接受者吗?对不起,我是新手。有些东西没有绑定,但我不知道为什么。


更新

更多调查...

图片:POST requests to the API cURL (via Postman) work just fine.

但是,当我从 Ember 发布时,服务器响应仍然是

{"error":"first_name is missing, last_name is missing, email is missing, title is missing"}

图片:The output from the POST request from the Chrome Dev Tools looks like this

我还将控制器更改为...,这为我提供了上面 chrome 开发工具日志中的输出。

`import Ember from 'ember'`

ContactsNewController = Ember.ObjectController.extend(
  actions:
    createContact: ->
      firstName = @get('firstName')
      lastName  = @get('lastName')
      email     = @get('email')
      title     = @get('title')

    newRecord = @store.createRecord('contact', {
                   firstName: firstName,
                   lastName:  lastName,
                   email:     email,
                   title:     title
                 })

    self = this

    transitionToContact = (contact) ->
      self.transitionToRoute('contacts.show', contact)

    newRecord.save().then(transitionToContact)
   )

`export default ContactsNewController`

【问题讨论】:

  • 您能否显示 Ember 发送的请求(例如通过 chrome 控制台?)。你能使用 curl 发出有效的 POST 请求吗?
  • 我对 Grape 一无所知,但您在 Ember 模型中的字段似乎是驼峰式,但在您的后端,这些字段被强调了。 (名字与名字)
  • @ThomasBrus 我添加了从 Postman 发送的成功 cURL 的图像以及从 Chrome 开发工具输出中查看的从 ember 发送时请求失败的图像。 ember帖子的序列化一定是错误的,不是吗?因为从外观上看,API 自己处理得很好。
  • @Griffosx 我认为这是一个问题,但在帖子的更新部分,我的输出 JSON 似乎被我设置的 ActiveModelAdapter 从骆驼正确序列化为蛇余烬应用程序。在 Rails 中,我正在运行一个 ActiveModelSerializer,当发送到 Ember 时,它还将所有 JSON 从蛇形转换为驼峰形;但是,我确实认为您是对的,因为 cURL 正在工作(通过 PostMan),我的 Ember 应用程序将数据发送回服务器的方式存在问题。
  • 我同意@Griffosx 的观点,即“简单”的路径是将所有属性在两个方向上都保持为snake_case。将您的模型修改为 first_name 等,然后摆脱将数据更改为 CamelCase 的活动模型序列化程序。

标签: ruby-on-rails ruby-on-rails-4 ember.js grape grape-api


【解决方案1】:

我对 Ember.js 一无所知,但我已经使用 Grape 构建 API 有一段时间了,所以我想我可以帮助你。查看您附加的图像,Ember.js 似乎在 Payload 中创建了一个不正确的 JSON,而您的 Grape API 并不期望以这种方式格式化的 JSON。正如您在第二张图片中看到的那样,它正在创建这样的 JSON:

{ contact: {...} }

但是,您的 API 需要这样的 JSON 格式:

{ first_name: "" ... }

现在,看看您是如何通过 Chrome 开发工具发送相同的请求的……您正在使用“form-data”选项来创建正文请求,这就是为什么在这种特定情况下它可以正常工作的原因。尝试将其更改为“原始”并将错误的 JSON 放在上面,您将得到与使用 Ember.Js 相同的错误。

我没有具体的解决方案,因为我没有帮助您使用 Ember.Js 的专业知识...但是您必须更改 Amber.js 应用程序中的某些内容,使其创建 JSON 请求,例如这个:

{ first_name: "", last_name: "", email: "" }

代替:

{ contact: { first_name: "" ... } }

希望对你有帮助!

更新

解决您的问题的另一个方法是更改​​您的 Grape API。在这种情况下,您必须在 params 块内创建一个 group 块,就像这样(考虑到联系人字段现在存储在 params[:contact]哈希):

desc 'Post a contact'
params do
    group :contact, type: Hash do
        requires :first_name, type: String, desc: 'First name of contact'
        requires :last_name , type: String, desc: 'Last name of the contact'
        requires :email     , type: String, desc: 'Email of the contact'
        requires :title     , type: String, desc: 'Title of the contact'
    end
end
post do
    Contact.create!(
        first_name: params[:contact][:first_name],
        last_name:  params[:contact][:last_name],
        email:      params[:contact][:email],
        title:      params[:contact][:title]
    )
end

【讨论】:

  • 这绝对是这里的问题。谢谢你。 /// Failing example /// Passing example /// 不过我有一个问题。我将尝试更改我的 Ember 应用程序的 JSON 输出以及更改 Grape API 接受的 JSON。我担心如果我更改 Ember 应用程序的 JSON 输出,它会与应用程序的默认行为发生冲突,而我自己构建了 API;更有可能是我错误地命名了它接受的 JSON。可以通过一个简短的示例来帮助我正确命名 API 吗?
  • 我更改了答案,向您展示如何修改您的 API 处理 JSON 的方式。
  • 好吧,我只有在将代码更改为(/n 表示换行符)\\ params do /n group :contact, type: Hash do /n ... 后才能工作。没有 type: Hash,Grape 认为我正在发送一个数组,所以它一直抛出 400 - 'contact' 无效,但是使用 type: Hash,它可以完美运行。谢谢你的帮助。当您更新答案以反映此更改时,请告诉我,我会接受它作为解决方案。
  • 是的!你是对的。我忘记将类型参数添加到“组”块中。我刚刚编辑了答案以反映这种变化。谢谢!
  • 感谢所有帮助!它现在运行良好。
【解决方案2】:

如果您想更改 Ember 格式化 JSON 的方式,您需要创建一个自定义序列化程序并覆盖 serializeIntoHash 函数。您可以使用此方法自定义序列化为 JSON 的根密钥。默认情况下,REST Serializer 发送模型的 typeKey,它是名称的骆驼化版本。

Ember-cli 带有一个用于启动序列化程序的生成器。你可以运行它:

 ember g serializer Contact

开箱即用的序列化器将如下所示:

import DS from 'ember-data';

export default DS.RESTSerializer.extend({
});

要使其与葡萄一起使用,您可以这样做:

import DS from 'ember-data';

export default DS.RESTSerializer.extend({
  serializeIntoHash: function(data, type, record, options) {
    var properties = this.serialize(record, options);
    for(var prop in properties){
      if(properties.hasOwnProperty(prop)){
        data[prop] = properties[prop];
      }
    }
  }
});

更多信息请关注documentation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    相关资源
    最近更新 更多