本文由 ImportNew - 邬柏 翻译自 javacodegeeks。如需转载本文,请先参见文章末尾处的转载要求。

ImportNew注:如果你也对Java技术翻译分享感兴趣,欢迎加入我们的 Java开发 小组。参与方式请查看小组简介。

这是关于使用Spring MVC创建Web API的另一个教程。这并不是一个非常精细的教程,而仅仅是一个演习(攻略)。本教程旨在创建一个提供服务器端API的应用,并且使用Mongo作为它的数据库,使用Spring Security作为安全框架。

准备开始——POM

由于我是一个maven脑残粉,所以这个项目还是基于maven的。现在Spring 4.0 RC2已经发布了,所以我决定使用最新的依赖管理工具。本文使用的pom.xml如下:使用这个配置创建Spring MVC应用确实非常简单。这里面比较新鲜的东西就是dependencyManagement 元素。详解猛戳这儿:http://spring.io/blog/2013/12/03/spring-framework-4-0-rc2-available

配置

这个应用可以使用JavaConfig完成配置。我把它切分为下面几个部分:

ServicesConfig(服务配置)

无需扫描组件,配置真的非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public

class

ServicesConfig {
    @Autowired
    private

AccountRepository accountRepository;
 
    @Bean
    public

UserService userService() {
        return

new

UserService(accountRepository);
    }
 
    @Bean
    public

PasswordEncoder passwordEncoder() {
        return

NoOpPasswordEncoder.getInstance();
    }
}

PersistenceConfig(持久层配置)

我们想要一个配置了所有可用仓库的MONGODB配置。在这个简单的应用中我们只用了一个仓库,所以配置也非常的简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Configuration
class

PersistenceConfig {
    @Bean
    public

AccountRepository accountRepository()
throws

UnknownHostException {
        return

new

MongoAccountRepository(mongoTemplate());
    }
 
    @Bean
    public

MongoDbFactory mongoDbFactory()
throws

UnknownHostException {
        return

new

SimpleMongoDbFactory(
new

Mongo(),
"r");
    }
 
    @Bean
    public

MongoTemplate mongoTemplate()
throws

UnknownHostException {
        MongoTemplate
template =
new

MongoTemplate(mongoDbFactory(), mongoConverter());
        return

template;
    }
 
    @Bean
    public

MongoTypeMapper mongoTypeMapper() {
        return

new

DefaultMongoTypeMapper(
null);
    }
 
    @Bean
    public

MongoMappingContext mongoMappingContext() {
        return

new

MongoMappingContext();
    }
 
    @Bean
    public

MappingMongoConverter mongoConverter()
throws

UnknownHostException {
        MappingMongoConverter
converter =
new

MappingMongoConverter(mongoDbFactory(), mongoMappingContext());
        converter.setTypeMapper(mongoTypeMapper());
        return

converter;
    }
}

SecurityConfig(安全配置)

理论上,Spring Security 3.2完全可以使用JavaConfig。但对于我这也仅仅是一个理论,所以这里还是选择xml配置的方式:

1
2
3
@Configuration
@ImportResource("classpath:spring-security-context.xml")
public

class

SecurityConfig {}

使用这个xml就让API能使用基本的安全机制了。

WebAppInitializer(初始化)

我们不想使用web.xml,所以使用下面的代码配置整个应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Order(2)
public

class

WebAppInitializer
extends

AbstractAnnotationConfigDispatcherServletInitializer {
 
    @Override
    protected

String[] getServletMappings() {
        return

new

String[]{
"/"};
    }
 
    @Override
    protected

Class[] getRootConfigClasses() {
        return

new

Class[] {ServicesConfig.
class,
PersistenceConfig.
class,
SecurityConfig.
class};
    }
 
    @Override
    protected

Class[] getServletConfigClasses() {
        return

new

Class[] {WebMvcConfig.
class};
    }
 
    @Override
    protected

Filter[] getServletFilters() {
        CharacterEncodingFilter
characterEncodingFilter =
new

CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        return

new

Filter[] {characterEncodingFilter};
    }
 
    @Override
    protected

void

customizeRegistration(ServletRegistration.Dynamic registration) {       
        registration.setInitParameter("spring.profiles.active",
"default");
    }
}

WebAppSecurityInitializer (安全配置初始化)

相对于Spring3,可以使用下面这种更加新颖的特性来完成配置:

1
2
@Order(1)
public

class

WebAppSecurityInitializer
extends

AbstractSecurityWebApplicationInitializer {}

WebMvcConfig (Mvc配置)

调度控制器配置。这个也非常简单,仅仅包含了构建一个简单API的最重要配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Configuration
@ComponentScan(basePackages
= {
"pl.codeleak.r"

}, includeFilters = {
@Filter(value
= Controller.
class)})
public

class

WebMvcConfig
extends

WebMvcConfigurationSupport {
 
    private

static

final

String MESSAGE_SOURCE =
"/WEB-INF/i18n/messages";
 
    @Override
    public

RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping
requestMappingHandlerMapping =
super.requestMappingHandlerMapping();
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
        requestMappingHandlerMapping.setUseTrailingSlashMatch(false);
        return

requestMappingHandlerMapping;
    }
 
    @Bean(name
=
"messageSource")
    public

MessageSource messageSource() {
        ReloadableResourceBundleMessageSource
messageSource =
new

ReloadableResourceBundleMessageSource();
        messageSource.setBasename(MESSAGE_SOURCE);
        messageSource.setCacheSeconds(5);
        return

messageSource;
    }
 
    @Override
    public

Validator getValidator() {
        LocalValidatorFactoryBean
validator =
new

LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource());
        return

validator;
    }
 
    @Override
    public

void

configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

这就是需要的配置,非常简单吧!

IndexController (INDEX控制器)

为了验证这个配置是正确的,我创建了一个IndexController。功能非常简单,只是简单地返回“Hello World”,示例代码如下:

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/")
public

class

IndexController {
    @RequestMapping
    @ResponseBody
    public

String index() {
        return

"This is an API endpoint."
;
    }
}

如果运行一下这个应用,就能够在浏览器中看到返回的“Hello World”文本。

构建API

UserService

为了完成Spring安全框架配置,还需要完成另一个部分:实现之前创建的UserService。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public

class

UserService
implements

UserDetailsService {
    private

AccountRepository accountRepository;
 
    public

UserService(AccountRepository accountRepository) {
        this.accountRepository
= accountRepository;
    }
 
    @Override
    public

UserDetails loadUserByUsername(String username)
throws

UsernameNotFoundException {
        Account
account = accountRepository.findByEmail(username);
        if(account
==
null)
{
            throw

new

UsernameNotFoundException(
"user
not found"
);
        }
        return

createUser(account);
    }
 
    public

void

signin(Account account) {
        SecurityContextHolder.getContext().setAuthentication(authenticate(account));
    }
 
    private

Authentication authenticate(Account account) {
        return

new

UsernamePasswordAuthenticationToken(createUser(account),
null,
Collections.singleton(createAuthority(account))); 
    }
 
    private

User createUser(Account account) {
        return

new

User(account.getEmail(), account.getPassword(), Collections.singleton(createAuthority(account)));
    }
 
    private

GrantedAuthority createAuthority(Account account) {
        return

new

SimpleGrantedAuthority(account.getRole());
    }
}

构建一个API节点需要处理三个方法:获取当前登陆用户、获取所有用户(可能不是太安全)、创建一个新账户。那么我们就按照这个步骤来进行吧。

Account

Account 将会是我们的第一个Mongo文档。同样也是非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@SuppressWarnings("serial")
@Document
public

class

Account
implements

java.io.Serializable {
 
    @Id
    private

String objectId;
 
    @Email
    @Indexed(unique
=
true)
    private

String email;
 
    @JsonIgnore
    @NotBlank
    private

String password;
 
    private

String role =
"ROLE_USER";
 
    private

Account() {
 
    }
 
    public

Account(String email, String password, String role) {
        this.email
= email;
        this.password
= password;
        this.role
= role;
    }
 
   //
getters and setters
}

Repository

先创建一个接口:

1
2
3
4
5
6
7
public

interface

AccountRepository {
    Account
save(Account account);
 
    List
findAll();
 
    Account
findByEmail(String email);
}

接下来创建它的Mongo实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public

class

MongoAccountRepository
implements

AccountRepository {
 
    private

MongoTemplate mongoTemplate;
 
    public

MongoAccountRepository(MongoTemplate mongoTemplate) {
        this.mongoTemplate
= mongoTemplate;
    }
 
    @Override
    public

Account save(Account account) {
        mongoTemplate.save(account);
        return

account;
    }
 
    @Override
    public

List findAll() {
        return

mongoTemplate.findAll(Account.
class);
    }
 
    @Override
    public

Account findByEmail(String email) {
        return

mongoTemplate.findOne(Query.query(Criteria.where(
"email").is(email)),
Account.
class);
    }
}

API控制器

功能快要完成了。我们需要将内容提供给用户,所以需要创建自己的节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Controller
@RequestMapping("api/account")
class

AccountController {
 
    private

AccountRepository accountRepository;
 
    @Autowired
    public

AccountController(AccountRepository accountRepository) {
        this.accountRepository
= accountRepository;
    }
 
    @RequestMapping(value
=
"current",
method = RequestMethod.GET)
    @ResponseStatus(value
= HttpStatus.OK)
    @ResponseBody
    @PreAuthorize(value
=
"isAuthenticated()")
    public

Account current(Principal principal) {
        Assert.notNull(principal);
        return

accountRepository.findByEmail(principal.getName());
    }
 
    @RequestMapping(method
= RequestMethod.GET)
    @ResponseStatus(value
= HttpStatus.OK)
    @ResponseBody
    @PreAuthorize(value
=
"isAuthenticated()")
    public

Accounts list() {
        List
accounts = accountRepository.findAll();
        return

new

Accounts(accounts);
    }
 
    @RequestMapping(method
= RequestMethod.POST)
    @ResponseStatus(value
= HttpStatus.CREATED)
    @ResponseBody
    @PreAuthorize(value
=
"permitAll()")
    public

Account create(
@Valid

Account account) {
        accountRepository.save(account);
        return

account;
    }
 
    private

class

Accounts
extends

ArrayList {
        public

Accounts(List accounts) {
            super(accounts);
        }
    }
}

我希望你能明白:因为需要直接连接数据库,所以没有对密码进行编码。如果你比较在意这些小细节,那么可以稍后修改。目前这种方式是OK的。

完成

最后我考虑到还需要一个错误处理器,这样用户就可以看到JSON格式的错误信息而不是HTML。使用Spring Mvc以及@ControllerAdvice很容易实现这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@ControllerAdvice
public

class

ErrorHandler {
 
    @ExceptionHandler(value
= Exception.
class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public

ErrorResponse errorResponse(Exception exception) {
        return

new

ErrorResponse(exception.getMessage());
    }
 
}
 
public

class

ErrorResponse {
    private

String message;
    public

ErrorResponse(String message) {
        this.message
= message;
    }
    public

String getMessage() {
        return

message;
    }
}

如果你想了解关于Spring4 中@ControllerAdvice的用法,请点击以下链接

测试一下这个app

作为一个单元测试极客,本来应该先创建一个单元测试。但是……这次哥想用用新的工具:Postman(Chrome 插件),所以请往下看我是怎么做的:

获取所有account(非授权)

提交account(无需授权)

获取所有account(已授权)

获取当前account(已授权)

结束语

以上就是所有内容,希望你能像我一样喜欢这种创建项目的方式。创建这个项目以及写这篇文章总共花了我大概三个钟头。其中绝大多数时间是配置安全框架(我希望它在Java中能够更加彻底),以及编写这篇攻略。

原文链接: javacodegeeks 翻译: ImportNew.com邬柏

译文链接: http://www.importnew.com/7903.html

如何快速构建基于Spring4.0的Rest API(攻略)的更多相关文章

  1. SSM框架整合环境构建——基于Spring4和Mybatis3

    目录 环境 配置说明 所需jar包 配置db.properties 配置log4j.properties 配置spring.xml 配置mybatis-spring.xml 配置springmvc.x ...

  2. 使用PowerApps快速构建基于主题的轻业务应用 —— 进阶篇

    作者:陈希章 发表于 2017年12月14日 在上一篇 使用PowerApps快速构建基于主题的轻业务应用 -- 入门篇 中,我用了三个实际的例子演示了如何快速开始使用PowerApps构建轻业务应用 ...

  3. 使用PowerApps快速构建基于主题的轻业务应用 —— 入门篇

    作者:陈希章 发表于 2017年12月12日 前言 在上一篇文章 基于Office 365的随需应变业务应用平台 中我提到,随着随需应变的业务需要,以及技术的发展,业务应用的开发的模式也有了深刻的变化 ...

  4. Kaggle大牛小姐姐自述:我是怎么成为竞赛中Top 0.3%的 | 干货攻略

    天天跟数据打交道的研究人员,都有一个成为Kaggle顶级大师(Grandmaster)的梦想. 但每年的Kaggle参赛团队众多,通常一个项目都有数千人至上万人报名,如何在其中脱颖而出? 最近,自动化 ...

  5. 本地使用 Docker Compose 与 Nestjs 快速构建基于 Dapr 的 Redis 发布/订阅分布式应用

    Dapr(分布式应用程序运行时)介绍 Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的.无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言 ...

  6. 基于spring4.0配置分布式ehcache,以及相关使用

    说明:本文是基于RMI手动同步的方式,使用程序动态注入配置缓存,抛弃传统的ehcache.xml配置方式 1,注入cacheManager管理所有缓存,添加各个缓存名及相关参数配置: 思路大致是: 在 ...

  7. Angular for TypeScript 语法快速指南 (基于2.0.0版本)

    引导 import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; platformBrowserDynami ...

  8. 使用Spring Boot快速构建基于SQLite数据源的应用

    为了提供一个单包易部署的服务器应用,考虑使用Spring Boot,因为其集成了Apache Tomcat,易于运行,免去绝大部分了服务器配置的步骤. 项目初始化 首先从mvn archetype:g ...

  9. vuex 快速入门( 基于vue2.0,vue1.0未知可否)

    1.原理概述 2.用户登录例子解析: 由上图可以看到: 1.组件的数据是username,我们把它以name放在state中: 2.更改name发生在mutations的回调里,事件名字是showUs ...

  10. 使用Maven完成SSM框架整合环境构建——基于Spring4和Mybatis3

    只言片语 使用Maven来搭建一个SSM环境,其实和使用手工倒入jar的过程没有多大区别,所用的jar包都是一样的,但是区别在与不用你手动导入jar包了,而是只修改pom.xml,maven会自动根据 ...

随机推荐

  1. 机器学习--决策树算法(CART)

    CART分类树算法 特征选择 ​ 我们知道,在ID3算法中我们使用了信息增益来选择特征,信息增益大的优先选择.在C4.5算法中,采用了信息增益比来选择特征,以减少信息增益容易选择特征值多的特征的问题. ...

  2. 解决向github上push报 error: failed to push some refs to 'xxxxx' 问题

    解决向github上push报 error: failed to push some refs to 'xxxxx' 问题 1.问题 向github上push 代码时,报  error: failed ...

  3. Nuxt Kit 组件管理:注册与自动导入

    title: Nuxt Kit 组件管理:注册与自动导入 date: 2024/9/15 updated: 2024/9/15 author: cmdragon excerpt: Nuxt Kit 为 ...

  4. 【原创】解决NasCab掉进程,NasCab进程维护

    最近对象吐槽家里服务器又连不上,看不了考研视频了. 我掏出手机一试,确实连不上.家里的服务器是Win11平台,用NasCab管理的视频文件,然后通过frpc做的内网穿透. 我们在外面的图书馆,连不上无 ...

  5. JavaScript——案例-表单验证

    需求       <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  6. foobar2000 v1.6.13 汉化版(更新于2022.11.22)

    foobar2000 v1.6.13 汉化版 -----------------------[软件截图]---------------------- -----------------------[软 ...

  7. 2024年5月中国数据库排行榜:OP持续领跑,GoldenDB稳步上升进前八

    入夏时节,2024年5月的中国流行度排行榜排行榜如期发布.在这个骄阳似火的季节,各大数据库产品之间的竞争愈发激烈,名次间的细微变动展示了市场的动态和活力.各家厂商不断创新,通过技术升级和性能优化,力求 ...

  8. IOC注入分类 依赖注入

    依赖注入  也就是服务的注入 可以理解 一些服务的容器,目的:把一些全局需要使用的资源,服务放到某个接口中,使其可以在全局中使用 和前端的状态管理工具实现的功能差不多 注册服务的三种形式 单例模式Ad ...

  9. 云原生周刊:Artifact Hub 成为 CNCF 孵化项目|2024.9.23

    开源项目推荐 Coroot Coroot 是一个开源监控工具,旨在为云原生应用提供可观察性.它通过整合指标.日志和追踪信息,专注于提供应用性能的洞察. DirectPV DirectPV 是一个开源项 ...

  10. 云原生周刊:Kubernetes 1.29 中的删除、弃用和主要更改 | 2023.11.27

    开源项目推荐 Orphaned ConfigMaps 该版本库包含一个脚本,用于识别 Kubernetes 命名空间中的孤立的配置映射.孤立的配置映射是指那些未被命名空间中的任何活动 Pod 或容器引 ...