化石原创文章,转载请注明来源并保留原文链接


1、使用Maven,pom文件中加入:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2、在需要的field上,比如某个类的一个变量不能为null或者empty

import javax.validation.constraints.NotBlank;

public class Article {
    @NotBlank(message = "title cannot be blank")
    private String title;
}

3、使用到Article实例的地方,用上@valid

import javax.validation.Valid;

public ResponseEntity<?> saveArticle(@Valid @RequestBody Article article) {
}

到此,如果不能通过验证(例子中title如果是null或者trim()后长度为零),那么底层就直接拦住了,saveArticle()不会得到调用。

但是用户收到的信息缺失一个404的错误。这里可以定制处理。

4、写一个继承ResponseEntityExceptionHandler的类

package API;

import java.util.ArrayList;
import java.util.List;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@SuppressWarnings({"unchecked","rawtypes"})
@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
        List<String> details = new ArrayList<>();
        details.add(ex.getLocalizedMessage());
        ErrorResponse error = new ErrorResponse("Server Error", details);
        return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<String> details = new ArrayList<>();
        for(ObjectError error : ex.getBindingResult().getAllErrors()) {
            details.add(error.getDefaultMessage());
        }
        ErrorResponse error = new ErrorResponse("Validation Failed", details);
        return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
    }
}

其中的方法handleMethodArgumentNotValid,就是用来处理参数验证错误。

ErrorResponse是一个自定义的类:

package API;

import java.util.List;

public class ErrorResponse {
    private String message;
    private List<String> details;

    public ErrorResponse(String message, List<String> details) {
        super();
        this.message = message;
        this.details = details;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public List<String> getDetails() {
        return details;
    }

    public void setDetails(List<String> details) {
        this.details = details;
    }
}

注意getter一定要写,否则ajax的error回调,responseText是空的json。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


使用Ajax,我们可以指定Post的格式,比如json、uft-8

    var post = {"title": title, "content": content, "catalog": catalog};
    
    $.ajax({
        type: 'post',
        async: true,
        data: JSON.stringify(post),
        url: document.location.origin + '/api/savearticle',
        dataType:'json',
        contentType: 'application/json; charset=utf-8',
        success: function(data) {
            console.log("保存成功");
        },
        error: function () {
            console.log("Ajax 发生错误!");
        }
    });

上面例子上的post数据,可以在SpringBoot服务器端用这样的方式接收:

@RestController
public class ArticleApi {
    @RequestMapping(value = "/api/savearticle", method = RequestMethod.POST,  consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> saveArticle(@RequestBody Article article) {

    }
}

其中,Article是这样:

public class Article {
    private String title;
    private String content;
    private String catalog;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getCatalog() {
        return catalog;
    }

    public void setCatalog(String catalog) {
        this.catalog = catalog;
    }
}

这样的方式,能让SpringBoot直接在底层就帮我们绑定好数据。如果加上@Valid,什么数据验证、取数据栏位等工作就方便多了,可以少些很多代码,提升开发效率。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


一般上传的时候目录不会直接放在默认的静态资源目录下面。因为这样就绑定了原来的网站内容。

1、实现让上传的图片放置到服务器的其他文件夹,这个在springboot的接收上传的代码中可以通过路径支持到。

package main.controller.admin;

import main.StringHelper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@RestController
public class UploadPageController {
    @PostMapping("/upload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
            return "error";
        }

        try {
            String originalFilename = file.getOriginalFilename();
            int dot = originalFilename.lastIndexOf('.');
            String extension = file.getOriginalFilename().substring(dot + 1);

            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
            String destFileName = now.format(formatter) + StringHelper.getAlphaNumericString(8) + "." + extension;
            String filePath = "f:/" + destFileName;
            File dest = new File(filePath);
            file.transferTo(dest);
//            byte[] bytes = file.getBytes();
//            Path path = Paths.get(filePath);
//            Files.write(path, bytes);

            return destFileName;

        } catch (IOException e) {
            e.printStackTrace();
        }

        return "error";
    }
}

上面的这段代码,在接收到图片的时候,把目录定在了f盘的根目录(在windows上开发的时候使用)。

2、springboot配置,让我们的f:/成为静态资源的搜索目录的一部分。这个需要在springboot工程的application.properties中完成。

web.upload-path=f:/
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${web.upload-path}

上面最后file:${web.upload-path}指定了f:/。其他几个目录其实都是springboot内置的。

3、完成上面两步后,这样我们在上传的时候,就能得到服务器上的该上传图片对应的名称(上面的1中的代码),这样只要改变对应的img的src内容,浏览器就会刷出对应的图片。

4、Linux上,路径不要用~(当前用户目录标识),实际测试的时候发现不能用。用绝对路径吧。

5、文章中的PathUtil中的路径,在SpringBoot中可以使用

@Value("${web.upload-path}")
private String path;

来自动绑定,测试工程因为涉及很多地方要改,这里不再赘述。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


背景

一个Intellij,mavn工程。

1、使用了local的jar作为lib

2、使用main()方法作为入口方法

3、使用ftl文件作为模板(FreeMarker)

在使用Intellij开发的时候,没有任何问题。现在要发布到外部机器,希望用户能通过web默认端口80访问。

需要的步骤

1、通过Intellij的maven工具,package命令打包成war文件或者是war同等的文件夹

可能会遇到的问题:

因为使用local的jar,而不是maven reposity的,而local jar是通过File|Project Structure… |Library添加,所以package命令不能成功,遇上这个jar找不到问题。

解决方案:

在项目的pom.xml中,加入使用<systemPath>的<dependency>项,告知maven工具local的jar路径

注意:

如果项目非有springboot initializer生成,那么还需要在pom.xml中加入

<!--  手工设置:让打包成war的设置  -->
<packaging>war</packaging>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

一个完整的的pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>wlxsphb</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--  手工设置:让打包成war的设置  -->
    <packaging>war</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver</artifactId>
            <version>3.11.2</version>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20190722</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.1-jre</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.sun.mail/javax.mail -->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>it.sauronsoftware</groupId>
            <artifactId>base64</artifactId>
            <version>1.3.1</version>
            <scope>system</scope>
            <systemPath>C:/Users/HanIT/.m2/repository/it/sauronsoftware/base64/javabase64-1.3.1.jar</systemPath>
        </dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2、在main()中,有初始化的代码,会在springboot模块前后初始化自己的类

可能会遇到的问题:

发布到外部tomcat,这个main就不会被主动运行。tomcat会由spring boot去寻找SpringBootServletInitializer继承类的configure()方法运行(代替这个main)。所以,所有的初始化没有机会,就导致后续的各种错误,而导致服务无法正常,最常见的就是各种NullException的发生。

解决方案:

1、在Override的configure()中,拷贝对应的初始化方法。视confiure()为入口方法。

有了上面两个大前提,把一个这样的web application放置到外部tomcat的步骤就是:

1、删空tomcat webapps下的ROOT文件夹下所有默认文件(这个方式让web application的根目录替代默认的网站根目录)

2、使用maven的package,打包出target下的war,同时会有同名目录放在war旁

3、拷贝这个同名目录到ROOT下,spring boot工程会出两个文件夹,一个为WEB-INF,另外一个为META-INF。WEB-INF中,含两个目录:classes和lib。classes包含的是Intellij项目中java(src目录)下的java代码产生的class(按原目录组织)文件和resources下所有文件(文件夹);lib下是项目中用到的maven引用的其他jar。META-INF文件夹可能是空的。

查错

发布到外部的tomcat,启动服务后,看不到自己的网站页面,或者看页面时见到错误。查错的方式是看tomcat的日志:在tomcat安装目录下,有logs文件夹,启动服务后会出现很多的log文件,主要是stderr字眼的文件和stdout字眼的文件。严重的错误,比如spring boot前就发生的错误,比如configure()方法就发生了导致springboot不能继续的错误,在stderr中能看到。而后续的各种mapping错误,信息等,都可以在stdout中看到。

常见的System.out.println()发出的信息,在这些文件里都会存在。

修改默认的8080端口

打开tomcat安装目录下的conf文件夹下的server.xml,查找8080字眼,应该能看到未注释的如下字眼:

<Connector port="8080" protocol="HTTP/1.1"            connectionTimeout="20000"            redirectPort="8443" />

改动其中的8080到80,保存。然后重启tomcat即可。


化石原创文章,转载请注明来源并保留原文链接



化石原创文章,转载请注明来源并保留原文链接


1、项目的application.properties中,写入

server.error.whitelabel.enabled: false

2、项目resources目录下,建立public目录,下方建立error目录,然后放置

404.html文件,测试内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Something went wrong! </h1>
    <h2>Our Engineers are on it</h2>
    <a href="/">Go Home</a>
</body>
</html>

这样准备工作就完毕了。

启动Tomcat,使用浏览器访问没有准备的url连接,如果没有错误的话,就会看到上面的404页面的内容。

SpringBoot版本:2.2.1


化石原创文章,转载请注明来源并保留原文链接