[英]Spring boot doesn't bind the data sent by form to POST endpoint
Spring boot cannot find the data sent in request body. Spring boot 找不到请求正文中发送的数据。
As specified below, in code extracts, I send form with application/x-www-form-urlencoded
content-type to the endpoint POST /cards
.如下所述,在代码摘录中,我将带有
application/x-www-form-urlencoded
内容类型application/x-www-form-urlencoded
发送到端点POST /cards
。 The good method is called by Spring boot but data from the request body aren't loaded in card entity, which is passed as parameter (see console output below).好的方法由 Spring boot 调用,但请求正文中的数据未加载到卡片实体中,该实体作为参数传递(请参阅下面的控制台输出)。
Versions:版本:
2020-10-21 00:26:58.594 DEBUG 38768 --- [nio-8080-exec-1] c.b.c.c.f.RequestResponseLoggingFilter : New request method=POST path=/cards content-type=application/x-www-form-urlencoded
2020-10-21 00:26:58.595 DEBUG 38768 --- [nio-8080-exec-1] c.b.c.c.f.RequestResponseLoggingFilter : RequestBody: title=First+card&seoCode=first-card&description=This+is+the+first+card+of+the+blog&content=I+think+I+need+help+about+this+one...
### createNewCard ###
card: Card<com.brunierterry.cards.models.Card@34e63b41>{id=null, seoCode='null', publishedDate=null, title='null', description='null', content='null'}
result: org.springframework.validation.BeanPropertyBindingResult: 0 errors
model: {card=Card<com.brunierterry.cards.models.Card@34e63b41>{id=null, seoCode='null', publishedDate=null, title='null', description='null', content='null'}, org.springframework.validation.BindingResult.card=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
2020-10-21 00:26:58.790 TRACE 38768 --- [nio-8080-exec-1] c.b.c.c.f.RequestResponseLoggingFilter : Response to request method=POST path=/cards status=200 elapsedTime=196ms
(Here I read body with req.getReader()
, but I comment it usually to not consume the buffer.) (在这里,我使用
req.getReader()
读取 body,但我通常对其进行注释以不消耗缓冲区。)
@Controller
public class CardController implements ControllerHelper {
@PostMapping(value = "/cards", consumes = MediaType.ALL_VALUE)
public String createNewCard(
@ModelAttribute Card card,
BindingResult result,
ModelMap model
) {
System.out.println("\n### createNewCard ###\n");
System.out.println("card: "+card);
System.out.println("result: "+result);
System.out.println("model: "+model);
return "/cards/editor";
}
@GetMapping(value = "/cards/form")
public String newPost(
Model model
) {
model.addAttribute("card", Card.defaultEmptyCard);
return "/cards/editor";
}
}
<form action="/cards"
method="POST"
modelAttribute="card"
enctype="application/x-www-form-urlencoded"
>
<div class="form-group">
<label for="title">Title & SEO slug code</label>
<div class="form-row">
<div class="col-9">
<@spring.formInput
"card.title"
"class='form-control' placeholder='Title'"
/>
<@spring.showErrors "<br>"/>
</div>
<div class="col-2">
<@spring.formInput
"card.seoCode"
"class='form-control' placeholder='SEO slug code' aria-describedby='seoCodeHelp'"
/>
<@spring.showErrors "<br>"/>
</div>
<div class="col-1">
<@spring.formInput
"card.id"
"DISABLED class='form-control' placeholder='ID'"
/>
</div>
</div>
<div class="form-row">
<small id="seoCodeHelp" class="form-text text-muted">
Keep SEO slug very small and remove useless words.
</small>
</div>
</div>
<div class="form-group">
<label for="description">Description</label>
<@spring.formInput
"card.description"
"class='form-control' placeholder='Short description of this card..' aria-describedby='descriptionHelp'"
/>
<small id="descriptionHelp" class="form-text text-muted">
Keep this description as small as possible.
</small>
</div>
<div class="form-group">
<label for="content">Content</label>
<@spring.formTextarea
"card.content"
"class='form-control' rows='5'"
/>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
@Entity
public class Card implements Comparable<Card> {
protected Card() {}
public static final Card defaultEmptyCard = new Card();
private final static Logger logger = LoggerFactory.getLogger(Card.class);
@Autowired
private ObjectMapper objectMapper;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@NotBlank(message = "Value for seoCode (the slug) is mandatory")
@Column(unique=true)
private String seoCode;
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate publishedDate;
@NotBlank(message = "Value for title is mandatory")
private String title;
@NotBlank(message = "Value for description is mandatory")
private String description;
@NotBlank(message = "Value for content is mandatory")
private String content;
public boolean hasIdUndefine() {
return null == id;
}
public boolean hasIdDefined() {
return null != id;
}
public Long getId() {
return id;
}
public String getSeoCode() {
return seoCode;
}
public LocalDate getPublishedDate() {
return publishedDate;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getContent() {
return content;
}
private String formatSeoCode(String candidateSeoCode) {
return candidateSeoCode.replaceAll("[^0-9a-zA-Z_-]","");
}
private Card(
@NonNull String rawSeoCode,
@NonNull String title,
@NonNull String description,
@NonNull String content,
@NonNull LocalDate publishedDate
) {
this.seoCode = formatSeoCode(rawSeoCode);
this.title = title;
this.description = description;
this.content = content;
this.publishedDate = publishedDate;
}
public static Card createCard(
@NonNull String seoCode,
@NonNull String title,
@NonNull String description,
@NonNull String content,
@NonNull LocalDate publishedDate
) {
return new Card(
seoCode,
title,
description,
content,
publishedDate
);
}
public static Card createCard(
@NonNull String seoCode,
@NonNull String title,
@NonNull String description,
@NonNull String content
) {
LocalDate publishedDate = LocalDate.now();
return new Card(
seoCode,
title,
description,
content,
publishedDate
);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Card card = (Card) o;
return Objects.equals(id, card.id) &&
seoCode.equals(card.seoCode) &&
publishedDate.equals(card.publishedDate) &&
title.equals(card.title) &&
description.equals(card.description) &&
content.equals(card.content);
}
@Override
public int hashCode() {
return Objects.hash(id, seoCode, publishedDate, title, description, content);
}
@Override
public String toString() {
return "Card<"+ super.toString() +">{" +
"id=" + id +
", seoCode='" + seoCode + '\'' +
", publishedDate=" + publishedDate +
", title='" + title + '\'' +
", description='" + description + '\'' +
", content='" + content + '\'' +
'}';
}
public Either<JsonProcessingException,String> safeJsonSerialize(
ObjectMapper objectMapper
) {
try {
return Right(objectMapper.writeValueAsString(this));
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return Left(e);
}
}
public Either<JsonProcessingException,String> safeJsonSerialize() {
try {
return Right(objectMapper.writeValueAsString(this));
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return Left(e);
}
}
@Override
public int compareTo(@NotNull Card o) {
int publicationOrder = this.publishedDate.compareTo(o.publishedDate);
int defaultOrder = this.seoCode.compareTo(o.seoCode);
return publicationOrder == 0 ? defaultOrder : publicationOrder;
}
}
I got a good answer.我得到了一个很好的答案。 It works when adding empty constructor and setters to the Card entity.
它在向 Card 实体添加空构造函数和 setter 时起作用。 However, it's not the class I want.
然而,这不是我想要的课程。 I want card to be only instantiated with a constructor that have all parameters.
我希望卡仅使用具有所有参数的构造函数进行实例化。 Do you have an idea about how to achieve that ?
您知道如何实现这一目标吗? Should I create another class to represent the form ?
我应该创建另一个类来表示表单吗? Oris there a way to only allow Spring to use such setters ?
或者有没有办法只允许 Spring 使用这样的 setter?
Did you make sure that you Card.java
has the appropriate getters and setters?你确定你的
Card.java
有合适的 getter 和 setter 吗? This way spring can actually populate the data in the object it is trying to create.通过这种方式,spring 实际上可以填充它试图创建的对象中的数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.