Java 弹簧靴不';t将表单发送的数据绑定到POST端点 问题的描述
Spring boot找不到请求正文中发送的数据 如下所述,在代码摘录中,我将带有Java 弹簧靴不';t将表单发送的数据绑定到POST端点 问题的描述,java,forms,spring-boot,post,freemarker,Java,Forms,Spring Boot,Post,Freemarker,Spring boot找不到请求正文中发送的数据 如下所述,在代码摘录中,我将带有application/x-www-form-urlencoded内容类型的表单发送到端点POST/cards。 good方法由Spring boot调用,但来自请求主体的数据没有加载到card实体中,该实体作为参数传递(请参见下面的控制台输出) 版本: 弹簧靴:2.3.4.1释放 弹簧靴启动器自由标记:2.3.4.1释放 控制台输出(在请求筛选器中手动读取请求正文): HTML表单(使用freemarker模板编
application/x-www-form-urlencoded
内容类型的表单发送到端点POST/cards
。
good方法由Spring boot调用,但来自请求主体的数据没有加载到card实体中,该实体作为参数传递(请参见下面的控制台输出)
版本:
标题和搜索引擎优化slug代码
保持搜索引擎优化弹头非常小,并删除无用的话。
描述
请将此描述尽可能小。
内容
拯救
卡实体
@实体
公共类卡工具{
受保护的卡(){}
公共静态最终卡defaultEmptyCard=新卡();
私有最终静态记录器Logger=LoggerFactory.getLogger(Card.class);
@自动连线
私有对象映射器对象映射器;
@身份证
@GeneratedValue(策略=GenerationType.AUTO)
私人长id;
@NotBlank(message=“seoCode(slug)的值是必需的”)
@列(唯一=真)
私有字符串seoCode;
@JsonDeserialize(使用=LocalDateDeserializer.class)
@JsonSerialize(使用=LocalDateSerializer.class)
私有localdatepublisheddate;
@NotBlank(message=“标题值为必填项”)
私有字符串标题;
@NotBlank(message=“说明值为必填项”)
私有字符串描述;
@NotBlank(message=“内容值为必填项”)
私有字符串内容;
公共布尔值hasIdUndefine(){
返回null==id;
}
公共布尔值hasIdDefined(){
返回null!=id;
}
公共长getId(){
返回id;
}
公共字符串getSeoCode(){
返回seoCode;
}
public LocalDate getPublishedDate(){
返回数据;
}
公共字符串getTitle(){
返回标题;
}
公共字符串getDescription(){
返回说明;
}
公共字符串getContent(){
返回内容;
}
私有字符串格式EOCODE(字符串候选EOCODE){
返回candidateSeoCode.replaceAll(“[^0-9a-zA-Z_u-]”和“);
}
私人卡(
@非空字符串rawSeoCode,
@非空字符串标题,
@非空字符串描述,
@非空字符串内容,
@非Null LocalDate publishedDate
) {
this.seoCode=格式化seoCode(rawsecode);
this.title=标题;
this.description=描述;
this.content=内容;
this.publishedDate=publishedDate;
}
公共静态卡(
@非空字符串seoCode,
@非空字符串标题,
@非空字符串描述,
@非空字符串内容,
@非Null LocalDate publishedDate
) {
归还新卡(
seoCode,
标题
描述
内容,,
出版日期
);
}
公共静态卡(
@非空字符串seoCode,
@非空字符串标题,
@非空字符串描述,
@非空字符串内容
) {
LocalDate publishedDate=LocalDate.now();
归还新卡(
seoCode,
标题
描述
内容,,
出版日期
);
}
@凌驾
公共布尔等于(对象o){
如果(this==o)返回true;
如果(o==null | | getClass()!=o.getClass())返回false;
卡片=(卡片)o;
返回Objects.equals(id,card.id)&&
seoCode.equals(card.seoCode)&&
publishedDate.equals(card.publishedDate)&&
头衔.相等(卡片.头衔)&&
description.equals(卡片描述)&&
content.equals(card.content);
}
@凌驾
公共int hashCode(){
返回Objects.hash(id、seoCode、publishedDate、title、description、content);
}
@凌驾
公共字符串toString(){
返回“卡片{”+
“id=”+id+
“,seoCode='”+seoCode+'\''+
“,publishedDate=“+publishedDate”+
“,title='”+title+'\''+
,description=''+description+'\''+
,content=''+content+'\''+
'}';
}
公共或安全或序列化(
对象映射器对象映射器
) {
试一试{
返回权限(objectMapper.writeValueAsString(this));
}捕获(JsonProcessingException e){
logger.error(例如getMessage());
左转(e);
}
}
公共或安全JSONSerialize(){
试一试{
返回权限(objectMapper.writeValueAsString(this));
}捕获(JsonProcessingException e){
logger.error(例如getMessage());
左转(e);
}
}
@凌驾
公共整数比较(@NotNull卡o){
int publicationOrder=this.publishedDate.compareTo(o.publishedDate);
int defaultOrder=this.seoCode.compareTo(o.seoCode);
return publicationOrder==0?默认顺序:publicationOrder;
}
}
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
@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;
}
}