简体   繁体   English

使用spring RestController对错误请求的自定义响应

[英]Custom response on bad request using spring RestController

I have the following controller. 我有以下控制器。 I am using Spring to create Restful APIs. 我正在使用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}

}

The UserInput class looks like this: UserInput类如下所示:

public class UserInput{

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

//getters and setters

Now when I try and access /user/ with data {"sex":"Male"} , I get the following response: 现在,当我尝试使用数据{"sex":"Male"}访问/user/时,得到以下响应:

错误的请求响应

I want the response in case of such a request to be: 我希望这样的请求的响应是:

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

Is there any way of customising BAD REQUEST responses in Spring? 有什么方法可以在Spring中自定义BAD REQUEST响应?

Considering the current scenario the most ideal solution would be to alter the behavior of HandlerMethodArgumentResolve as the json to pojo constructed by @RequestBody fails because we dont get a chance to check the wrong data and this check can very well be done in the custom message converter 考虑到当前情况,最理想的解决方案是更改HandlerMethodArgumentResolve的行为,因为@RequestBody构造的json转换为pojo失败,因为我们没有机会检查错误的数据,并且此检查可以很好地在自定义消息转换器中完成

A. first we would need to create LanguageMessageConverter as follows 答:首先,我们需要创建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. we need to set the media type new MediaType("application", "json", Charset.forName("UTF-8")) which will make sure this class intervenes the mentioned MIME type 我们需要设置媒体类型new MediaType(“ application”,“ json”,Charset.forName(“ UTF-8”)),以确保此类干预所提到的MIME类型
  2. Considering we need to manipulate the result I found it best to convert it to map langmp (There are better JSON Parsers which can be used) 考虑到我们需要处理结果,我发现最好将其转换为映射langmp (可以使用更好的JSON解析器)
  3. Since we need to to understand the existing type I used reflection api to get the fields via getDeclaredFields() 由于我们需要了解现有的类型,因此我使用了反射api通过getDeclaredFields()获取字段。
  4. Using the above made the logical check using the datatype to understand if the type is incorrect for eg if the field datatype is int and if it is found as String then corresponding map value will be substituted 使用上面的方法,使用数据类型进行了逻辑检查,以了解该类型是否不正确,例如,如果字段数据类型为int且如果发现它为String,则将替换对应的映射值
  5. once that is done the map will hold the updated values where in if the data was wrong a default value would be set eg if the int var is set to 0 since the originating json had a String in it. 一旦完成,映射将保存更新的值,如果数据错误,则将设置默认值,例如,如果int var设置为0,因为原始json中包含String。
  6. Once that is done the updated map is converted to the concerned class. 完成此操作后,更新的地图将转换为相关的类。

B. Secondly we need to register the custom MessageConverter in the dispatcher xml ie LanguageMessageConverter 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" is very important since we are adding Custom MessageConverter but we also need the other existing converters working along with the one we have added register-defaults="true"非常重要,因为我们要添加Custom MessageConverter,但是我们还需要其他现有的转换器与我们添加的转换器一起工作
  2. LanguageMessageConverter needs to be registered here. LanguageMes​​sageConverter需要在这里注册。

C. Considering the concerned pojo is populated with the necessary details it would reach our controller post processing in the custom converter now we would add the manual validation eg. C.考虑到相关的pojo填充有必要的详细信息,它将在自定义转换器中到达我们的控制器后处理,现在我们将添加手动验证,例如。 if the int variable has 0 the necessary error json should be returned 如果int变量为0,则应返回必要的错误json

As per your request even if the json consists of the wrong data the custom message converter should process it and accordingly in the controller we can validate the condition mentioned. 根据您的请求,即使json包含错误的数据,自定义消息转换器也应对其进行处理,因此在控制器中,我们可以验证上述条件。 The code definitely can be improved further. 该代码肯定可以进一步改进。 Kindly let me know if this solution fulfilled your requirement or any part of the code requires further elaboration and hopefully addressed your concern. 请让我知道此解决方案是否满足您的要求或代码的任何部分需要进一步阐述,并希望能解决您的问题。

I had the same issue, than I solved that way: 我有同样的问题,而不是我这样解决的:

  1. Create an Object called Error, like that (don't forget to implement Serializable...): 创建一个名为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. Inside the @RestController method you ave to call inputValidator.validate() method (if you didn't create an Object Validator for your UserInput then we're really don't speaking the same language...) @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. Finally you'll have to translate the JSON object to your client side. 最后,您必须将JSON对象转换为客户端。 You'll have two kind of objects: 您将拥有两种对象:

    3.1. 3.1。 null ( undefined depending on the language you're using) null(根据您使用的语言undefined

    3.2. 3.2。 A JSON object like that: 像这样的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!" } ] 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM