Java 弹簧靴不';t将表单发送的数据绑定到POST端点 问题的描述

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模板编

Spring boot找不到请求正文中发送的数据

如下所述,在代码摘录中,我将带有
application/x-www-form-urlencoded
内容类型的表单发送到端点
POST/cards
。 good方法由Spring boot调用,但来自请求主体的数据没有加载到card实体中,该实体作为参数传递(请参见下面的控制台输出)

版本:

  • 弹簧靴:2.3.4.1释放
  • 弹簧靴启动器自由标记:2.3.4.1释放
  • 控制台输出(在请求筛选器中手动读取请求正文): HTML表单(使用freemarker模板编写):
    
    标题和搜索引擎优化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;
        }
    }