本文由 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. Python 在PDF中添加条形码、二维码

    在PDF中添加条码是一个常见需求,特别是在需要自动化处理.跟踪或检索PDF文件时.作为一种机器可读的标识符,PDF中的条码可以包含各种类型的信息,如文档的唯一标识.版本号.日期等.以下是一篇关于如何使 ...

  2. EF Core报错“Format of the initialization string does not conform to specification starting at index 0.”

    问题分析: 今天在EF Core数据库迁移的过程中无意中发现此错误,我的项目仅仅复制黏贴了配置文件而已,自此发现是数据库配置文件json在作祟. 对比了下发现是.json文件没有被设置"复制 ...

  3. ASP.NET Core – Logging & Serilog

    前言 以前就写过了 Asp.net core 学习笔记 (Logging 日志), 只是有点乱, 这篇作为整理版. 参考: docs – Logging in .NET Core and ASP.NE ...

  4. Docker修改IP地址方法

    一.查看Docker IP root@master:/# ifconfig docker0 docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu ...

  5. 【赵渝强老师】大数据分析引擎:Presto

    一.什么是Presto? 背景知识:Hive的缺点和Presto的背景 Hive使用MapReduce作为底层计算框架,是专为批处理设计的.但随着数据越来越多,使用Hive进行一个简单的数据查询可能要 ...

  6. c++面经系列0:开篇-c++岗位面试都会问些什么?

    本文是C++岗位面试经验分享系列的开篇,敬请持续关注. 在C++岗位面试中,通常首先进行技术面试,若通过则会进行HR面试.HR面试的内容先暂且略过,未来我们会有机会深入探讨,今天我们主要聚焦于技术面试 ...

  7. laravel中添加公共函数

    laravel中添加公共函数 1. 在项目中的新建app/Helper/functions.php文件 2.在项目的跟目录找到composer.json 文件,并打开,然后再autoload中添加如下 ...

  8. ftrace irqs跟踪器

    当中断被关闭(俗称关中断)了,CPU就不能响应其他的事件,如果这时有一个鼠标中断,要在下一次开中断时才能响应这个鼠标中断,这段延迟称为中断延迟.向current_tracer 文件写入 irqsoff ...

  9. CE-植物大战僵尸杂交版

    植物大战僵尸杂交版 偏移:208+82c

  10. dotnet 委托delegate的使用 定义和使用

    void Main() { // 委托 - 初级和高级的分水岭 // 1. 委托的初体验 // 委托是一个引用类型,其实是一个类型,保存方法的指针(地址) (变量名字都是地址 都是指针) // 是一个 ...