为啥@RequestBody 得到一个具有空属性的对象
Posted
技术标签:
【中文标题】为啥@RequestBody 得到一个具有空属性的对象【英文标题】:Why is the @RequestBody getting an object with null attributes为什么@RequestBody 得到一个具有空属性的对象 【发布时间】:2020-01-18 15:39:55 【问题描述】:我有一个 springboot REST 控制器,它带有 PATCH 和 PUT 的请求方法,如图所示。出于某种原因,@RequestBody“公司”的字段/属性以空值的形式出现。我错过了什么?
我在前端使用 angular8,它正在执行 PATCH 调用。
我尝试了其他一些帖子建议,但没有运气。如果这是一个重复的问题,请指出答案。
Spring Tool Suite 4
版本:4.1.0.RELEASE 内部版本号:201812201347
版权 (c) 2007 - 2018 Pivotal, Inc. 版权所有。访问http://spring.io/tools4
我正在为 postgres 使用 pgAdmin 4.12。
这是我从 Angular 发出的调用:
this.companyService.patch$(this.currentId, this.appMenu.currentObject).subscribe(selectedCompany => this.appMenu.currentObject = selectedCompany);
这是如上所述调用的角度服务:
import Injectable from '@angular/core';
import HttpClient from '@angular/common/http';
import Observable, BehaviorSubject from 'rxjs';
import Company from '../models/company.model';
@Injectable(
providedIn: 'root'
)
export class CompanyService
private url: string;
constructor(private http: HttpClient)
this.url = "http://localhost:8080/niche/company";
getOne$ = (companyId: number): Observable<Company> => this.http.get<Company>(`$this.url/$companyId`);
get$ = (): Observable<Company[]> => this.http.get<Company[]>(this.url);
post$ = (company: Company): Observable<Company> => this.http.post<Company>(this.url, company );
patch$ = (companyId: number, company: Company): Observable<Company> => this.http.patch<Company>(`$this.url/$companyId`, company );
delete$ = (companyId: number): Observable<Company> => this.http.delete<Company>(`$this.url/$companyId`);
从 Angular 前端请求有效负载:
company: createdBy: "denis", createdDate: "2019-04-14T04:00:00.000+0000", updatedBy: "denis",…
company: createdBy: "denis", createdDate: "2019-04-14T04:00:00.000+0000", updatedBy: "denis",…
companyName: "Bull Winkle"
createdBy: "denis"
createdDate: "2019-04-14T04:00:00.000+0000"
email: "bullwinkle@mail.com"
id: 2
notes: "test"
phone: "999999999"
products: []
updatedBy: "denis"
updatedDate: "2019-05-14T04:00:00.000+0000"
webSite: "bullwilkle.com"
这是实际的 JSON:
"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":[]
springboot 后端控制器:
/**
*
*/
package com.ebusiness.niche.controller;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import com.ebusiness.niche.entity.Company;
import com.ebusiness.niche.service.CompanyService;
//import com.sun.istack.internal.logging.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author denisputnam
*
*/
@RestController
@RequestMapping( value = "/niche" )
public class CompanyController
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private CompanyService companyService;
@ResponseBody
@RequestMapping(value = "/company", method = RequestMethod.GET )
@CrossOrigin(origins = "http://localhost:4200")
public ResponseEntity<List<Company>> getCompanys()
log.info("getCompanys(): Called...");
List<Company> companyList = null;
companyList = this.companyService.findAll();
if( companyList == null || companyList.isEmpty() )
log.info("getCompanys(): returned a null or empty list.");
ResponseEntity<List<Company>> rVal = new ResponseEntity<List<Company>>(companyList, HttpStatus.NO_CONTENT);
return rVal;
return new ResponseEntity<List<Company>>(companyList, HttpStatus.OK);
@ResponseBody
@RequestMapping(value = "/company/id", method = RequestMethod.GET )
@CrossOrigin(origins = "http://localhost:4200")
public ResponseEntity<Company> getCompany(@PathVariable("id") Long id)
log.info("getCompany(): Called...");
log.info("id=" + id);
// List<Company> companyList = null;
Optional<Company> optcompany = null;
Company company = null;
optcompany = this.companyService.findById(id);
if( optcompany == null )
log.info("getCompany(): returned a null.");
ResponseEntity<Company> rVal = new ResponseEntity<Company>(company, HttpStatus.NO_CONTENT);
return rVal;
else
company = optcompany.get();
return new ResponseEntity<Company>(company, HttpStatus.OK);
@ResponseBody
// @RequestMapping(value = "/company/id", headers =
// "content-type=application/json" , consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PATCH, RequestMethod.PUT, RequestMethod.POST )
@RequestMapping(value = "/company/id", method = RequestMethod.PATCH, RequestMethod.PUT )
@CrossOrigin(origins = "http://localhost:4200")
public ResponseEntity<Company> updateCompany(@PathVariable("id") Long id, @RequestBody Company company)
log.info("updateCompany(): Called...");
log.info("id=" + id);
Optional<Company> currentCompany = this.companyService.findById(id);
Company dbCompany = null;
if( currentCompany == null )
log.error("Unable to update. The company with id not found.", id);
ResponseEntity<Company> rVal = new ResponseEntity<Company>(company, HttpStatus.NO_CONTENT);
return rVal;
dbCompany = currentCompany.get();
dbCompany.setCompanyName(company.getCompanyName());
dbCompany.setEmail(company.getEmail());
dbCompany.setNotes(company.getNotes());
dbCompany.setPhone(company.getPhone());
dbCompany.setWebSite(company.getWebSite());
this.companyService.update(dbCompany);
return new ResponseEntity<Company>(dbCompany, HttpStatus.OK);
springboot后端实体bean:
/**
*
*/
package com.ebusiness.niche.entity;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
/**
* @author denisputnam
*
*/
@Entity
@Table(
uniqueConstraints =
@UniqueConstraint(columnNames = "email")
)
public class Company extends History implements Serializable
/**
*
*/
private static final long serialVersionUID = 1329304564033025946L;
@Id
@GeneratedValue
private Long id;
@Column
@NotNull
private String email;
@Column
@NotNull
private String companyName;
@Column
@NotNull
private String webSite;
@Column
@NotNull
private String phone;
@Column(length=4096)
private String notes;
@ManyToMany(mappedBy="companys")
Set<Product> products;
public String getNotes()
return notes;
public void setNotes(String notes)
this.notes = notes;
public Long getId()
return id;
public void setId(Long id)
this.id = id;
public String getEmail()
return email;
public void setEmail(String email)
this.email = email;
public String getCompanyName()
return companyName;
public void setCompanyName(String companyName)
this.companyName = companyName;
public String getWebSite()
return webSite;
public void setWebSite(String webSite)
this.webSite = webSite;
public String getPhone()
return phone;
public void setPhone(String phone)
this.phone = phone;
public Set<Product> getProducts()
return products;
public void setProducts(Set<Product> products)
this.products = products;
这个 History 基类实体 bean:
/**
*
*/
package com.ebusiness.niche.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.validation.constraints.NotNull;
/**
* @author denisputnam
*
*/
//@Entity
@MappedSuperclass
public class History implements Serializable
/**
*
*/
private static final long serialVersionUID = -1136283585074348099L;
private String createdBy;
@Column(nullable = false)
@NotNull
private Date createdDate = new Date();
private String updatedBy;
private Date updatedDate;
/**
* @return the createdBy
*/
public String getCreatedBy()
return createdBy;
/**
* @param createdBy the createdBy to set
*/
public void setCreatedBy(String createdBy)
this.createdBy = createdBy;
/**
* @return the createdDate
*/
public Date getCreatedDate()
return createdDate;
/**
* @param createdDate the createdDate to set
*/
public void setCreatedDate(Date createdDate)
this.createdDate = createdDate;
/**
* @return the updatedBy
*/
public String getUpdatedBy()
return updatedBy;
/**
* @param updatedBy the updatedBy to set
*/
public void setUpdatedBy(String updatedBy)
this.updatedBy = updatedBy;
/**
* @return the updatedDate
*/
public Date getUpdatedDate()
return updatedDate;
/**
* @param updatedDate the updatedDate to set
*/
public void setUpdatedDate(Date updatedDate)
this.updatedDate = updatedDate;
我对此进行了编辑以添加来自控制台日志的 TRACE 输出。我还简化了 Company 实体,使其不包含任何日期或与其他实体的任何其他关系,因此现在只传递字符串。
这是 TRACE 输出:
2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources
2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] cResourceLocalTransactionCoordinatorImpl : ResourceLocalTransactionCoordinatorImpl#afterCompletionCallback(true)
2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] .t.i.SynchronizationRegistryStandardImpl : SynchronizationRegistryStandardImpl.notifySynchronizationsAfterTransactionCompletion(3)
2019-09-18 15:52:19.591 TRACE 47732 --- [nio-8080-exec-1] org.hibernate.internal.SessionImpl : SessionImpl#afterTransactionCompletion(successful=true, delayed=false)
2019-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]
2019-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]]
2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] org.hibernate.internal.SessionImpl : Closing session [f1652eeb-71a2-4776-8d94-9573336d60f3]
2019-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]
2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources
2019-09-18 15:52:19.619 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl : Closing logical connection
2019-09-18 15:52:19.620 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.ResourceRegistryStandardImpl : Releasing JDBC resources
2019-09-18 15:52:19.620 TRACE 47732 --- [nio-8080-exec-1] o.h.r.j.i.LogicalConnectionManagedImpl : Logical connection closed
2019-09-18 15:52:19.620 DEBUG 47732 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK, headers=masked
2019-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)
2019-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)
2019-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'
2019-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)
2019-09-18 15:52:31.268 TRACE 47732 --- [nio-8080-exec-3] .i.SessionFactoryImpl$SessionBuilderImpl : Opening Hibernate Session. tenant=null, owner=null
2019-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
2019-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"
2019-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]
2019-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]
【问题讨论】:
在这行companyService.update(dbCompany);
之前,您是否将 bdCompany 设为空?您可以调试并让我们知道这是否没有属性为 null 则意味着当公司服务更新时 dbCompany 正在那里做某事。
从 find 调用返回的 currentCompany 中设置 dbCompany。这不是空的。问题是“company”参数具有 null 属性,这会导致在 update() 调用上为 null 值触发约束。我需要知道为什么属性作为公司参数的空值出现。
payload 显示 angular 正在设置 json 请求的属性。
那你是在假设后端的问题吗?正确的? Optional<Company> currentCompany = this.companyService.findById(id);
让我们知道这个变量是否有一些带有值(非空)的字段,如果这是服务中的问题。
嗨乔纳森,我没有假设任何事情。我只知道调试器显示@RequestBody 公司具有空属性。 currentCompany 和 dbCompany 不是问题。它是传递给请求的参数。从 Angular 发送带有给定有效负载的请求的时间,到它到达 updateCompany() 的请求方法的时间,出现了问题。只要“company”参数属性为空,dbCompany 变量就会设置错误。
【参考方案1】:
好的,这个问题是因为从Front End application
发送了一个 JSON 主体作为嵌套了公司的 JSON 对象。 company
patch$ = (companyId: number, company: Company): Observable<Company>
=> this.http.patch<Company>(`$this.url/$companyId`, company );
然后当请求到达Back End application
时,字段值是null
,因为它是company
中的一个JSON 对象。已通过删除括号 以将
company
作为对象来解决此问题。
patch$ = (companyId: number, company: Company): Observable<Company>
=> this.http.patch<Company>(`$this.url/$companyId`, company);
【讨论】:
有效载荷需要看起来像,"createdBy":"denis","createdDate":"2019-04-14","updatedBy":"denis","updatedDate":"2019 -05-14","id":2,"email":"bullwinkle@mail.com","companyName":"Bull Winkle","webSite":"bullwilkle.com","phone":"999999999" ,"notes":"test" 而不是 "company":"createdBy":"denis","createdDate":"2019-04-14","updatedBy":"denis","updatedDate": "2019-05-14","id":2,"email":"bullwinkle@mail.com","companyName":"Bull Winkle","webSite":"bullwilkle.com","电话":" 999999999","notes":"test" 感谢乔纳森,感谢您的帮助。【参考方案2】:我的 Spring Boot 后端代码
@PutMapping("updateFile")
public ResponseEntity<String> updateAttachment(@RequestBody Attachment attachment)
storageService.updateAttachment(attachment);
return ResponseEntity.ok("file updated successfully");
我之前尝试过下面的代码,并且在后端 attachment
整个对象发生 null
const message = await axiosInstance.put<string>(
`localhost:8080/files/updateFile`,
attachment:attachment,
);
下面的代码有效。传递对象时,只需传递整个对象而不使用括号。它将被视为数据。
const message = await axiosInstance.put<string>(
`localhost:8080/files/updateFile`,
attachment,
);
【讨论】:
以上是关于为啥@RequestBody 得到一个具有空属性的对象的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 ::after 伪元素上需要一个空的 `content` 属性? [复制]
为啥 Flutter Floor DAO findAll 返回具有空 ID 的实体集?