【问题标题】:Manage REST response with Spring MVC使用 Spring MVC 管理 REST 响应
【发布时间】:2015-12-15 06:27:44
【问题描述】:

我正在使用 Spring 创建一个 REST api。目前我使用这种结构来提供文件浏览器服务:

文件模型.java

package hello;


    public class FileModel {
        private String name;
        private Long lastUpdate;
        private Long size;

        /**
         * Void Constructor
         */
        public FileModel() {
        }


        /**
         * Parametrized constructor
         * @param name
         * @param created
         * @param lastUpdate
         * @param size
         */
        public FileModel(String name, Long lastUpdate, Long size) {
            super();
            this.name = name;
            this.lastUpdate = lastUpdate;
            this.size = size;
        }


        /**
         * @return the name
         */
        public String getName() {
            return name;
        }


        /**
         * @param name the name to set
         */
        public void setName(String name) {
            this.name = name;
        }


        /**
         * @return the lastUpdate:A long value representing the time the file was last modified, 
         * measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970)
         */
        public Long getLastUpdate() {
            return lastUpdate;
        }


        /**
         * @param lastUpdate the lastUpdate to set
         */
        public void setLastUpdate(Long lastUpdate) {
            this.lastUpdate = lastUpdate;
        }


        /**
         * @return the size in bytes
         */
        public Long getSize() {
            return size;
        }


        /**
         * @param size the size to set
         */
        public void setSize(Long size) {
            this.size = size;
        }


    }

文件服务.java

    package hello;

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.nio.file.Files;
    import java.nio.file.LinkOption;
    import java.util.ArrayList;

    import org.springframework.stereotype.Service;

    @Service
    public class FileServices {

        public ArrayList<FileModel> getAllFiles(String path) throws FileNotFoundException {
            ArrayList<FileModel> files=new ArrayList<FileModel>();
            File directory = new File(path);
            if (directory.exists()){
                //get all the files from a directory
                File[] fList = directory.listFiles();
                //check if list is null
                for (File file : fList){
                    if (file.isFile()){
                        FileModel f=new FileModel(file.getName(),file.lastModified(),file.length());
                        files.add(f);
                    }
                }
                return files;
            }
            else throw new ResourceNotFoundException(path);
        }
    }

ResourceNotFoundException.java

    package hello;

    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ResponseStatus;

    @ResponseStatus(HttpStatus.NOT_FOUND)
    public class ResourceNotFoundException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        public ResourceNotFoundException(String path){
            super("The specified path: "+ path +"  doesn't exist");
        }
    }

文件管理器

    package hello;

    import java.io.FileNotFoundException;
    import java.util.Collection;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    public class FileManager {

        @Autowired
        private FileServices file;

        @RequestMapping(value = "/files", method = RequestMethod.GET)
        public Collection<FileModel> getAllFiles(@RequestParam(value="path", defaultValue="/home") String path) throws FileNotFoundException {
            return file.getAllFiles(path);
        }

    }

响应是这样的错误消息

> {   "timestamp": 1442560477794,   "status": 404,   "error": "Not
> Found",   "exception": "hello.ResourceNotFoundException",   "message":
> "The specified path: /home  doesn't exist",   "path":
> "/MatlabLib/files" }

或者这个。

[
  {
    "name": "apache-tomcat-8.0.26-windows-x64.zip",
    "lastUpdate": 1441282759343,
    "size": 10470002
  },
  {
    "name": "desktop.ini",
    "lastUpdate": 1441357976196,
    "size": 282
  }
]

鉴于我必须通过其他 java 或 matlab 代码管理此 Web 服务,我需要自定义响应,例如状态、错误、异常、消息、正文,以便我可以检查状态代码并了解是否有错误或更少。 Spring中是否有一种构建方法可以做到这一点?

谢谢

更新:我创建了两个响应类,一个用于 ErrorResponse,另一个用于具有不同字段数的响应。然后我用了

@ControllerAdvice 
public class ErrorController {

    /**
     * 
     * @param e: exception thrown
     * @return ErroreResponse
     */
    @ExceptionHandler(Exception.class)
    public @ResponseBody ErrorResponse errorHandler(Exception e){   
        //Make the exception by buildErrorResponse
        return ErrorResponseBuilder.buildErrorResponse(e);
    }

在ErrorResponseBuilder中做了这个方法:

/**
 * Build exception response beginning from exception
 * @param e exception thrown
 * @return ErrorResponse: response of an exception
 */
public static ErrorResponse buildErrorResponse(Exception e){
    StringWriter errors = new StringWriter();
    e.printStackTrace(new PrintWriter(errors));
     return new ErrorResponse(HttpStatusManager.getHttpCode(e),e.getClass().getName(),e.getMessage(),errors.toString());
}

在 HttpStatusManager 中我实现了这个:

public HttpStatusManager() {
    }

    /**
     * Add to this class all new exception associating a code error
     * @param exception
     * @return
     */
    public static int getHttpCode(Exception exception){
        if (exception instanceof ResourceNotFoundException);
            return HttpStatus.NOT_FOUND.value();
        }

所以从控制器使用这条简单的线

@RequestMapping(value = "/files", method = RequestMethod.GET)
    public Response<Collection<FileModel>> getAllFiles(@RequestParam(value="path", defaultValue="/home") String path) throws ResourceNotFoundException {
        Collection<FileModel> result;
        result = file.getAllFiles(path);
        return new Response<Collection<FileModel>>(HttpStatus.OK.value(),result);
    }

你怎么看?

【问题讨论】:

  • 这不是 Spring 的东西,但我想它可能适合您的需求:javax.ws.rs.core.Response。
  • 那已经存在了,它被称为HTTP。在第一种情况下,您的 http 代码在后 200 中将是 404。基本上 400 或 500 范围内的内容表示错误...

标签: java spring rest spring-mvc exception


【解决方案1】:

你可以使用javax.ws.rs.core.Response,它有很好的 API。但就个人而言,我宁愿创建一个自定义类来处理此类响应。

当然,您需要附加到您的项目 f.e. Jackson JSON API 这样您就可以构建返回对象的方法。另外你必须在你的spring配置文件中配置messageConverters


更新

public class Response {

private Object responseBody;
private String message;
private int responseCode;
public Response() {
    responseCode = 200; //default HTTP 200 OK
}
public Object getResponseBody() {
    return responseBody;
}

public void setResponseBody(Object responseBody) {
    this.responseBody = responseBody;
}

public String getMessage() {
    return message;
}

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

public int getResponseCode() {
    return responseCode;
}

public void setResponseCode(int responseCode) {
    this.responseCode = responseCode;
}


}

使用示例

  @RequestMapping(value = "/files", method = RequestMethod.GET)
    public Response getAllFiles(@RequestParam(value="path", defaultValue="/home") String path) {
        Response response = new Response();
        try {
            Collection<FileModel> files = file.getAllFiles(path);
            response.setResponseBody(files);
        } catch (FileNotFoundException e) {
            Utils.setErrMessage(response, e);
        }

        return response;
    }

您的setErrMessage 函数可能如下所示

public void setErrMessage(Response response, Exception e) {
    if(e instanceof NullPointerException) {
        response.setErrCode(400); //HTTP 400 Bad Request
    }
    else if(e instanceof FileNotFoundException || ...) {
        response.setErrCode(500); //HTTP 500 Interval Server Error
    }
    ...
    response.setMessage(e.getMessage);
}

这只是一个大概的想法,你可以随意改变它。

【讨论】:

  • 我的想法是使用一个新类,例如 ResponeBulder,它具有以下字段:状态、错误、异常、消息、正文。然后,如果我有错误,则使用一个构造函数在正文中使用 null,如果我有 200 个状态代码,则在其他字段中使用 null。是个好主意吗?如何从我的异常中检索状态码、错误和异常?
  • 这是一个非常好的主意(也推荐使用Builder模式)。如果我正确理解您的问题,如果您正在调用某个方法并且在处理异常时会发生,为什么不使用仅使用 try-catch 块?您可以轻松地从 catch 块中的 Exception 对象中提取错误消息、异常类等,然后将其设置为您的 ResponeBuilder 对象。
  • 关于错误代码,您可以自己分配。您可以创建一个类似 int getExceptionCode(Exception e) 的方法,并根据异常类型返回正确的代码。您可能需要区分十几种不同的代码。
  • 我得试试。正文是一个字符串变量?使用 Builder 模式而不是一个类有两个方法()一个有 http 和 body 另一个有 http 和异常)有用吗?我一完成就更新你
  • Builder 模式将是一个不错的选择,因为它很优雅,并且在您的情况下您可以设置可变数量的字段(如果没有发生异常,您不会设置所有错误字段,您不需要有异常时设置正文)。对于一种类型的body元素,它真的取决于你的需求。如果您的服务只返回一件事(XML、单个值、..),字符串可能就足够了。但如果您有更复杂的响应,您也可以为 body 创建一个自定义类。
【解决方案2】:

我有一个通用的响应 DTO,我使用 Spring @ControllerAdvice@ExceptionHandler 在我的休息服务中实现全局错误处理机制。

全局错误处理代码:

@ControllerAdvice    
public class WSControllerAdvice {
        private static Logger logger = Logger.getLogger(SearchControllerAdvice.class);
        @ExceptionHandler(Exception.class)
        public @ResponseBody WSResponse<String> errorHandler(Exception e){
            WSResponse<String> response = new WSResponse<String>();
            logger.error("Exception occured "+e.getMessage());
            response.setResponseStatus(WSResponseCode.UNHANDLED_EXCEPTION);
            response.setCount(0);
            response.getErrors().add("Unhandled Search Error : " + e.getMessage());
            return response;
        }
    }

通用响应 DTO

public class WSResponse<T> {

    private String responseStatus;
    private long count;
    private long totalNumFound;
    private T results ;
    private List<String> errors = new ArrayList<>();
    private String nextPageMark;
    private List<Facet> facets = new ArrayList<>();
}

Getter Setter 被移除

无论是否发生异常,您都有一个一致的响应格式和这个结构。

【讨论】:

  • 如何使用它?所以进入web服务器,无一例外都要设置null值?
  • 如果您成功处理了请求,您的errors 数组将为空。
【解决方案3】:

有两种方法可以实现:

【讨论】:

    猜你喜欢
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 2016-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    相关资源
    最近更新 更多