Spring Boot Web:自定义Method Not Allowed响应


#Spring Boot


示例

项目结构

.
├── build.gradle
└── src
    └── main
        └── java
            └── hello
                ├── Application.java
                ├── BaseResponse.java
                ├── CustomExceptionHandler.java
                ├── Greeting.java
                └── GreetingController.java

代码中使用了 Lombok 库,关于这个库的具体使用,可以参考:Java Lombok 库:为你减少样板代码

build.gradle

buildscript {
    repositories {
        maven { url 'http://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.3.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'


sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile group: 'org.projectlombok', name: 'lombok', version: '1.18.0'
}

BaseResponse 类

package hello;

import lombok.Data;

import java.io.Serializable;

@Data
public class BaseResponse implements Serializable {

    private Boolean success;
    private String msg;
    private Object result;

    public static BaseResponse success(String msg, Object result) {
        return new BaseResponse(true, msg, result);
    }

    public static BaseResponse fail(String msg, Object result) {
        return new BaseResponse(false, msg, result);
    }

    private BaseResponse(Boolean isSuccess, String msg, Object result) {
        this.success = isSuccess;
        this.msg = msg;
        this.result = result;
    }

}

Greeting 类

package hello;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Greeting {

    private long id;
    private String content;

}

Application 类:程序主入口

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

GreetingController 类

package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();


    /**
     * 只允许 HTTP POST 方法
     */
    @RequestMapping(value = "/greeting1", method = RequestMethod.POST)
    public Greeting greeting1(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                String.format(template, name));
    }

}

运行示例

上面我们少介绍了 CustomExceptionHandler 类,我们把该类删掉,运行 Application。

如果我们用 GET 方法请求 http://127.0.0.1:8080/greeting1,响应状态是405,响应体是:

{
    "timestamp": "2018-07-22T00:46:07.791+0000",
    "status": 405,
    "error": "Method Not Allowed",
    "message": "Request method 'GET' not supported",
    "path": "/greeting1"
}

如何自定义响应体?

增加CustomExceptionHandler,内容如下:

@ControllerAdvice // 注意,这个必须有,用于声明是对 controller 控制器的增强。
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
            HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
        if (!CollectionUtils.isEmpty(supportedMethods)) {
            headers.setAllow(supportedMethods);
        }
        return handleExceptionInternal(ex, null, headers, status, request);

    }

}

继承抽象类 ResponseEntityExceptionHandler 后,ResponseEntityExceptionHandler 内部的下面这个方法会用来处理异常:

@ExceptionHandler({
        HttpRequestMethodNotSupportedException.class,
        HttpMediaTypeNotSupportedException.class,
        HttpMediaTypeNotAcceptableException.class,
        MissingPathVariableException.class,
        MissingServletRequestParameterException.class,
        ServletRequestBindingException.class,
        ConversionNotSupportedException.class,
        TypeMismatchException.class,
        HttpMessageNotReadableException.class,
        HttpMessageNotWritableException.class,
        MethodArgumentNotValidException.class,
        MissingServletRequestPartException.class,
        BindException.class,
        NoHandlerFoundException.class,
        AsyncRequestTimeoutException.class
    })
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
    HttpHeaders headers = new HttpHeaders();

    if (ex instanceof HttpRequestMethodNotSupportedException) {
        HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
        return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
    }
    
    // 省略
    
}

其中,handleHttpRequestMethodNotSupported 用来处理请求方法不支持的异常。

运行 Application 类,用 GET 方法请求 http://127.0.0.1:8080/greeting1,响应状态是405,而响应体已经变空。为什么?因为return handleExceptionInternal(ex, null, headers, status, request);,第2个参数是null。

当然,我们也可以自定义第2个参数。我们将 handleHttpRequestMethodNotSupported 实现为:

    @Override
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
            HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
        if (!CollectionUtils.isEmpty(supportedMethods)) {
            headers.setAllow(supportedMethods);
        }
        return handleExceptionInternal(ex, BaseResponse.fail("请求方法不支持", null), headers, status, request);
    }

重新运行 Application 类,用 GET 方法请求 http://127.0.0.1:8080/greeting1,响应状态是405,而响应体变成:

{
    "msg": "请求方法不支持",
    "result": null,
    "success": false
}

还能学到什么?本文中出现了 @ExceptionHandler , 该注解在 Spring Boot Web:使用 @ExceptionHandler 处理异常会介绍。



( 本文完 )