如何从Controller返回“较轻”版本的数据?

时间:2017-02-06 20:05:28

标签: java spring rest

我正在使用Spring 4.3,我有一个REST控制器,它将一个User对象返回给UI(javascript)。

问题是我从数据库(比如Hibernate)获得了一个包含密码的User对象。我不想通过实际返回来公开密码。相反,我希望控制器方法在返回它之前将NULL放入其中(我可以使用Optional或其他解决方案来避免空值,但我在这个问题中保持简单。)

public class User {
    private String username;
    private String password;
    //setters and getters
}

@Controller
public class MainController {
    @RequestMapping(value = "/user/getOne", method = RequestMethod.GET)
    public User getOneUser() {
        User user = //getUser
        //something to nullify the password?
        return user;
    }

这个问题涉及用户和密码以便清楚,但我正在寻找一个广泛的解决方案来处理我的所有数据模型以及我不希望它们包含在某些回报中的值。

我不喜欢的解决方案:)

不喜欢的解决方案#1:在私有方法或实用程序类的方法或适配器类中删除密码

我不喜欢这样,因为它使代码很长。大多数控制器方法都需要自己调整数据。

我更喜欢更干净,更短的东西。

不喜欢的解决方案#2:使用@JsonIgnore注释

我不想将我的数据模型与Jackson包绑定。

不喜欢的解决方案#3:使用较小的数据模型类,并盲目复制较小的可包含的所有内容

此解决方案引用如下代码:

public class ReturnUser {
   private String username;
}

@Controller
public class MainController {
    @RequestMapping(value = "/user/getOne", method = RequestMethod.GET)
    public User getOneUser() {
        User user = //getUser

        ReturnUser smaller = copyWhatsInCommon(user, User.class, ReturnUser.class); //sees that there's only username common to both, so copies only it
        return smaller;

这也增加了代码的数量,所以我不喜欢它。

任何想法?

4 个答案:

答案 0 :(得分:1)

选项1 : 您可以在控制器和外观(或从数据库中填充实体的服务)之间添加转换层。转换层类可以将entities转换为value objects。 VO将仅包含您view所需的最少信息。如果需要转换为值对象的实体数多于1,则还可以使用reflections读取需要从实体读取并复制到VO的属性(来自配置文件或其他内容) 。但是,这与您不喜欢的问题中的解决方案3没有太大区别。虽然从性能和安全角度来看,它确实以转换层的形式添加了额外的代码。

选项2 :我建议的另一个直接选项是从“用户”类中读取所需的属性,并将其填充为model attributes

@RequestMapping(value = "/user/getOne", method = RequestMethod.GET)
    public User getOneUser(ModelMap modelMap) {
    User user = //getUser
    modelMap.addAttribute("userName", user.getName());
    modelMap.addAttribute("userEmail", user.getEmail());
    ...
    ...
}

}

答案 1 :(得分:0)

为什么您希望绝对返回实体中代表的用户?

服务和控制器层甚至不应该获得包含密码字段的User对象。所以应该避免你的1和3解决方案。

在您的情况下,返回User课程的视图似乎是实现您需求的最相关方式。只需使用DTO

您可以从访问数据访问层的服务层返回用户DTO。

或者,如果您没有服务层,则可以在数据访问层中提供一个方法,该方法返回没有密码字段的用户DTO。

答案 2 :(得分:0)

根据经验:

1。)您不应该从View层返回业务对象,即Controller。你在许多教程中看到了这一点,但这是糟糕的设计。

2.。)您应该创建一个响应对象。此响应对象仅包含您要返回给用户的字段。

3.。)您应该使用用户对象在构造函数中实例化UserResponse的字段。

在创建resposne对象时使用,使用@JsonIgnore注释没有意义。

虽然这可能是更多的代码,但它是一个更好的设计,明确分离责任。控制器只需要担心视图对象,业务层永远不需要了解视图。

实施例

public class UserResponse {
    private String firstName;
    private String lastName;

    public UserResponse(User user){
        this.firstName = user.getFirstName();
        this.lastName = user.getLastName();
    }

    ...
    //The getters
}

在控制器中:

return new UserResponse(user);

答案 3 :(得分:0)

我打算再提供一个解决方案。只是为了报道。这非常难看,不推荐。您可以创建对象映射器并过滤对象:

static ObjectMapper mapper = new ObjectMapper();

public static String filterOutAllExcept(Object obj, String filterName, String... properties) throws YpException {

    mapper.registerModule(new Hibernate4Module());
    SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept(properties);
    FilterProvider filterProvider = new SimpleFilterProvider().addFilter(filterName, filter).setFailOnUnknownId(false);
    String strValue;
    try {
        strValue = mapper.writer(filterProvider).writeValueAsString(obj);
    } catch (JsonProcessingException e) {
        // handle exception

    }

    return strValue;
}

然后你可以这样称呼它:

String filterApplied = ObjectMapperHelper.filterOutAllExcept(user, JsonDTOFilter.SOMEFILTER, "firstName", "lastName");

这将为您提供一个包含字段firstName和lastName

的json字符串