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


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,什么数据验证、取数据栏位等工作就方便多了,可以少些很多代码,提升开发效率。


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



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


glb是web3d中使用频率较高的一种3d格式。Unity原生到2019还没有支持。幸运的是,KhronosGroup官方在github上发布了对应的包,能加载glb、gltf格式。

尝试了一下,2019的版本应该还有问题。2018.4.xx后的版本可以正常处理。我测试的版本是2018.4.21f1。

克隆github的源代码后,先打开GLTFSerialization目录下的GLTFSerialization.sln,利用Visual Studio 2019编译默认的激活项目即可。该项目编译出一系列的dll到工程自带的Unity Demo工程下(UnityGLTF)。

然后就可以使用Unity打开工程目录下的UnityGLTF Unity项目。

工程下有很多的demo,在Samples下,每个场景最重要的就是名字为GLTF的GameObject,上挂一个GLTFComponent脚本,变量GLTF Uri是目标glb或者gltf的路径。

我克隆的项目,对应的目录下是空的。所以知道这个加载路径后,无非就是把glb文件放置到一定的路径下,然后在这里改动对应的变量值即可。


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



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


submodule可以用来链接子模块,通常是第三方在github上的源代码工程。在github上看源代码工程是,一般看到一些文件夹后面有@带上一串字符,就是这玩意。

使用普通的git clone url命令,这些链接的submodule不会被马上克隆到本地。如果克隆主工程后,发现没有submodule的代码,那么可以在该工程中使用

git submodule init
git submodule update

组合命令来拉取submodule的原代码。

当然,这样一个命令也能完成同样的任务:

git submodule update --init

在主工程中,submodule的url是存在.gitsubmodule文件中的,这个文件默认隐藏。里面是每个submodule的url,可以是https协议的,也可以是git协议的。

主工程的.gitsubmodule文件的协议可以修改,如果改了。需要用

git submodule sync

使你的修改跟./git/config中的相应地方同步。否则不会生效。

当然,git clone本身也提供一步到位的做法:

git版本1.6.5及以上:

git clone --recursive URL

git版本2.13以上:

git clone --recurse-submodules -j8 git://github.com/foo/example.git

j8表示同时并行8个任务拉取,可以不用。

如果因各种原因,git clone submodule中断的话,那么后续可以使用下面的命令继续。

git submodule update --recursive

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



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


突然发现w3school上的这段代码非常不错,因为其他地方的代码多多少少都有点问题,比如drag的时候屏幕闪烁、抓取的位置不对等等。而这里的代码,质量非常高,即取即用。

javascript代码:

dragElement(document.getElementById("element_to_drag"));

function dragElement(elmnt) {
	var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
	elmnt.onmousedown = dragMouseDown;

	function dragMouseDown(e) {
		e = e || window.event;
		e.preventDefault();
		// get the mouse cursor position at startup:
		pos3 = e.clientX;
		pos4 = e.clientY;
		document.onmouseup = closeDragElement;
		// call a function whenever the cursor moves:
		document.onmousemove = elementDrag;
	}

	function elementDrag(e) {
		e = e || window.event;
		e.preventDefault();
		// calculate the new cursor position:
		pos1 = pos3 - e.clientX;
		pos2 = pos4 - e.clientY;
		pos3 = e.clientX;
		pos4 = e.clientY;
		// set the element's new position:
		elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
		elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
	}

	function closeDragElement() {
		// stop moving when mouse button is released:
		document.onmouseup = null;
		document.onmousemove = null;
	}
}

使用的时候,只要把第一行代码里的element id改成需要抓取的element id即可。


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