使用spring RestController在错误请求上自定义响应

时间:2015-06-29 17:41:55

标签: java spring rest spring-mvc

我有以下控制器。我正在使用Spring创建Restful API。

@RestController
public class UserController extends RestControlValidator {

@RequestMapping(value = "/user/", method = RequestMethod.POST, headers = "Accept=application/json", consumes = "application/json", produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody List newUser(@RequestBody @Valid UserInput input,BindingResult result) 
            {Some code}

}

UserInput类看起来像这样:

public class UserInput{

    @NotEmpty
    private String emailId;
    @NotEmpty
    private String fName;
    private String lName;
    private int sex;

//getters and setters

现在,当我尝试使用数据/user/访问{"sex":"Male"}时,我收到以下回复:

Bad request response

如果有这样的请求,我想要回复:

{"errors":{"sex":"The value must be an integer"}}

有没有办法在Spring中自定义BAD REQUEST响应?

2 个答案:

答案 0 :(得分:0)

考虑到当前场景,最理想的解决方案是改变HandlerMethodArgumentResolve的行为,因为@RequestBody构造的json到pojo失败,因为我们没有机会检查错误的数据,这个检查很可能在自定义消息转换器

一个。首先,我们需要创建LanguageMes​​sageConverter,如下所示

public class LanguageMessageConverter extends
        AbstractHttpMessageConverter<Language> {
    private Gson gson = new Gson();
public LanguageMessageConverter() {
    super(new MediaType("application", "json", Charset.forName("UTF-8")));
}

@Override
protected boolean supports(Class<?> clazz) {
    return Language.class.equals(clazz);
}

Map<String, String> mp = new HashMap<>();

@Override
protected Language readInternal(Class<? extends Language> clazz,
        HttpInputMessage httpInputMessage) throws IOException,
        HttpMessageNotReadableException {
    Map langmp = gson.fromJson(
            convertStreamToString(httpInputMessage.getBody()), Map.class);

        for (Field field : clazz.getDeclaredFields()) {

            if (!langmp.get(field.getName()).getClass().getCanonicalName().equals(field.getType().getCanonicalName())) {
                if (field.getType().getCanonicalName().equals("java.lang.Integer")||field.getType().getCanonicalName().toString().equals("int")) {
                    langmp.put(field.getName(), "0");
                } else if (field.getType().equals("java.lang.String")) {
//TODO COde needs to be improved here because this check is not efficient
                    langmp.put(field.getName(), "wrong");
                }
            }
        }       
        Language lang = gson.fromJson(gson.toJson(langmp), clazz);
        return lang;
    }
  1. 我们需要设置媒体类型new MediaType(“application”,“json”,Charset.forName(“UTF-8”)),这将确保此类介入提到的MIME类型
  2. 考虑到我们需要操作结果,我发现最好将其转换为map langmp(可以使用更好的JSON解析器)
  3. 由于我们需要了解现有的类型,我使用了反射api来通过getDeclaredFields()获取字段
  4. 使用上面的方法使用数据类型进行逻辑检查,以了解类型是否不正确,例如,如果字段数据类型是int,如果找到它,那么相应的映射值将被替换
  5. 一旦完成,地图将保存更新的值,如果数据错误,将设置默认值,例如,如果int var设置为0,因为原始json中有一个String。
  6. 完成后,更新后的地图将转换为相关课程。
  7. B中。其次,我们需要在调度程序xml中注册自定义MessageConverter,即LanguageMessageConverter

    <mvc:annotation-driven >
       <mvc:message-converters register-defaults="true">       
           <bean class="com.comp.org.controller.LanguageMessageConverter" />
        </mvc:message-converters>
    </mvc:annotation-driven>
    
    1. register-defaults="true"非常重要,因为我们正在添加自定义MessageConverter,但我们还需要其他现有转换器与我们添加的转换器一起工作
    2. 需要在此处注册LanguageMes​​sageConverter。
    3. ℃。考虑到有关pojo填充了必要的细节,它将到达我们的控制器后处理自定义转换器现在我们将添加手动验证,例如。如果int变量为0,则应返回必要的错误json

      根据您的要求,即使json包含错误的数据,自定义消息转换器也应该处理它,因此在控制器中我们可以验证所提到的条件。 代码肯定可以进一步改进。请告知我这个解决方案是否符合您的要求,或者代码的任何部分需要进一步详细说明,并希望能够解决您的问题。

答案 1 :(得分:0)

我遇到了同样的问题,而不是我解决的问题:

  1. 创建一个名为Error的对象,就像那样(不要忘记实现Serializable ......):

    private String fieldName;
    private String errorCode;
    private String defaultMessage;
    
    public Error() {
    }
    
    public Error(String fieldName, String errorCode, String defaultMessage) {
        this.fieldName = fieldName;
        this.errorCode = errorCode;
        this.defaultMessage = defaultMessage;
    }
    
    /* getters, setters */
    
  2. @RestController方法中,您需要调用inputValidator.validate()方法(如果您没有为UserInput创建对象验证器,那么我们确实不要说同一种语言......)

    // validating the userInput
    userInputValidator.validate(userInput, bindingResult);
    
    if (bindingResult.hasErrors()) {
        List<Error> errors = new ArrayList<>(bindingResult.getErrorCount());
        for (FieldError fieldWithError : bindingResult.getFieldErrors()) {
            errors.add(new Error(fieldWithError.getField(), fieldWithError.getCode(), fieldWithError.getDefaultMessage()));
        }
        return errors;
    }
    
    // in case of success:
    return null;
    
  3. 最后,您必须将JSON对象转换为客户端。您将拥有两种对象:

    3.1。 null(undefined取决于您正在使用的语言)

    3.2。像这样的JSON对象:

    [
      {
        "fieldName": "name",
        "errorCode": "user.input.name.in.blank",
        "defaultMessage": "Insert a valid name!"
      },
      {
        "fieldName": "firstPhone",
        "errorCode": "user.input.first.phone.blank",
        "defaultMessage": "Insert a valid first phone!"
      }
    ]