为什么@RequestBody得到一个带有null属性的对象

Den*_*sMP 3 rest json spring-boot angular

我有一个 springboot REST 控制器,其中包含所示的 PATCH 和 PUT 请求方法。由于某种原因,@RequestBody“公司”的字段/属性作为空值出现。我缺少什么?

\n\n

我在前端使用 angular8,它正在执行 PATCH 调用。

\n\n

我尝试过其他一些帖子的建议,但没有运气。如果这是一个重复的问题,请指出我的答案。

\n\n
Spring Tool Suite 4 \n
Run Code Online (Sandbox Code Playgroud)\n\n

版本:4.1.0.RELEASE\n内部版本号:201812201347

\n\n

版权所有 (c) 2007 - 2018 Pivotal, Inc.\n保留所有权利。访问http://spring.io/tools4

\n\n

我正在使用 pgAdmin 4.12 作为 postgres。

\n\n

这是我从 Angular 发出的电话:

\n\n
    this.companyService.patch$(this.currentId, this.appMenu.currentObject).subscribe(selectedCompany => {this.appMenu.currentObject = selectedCompany});\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是如上所述调用的角度服务:

\n\n
    import { Injectable } from \'@angular/core\';\nimport { HttpClient } from \'@angular/common/http\';\nimport { Observable, BehaviorSubject } from \'rxjs\';\nimport { Company } from \'../models/company.model\';\n\n@Injectable({\n  providedIn: \'root\'\n})\nexport class CompanyService {\n  private url: string;\n\n  constructor(private http: HttpClient) {\n    this.url = "http://localhost:8080/niche/company";\n  }\n\n  getOne$ = (companyId: number): Observable<Company> => this.http.get<Company>(`${this.url}/${companyId}`);\n\n  get$ = (): Observable<Company[]> => this.http.get<Company[]>(this.url);\n\n  post$ = (company: Company): Observable<Company> => this.http.post<Company>(this.url, { company });\n\n  patch$ = (companyId: number, company: Company): Observable<Company> => this.http.patch<Company>(`${this.url}/${companyId}`, { company });\n\n  delete$ = (companyId: number): Observable<Company> => this.http.delete<Company>(`${this.url}/${companyId}`);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

从 Angular 前端请求有效负载:

\n\n
    {company: {createdBy: "denis", createdDate: "2019-04-14T04:00:00.000+0000", updatedBy: "denis",\xe2\x80\xa6}}\ncompany: {createdBy: "denis", createdDate: "2019-04-14T04:00:00.000+0000", updatedBy: "denis",\xe2\x80\xa6}\ncompanyName: "Bull Winkle"\ncreatedBy: "denis"\ncreatedDate: "2019-04-14T04:00:00.000+0000"\nemail: "bullwinkle@mail.com"\nid: 2\nnotes: "test"\nphone: "999999999"\nproducts: []\nupdatedBy: "denis"\nupdatedDate: "2019-05-14T04:00:00.000+0000"\nwebSite: "bullwilkle.com"\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是实际的 JSON:

\n\n
{"company":{"createdBy":"denis","createdDate":"2019-04-14T04:00:00.000+0000","updatedBy":"denis","updatedDate":"2019-05-14T04:00:00.000+0000","id":2,"email":"bullwinkle@mail.com","companyName":"Bull Winkle","webSite":"bullwilkle.com","phone":"999999999","notes":"test","products":[]}}\n
Run Code Online (Sandbox Code Playgroud)\n\n

springboot后端控制器:

\n\n
    /**\n * \n */\npackage com.ebusiness.niche.controller;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.CrossOrigin;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestBody;\n\nimport com.ebusiness.niche.entity.Company;\nimport com.ebusiness.niche.service.CompanyService;\n//import com.sun.istack.internal.logging.Logger;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @author denisputnam\n *\n */\n@RestController\n@RequestMapping( value = "/niche" )\npublic class CompanyController {\n    private final Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private CompanyService companyService;\n\n    @ResponseBody\n    @RequestMapping(value = {"/company"}, method = { RequestMethod.GET })\n    @CrossOrigin(origins = "http://localhost:4200")\n    public ResponseEntity<List<Company>> getCompanys() {\n        log.info("getCompanys(): Called...");\n\n        List<Company> companyList = null;\n\n        companyList = this.companyService.findAll();\n\n        if( companyList == null || companyList.isEmpty() ) {\n            log.info("getCompanys(): returned a null or empty list.");\n            ResponseEntity<List<Company>> rVal = new ResponseEntity<List<Company>>(companyList, HttpStatus.NO_CONTENT);\n            return rVal;\n        }\n        return new ResponseEntity<List<Company>>(companyList, HttpStatus.OK);       \n    }\n\n    @ResponseBody\n    @RequestMapping(value = {"/company/{id}"}, method = { RequestMethod.GET })\n    @CrossOrigin(origins = "http://localhost:4200")\n    public ResponseEntity<Company> getCompany(@PathVariable("id") Long id) {\n        log.info("getCompany(): Called...");\n        log.info("id=" + id);\n\n//      List<Company> companyList = null;\n        Optional<Company> optcompany = null;\n        Company company = null;\n\n        optcompany = this.companyService.findById(id);\n\n        if( optcompany == null  ) {\n            log.info("getCompany(): returned a null.");\n            ResponseEntity<Company> rVal = new ResponseEntity<Company>(company, HttpStatus.NO_CONTENT);\n            return rVal;\n        } else {\n            company = optcompany.get();\n        }\n        return new ResponseEntity<Company>(company, HttpStatus.OK);     \n    }\n\n    @ResponseBody\n//  @RequestMapping(value = {"/company/{id}"}, headers = {\n//    "content-type=application/json" }, consumes = MediaType.APPLICATION_JSON_VALUE, method = { RequestMethod.PATCH, RequestMethod.PUT, RequestMethod.POST })\n    @RequestMapping(value = {"/company/{id}"}, method = { RequestMethod.PATCH, RequestMethod.PUT })\n    @CrossOrigin(origins = {"http://localhost:4200"})\n    public ResponseEntity<Company> updateCompany(@PathVariable("id") Long id, @RequestBody Company company) {\n        log.info("updateCompany(): Called...");\n        log.info("id=" + id);\n\n        Optional<Company> currentCompany = this.companyService.findById(id);\n        Company dbCompany = null;\n\n        if( currentCompany == null ) {\n            log.error("Unable to update.  The company with id {} not found.", id);\n            ResponseEntity<Company> rVal = new ResponseEntity<Company>(company, HttpStatus.NO_CONTENT);\n            return rVal;\n        }\n\n        dbCompany = currentCompany.get();\n        dbCompany.setCompanyName(company.getCompanyName());\n        dbCompany.setEmail(company.getEmail());\n        dbCompany.setNotes(company.getNotes());\n        dbCompany.setPhone(company.getPhone());\n        dbCompany.setWebSite(company.getWebSite());\n\n        this.companyService.update(dbCompany);\n\n        return new ResponseEntity<Company>(dbCompany, HttpStatus.OK);       \n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

springboot后端实体bean:

\n\n
    /**\n * \n */\npackage com.ebusiness.niche.entity;\n\nimport java.io.Serializable;\nimport java.util.Set;\n\nimport javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.Table;\nimport javax.persistence.UniqueConstraint;\nimport javax.validation.constraints.NotNull;\n\n/**\n * @author denisputnam\n *\n */\n@Entity\n@Table(\n        uniqueConstraints = {\n                @UniqueConstraint(columnNames = {"email"})\n        }\n)\npublic class Company extends History implements Serializable{\n\n    /**\n     * \n     */\n    private static final long serialVersionUID = 1329304564033025946L;\n\n    @Id\n    @GeneratedValue\n    private Long id;\n\n    @Column\n    @NotNull\n    private String email;\n\n    @Column\n    @NotNull\n    private String companyName;\n\n    @Column\n    @NotNull\n    private String webSite;\n\n    @Column\n    @NotNull\n    private String phone;\n\n    @Column(length=4096)\n    private String notes;\n\n    @ManyToMany(mappedBy="companys")\n    Set<Product> products;\n\n    public String getNotes() {\n        return notes;\n    }\n\n    public void setNotes(String notes) {\n        this.notes = notes;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public void setEmail(String email) {\n        this.email = email;\n    }\n\n    public String getCompanyName() {\n        return companyName;\n    }\n\n    public void setCompanyName(String companyName) {\n        this.companyName = companyName;\n    }\n\n    public String getWebSite() {\n        return webSite;\n    }\n\n    public void setWebSite(String webSite) {\n        this.webSite = webSite;\n    }\n\n    public String getPhone() {\n        return phone;\n    }\n\n    public void setPhone(String phone) {\n        this.phone = phone;\n    }\n\n    public Set<Product> getProducts() {\n        return products;\n    }\n\n    public void setProducts(Set<Product> products) {\n        this.products = products;\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个History基类实体bean:

\n\n
    /**\n * \n */\npackage com.ebusiness.niche.entity;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.MappedSuperclass;\nimport javax.validation.constraints.NotNull;\n\n/**\n * @author denisputnam\n *\n */\n//@Entity\n@MappedSuperclass\npublic class History implements Serializable {\n    /**\n     * \n     */\n    private static final long serialVersionUID = -1136283585074348099L;\n    private String createdBy;\n    @Column(nullable = false)\n    @NotNull\n    private Date createdDate = new Date();\n    private String updatedBy;\n    private Date updatedDate;\n    /**\n     * @return the createdBy\n     */\n    public String getCreatedBy() {\n        return createdBy;\n    }\n    /**\n     * @param createdBy the createdBy to set\n     */\n    public void setCreatedBy(String createdBy) {\n        this.createdBy = createdBy;\n    }\n    /**\n     * @return the createdDate\n     */\n    public Date getCreatedDate() {\n        return createdDate;\n    }\n    /**\n     * @param createdDate the createdDate to set\n     */\n    public void setCreatedDate(Date createdDate) {\n        this.createdDate = createdDate;\n    }\n    /**\n     * @return the updatedBy\n     */\n    public String getUpdatedBy() {\n        return updatedBy;\n    }\n    /**\n     * @param updatedBy the updatedBy to set\n     */\n    public void setUpdatedBy(String updatedBy) {\n        this.updatedBy = updatedBy;\n    }\n    /**\n     * @return the updatedDate\n     */\n    public Date getUpdatedDate() {\n        return updatedDate;\n    }\n    /**\n     * @param updatedDate the updatedDate to set\n     */\n    public void setUpdatedDate(Date updatedDate) {\n        this.updatedDate = updatedDate;\n    }\n\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我编辑了此内容以添加控制台日志中的 TRACE 输出。我还简化了 Company 实体,使其不包含任何日期或与其他实体的任何其他关系,因此现在仅传递字符串。

\n\n

这是跟踪输出:

\n\n
   2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing JDBC resources\n2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] cResourceLocalTransactionCoordinatorImpl : ResourceLocalTransactionCoordinatorImpl#afterCompletionCallback(true)\n2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] .t.i.SynchronizationRegistryStandardImpl : SynchronizationRegistryStandardImpl.notifySynchronizationsAfterTransactionCompletion(3)\n2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] org.hibernate.internal.SessionImpl       : SessionImpl#afterTransactionCompletion(successful=true, delayed=false)\n2019-09-18 15:52:19.594 DEBUG 47732 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using \'application/json\', given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/json, application/*+json]\n2019-09-18 15:52:19.594 TRACE 47732 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [[com.ebusiness.niche.entity.Company@ddb52f3, com.ebusiness.niche.entity.Company@73d5674e]]\n2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : No view rendering, null ModelAndView returned.\n2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] org.hibernate.internal.SessionImpl       : Closing session [f1652eeb-71a2-4776-8d94-9573336d60f3]\n2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.h.e.jdbc.internal.JdbcCoordinatorImpl  : Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl@6effa4c8]\n2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing JDBC resources\n2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl   : Closing logical connection\n2019-09-18 15:52:19.620 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing JDBC resources\n2019-09-18 15:52:19.620 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl   : Logical connection closed\n2019-09-18 15:52:19.620 DEBUG 47732 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 200 OK, headers={masked}\n2019-09-18 15:52:31.192 TRACE 47732 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<com.ebusiness.niche.entity.Company> com.ebusiness.niche.controller.CompanyController.updateCompany(java.lang.Long,com.ebusiness.niche.entity.Company)\n2019-09-18 15:52:31.264 TRACE 47732 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<com.ebusiness.niche.entity.Company> com.ebusiness.niche.controller.CompanyController.updateCompany(java.lang.Long,com.ebusiness.niche.entity.Company)\n2019-09-18 15:52:31.266 TRACE 47732 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : PUT "/niche/company/2", parameters={}, headers={masked} in DispatcherServlet \'dispatcherServlet\'\n2019-09-18 15:52:31.267 TRACE 47732 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity<com.ebusiness.niche.entity.Company> com.ebusiness.niche.controller.CompanyController.updateCompany(java.lang.Long,com.ebusiness.niche.entity.Company)\n2019-09-18 15:52:31.268 TRACE 47732 --- [nio-8080-exec-3] .i.SessionFactoryImpl$SessionBuilderImpl : Opening Hibernate Session.  tenant=null, owner=null\n2019-09-18 15:52:31.268 TRACE 47732 --- [nio-8080-exec-3] org.hibernate.internal.SessionImpl       : Opened Session [740dabea-2970-4491-8ff7-d373afc649f6] at timestamp: 1568836351268\n2019-09-18 15:52:31.268 TRACE 47732 --- [nio-8080-exec-3] o.s.web.cors.DefaultCorsProcessor        : Skip: response already contains "Access-Control-Allow-Origin"\n2019-09-18 15:52:31.312 TRACE 47732 --- [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [com.ebusiness.niche.entity.Company@46388186]\n2019-09-18 15:52:31.320 TRACE 47732 --- [nio-8080-exec-3] .w.s.m.m.a.ServletInvocableHandlerMethod : Arguments: [2, com.ebusiness.niche.entity.Company@46388186]\n
Run Code Online (Sandbox Code Playgroud)\n

Jon*_*ohx 5

好的,这个问题是因为 fromFront End application正在将 JSON 正文作为嵌套公司的 JSON 对象发送。{ company }

patch$ = (companyId: number, company: Company): Observable<Company> 
          => this.http.patch<Company>(`${this.url}/${companyId}`, { company });
Run Code Online (Sandbox Code Playgroud)

然后,当请求到达Back End application字段值时null,因为它是company. 这是通过删除括号来修复的,{}以便将其作为company对象。

patch$ = (companyId: number, company: Company): Observable<Company> 
          => this.http.patch<Company>(`${this.url}/${companyId}`, company);
Run Code Online (Sandbox Code Playgroud)