I started this project a year ago when I was trying to migrate my YouTube Music playlists (essentially YouTube Playlists) to my Spotify account. It worked fine as a standalone tool. Later it became almost a must for me to upgrade this project from a simple Java application to a full-stack application, with a proper backend and a proper frontend. This article will walk you through parts of the project as well as the deployment details.
一年前,当我尝试将我的YouTube音乐播放列表(主要是YouTube播放列表)迁移到我的Spotify帐户时,我就开始了这个项目。 作为独立工具,它运行良好。 后来,对于我来说,将这个项目从一个简单的Java应用程序升级到一个具有适当后端和适当前端的全栈应用程序几乎成为了我的必须。 本文将引导您完成项目的各个部分以及部署详细信息。
In case you’re wondering, here are some frameworks/services I used for this project:
如果您想知道,以下是我用于该项目的一些框架/服务:
- VueJS (Frontend Dev) VueJS(前端开发)
- Java Spring Boot (Server Dev) Java Spring Boot(服务器开发)
- Nginx (Proxy server) Nginx(代理服务器)
- Docker / Docker stack (containerization) Docker / Docker堆栈(容器化)
- YouTube Data API & Spotify API YouTube数据API和Spotify API
OAuth
OAuth
For projects using any external resources that require some sort of API keys and access tokens, addressing the security concerns is usually the most critical component. I wanted all the OAuth handshakes to be taken care of by the server so that no access token or any type of secret key is visible on the client-side. Here is a diagram for the OAuth process of how I achieve that:
对于使用任何需要某种API密钥和访问令牌的外部资源的项目,解决安全问题通常是最关键的组件。 我希望服务器处理所有OAuth握手,以便在客户端看不到任何访问令牌或任何类型的密钥。 这是我如何实现OAuth过程的图表:
The access token from third party resources is never seen on the client-side, and it’s indexed with a uuid unique to each browser session.
来自第三方资源的访问令牌在客户端永远不会看到,并且使用每个浏览器会话唯一的uuid对其进行索引。
Server (Spring Boot)
服务器(Spring启动)
This server won’t have any type of data storage since it’s not meant for production. Memory storage is enough. Of course, if one does decide to bring this to the production level, some type of data storage is probably inevitable so that the same state can be shared across multiple docker worker nodes.
该服务器没有任何类型的数据存储,因为它不是用于生产的。 内存存储就足够了。 当然,如果确实决定将其带入生产级别,则某种类型的数据存储可能是不可避免的,以便可以在多个docker worker节点之间共享同一状态。
Using Spring Boot for this server is an overkill for this project, but I wanted a chance to play with Spring Boot. The server application’s main function is to interact with third-party resources, format the responses, and return it to the client application for rendering. You can find a lot of great tutorials on how to get started with Spring Boot online.
在该服务器上使用Spring Boot对于该项目来说是一个大问题,但我希望有机会使用Spring Boot。 服务器应用程序的主要功能是与第三方资源进行交互,格式化响应并将其返回给客户端应用程序进行渲染。 您可以找到很多很棒的教程 ,这些教程关于如何在线开始使用Spring Boot。
One thing I want to mention is the setup for CORS. Spring Boot provides support for CORS to prevent malicious requests from accessing server content. That’s why we need to specify the allowed cross-origin so that the frontend application can access server resources. It’s easy to define one Cross Origin with the @CrossOrigin annotation on controller scope. But adding multiple cross origins and sharing the cross-origin configuration globally can be a little tricky. Luckily, Spring Boot offers a somewhat simple way to do that as well. You just need to implement the WebMvcConfigurer class from SpringBoot and override the addCorsMappings function. You can find more information here.
我要提到的一件事是CORS的设置。 Spring Boot为CORS提供支持,以防止恶意请求访问服务器内容。 这就是为什么我们需要指定允许的跨域,以便前端应用程序可以访问服务器资源。 在控制器范围内使用@CrossOrigin批注定义一个交叉原点很容易。 但是添加多个交叉原点并在全球范围内共享交叉原点配置可能会有些棘手。 幸运的是,Spring Boot也提供了一种简单的方法。 您只需要从SpringBoot实现WebMvcConfigurer类并覆盖addCorsMappings函数。 您可以在此处找到更多信息。
Client-side Application(Vue)
客户端应用程序(Vue)
The client-side application is a very minimal setup to mostly display songs and allow the user to interact with these items. A uuid is associated with each client session so that the server application knows which access token it should use to interact with third-party resources. Also, some type of state management tool is needed when you’re dealing with a frontend framework like Vue or React. In this project, I used Vuex for that purpose. Vuex is a lot easier to learn than Redux. You can read more about it here.
客户端应用程序是一个非常小的设置,主要用于显示歌曲并允许用户与这些项目进行交互。 一个uuid与每个客户端会话相关联,以便服务器应用程序知道应该使用哪个访问令牌与第三方资源进行交互。 另外,在处理诸如Vue或React之类的前端框架时,需要某种类型的状态管理工具。 在这个项目中,我将Vuex用于此目的。 Vuex比Redux更容易学习。 您可以在此处了解更多信息。
部署-Docker堆栈 (Deployment — Docker Stack)
I used Docker to containerize this project because the client-side framework is vastly different from the server-side framework, and it would’ve required a lot of setup and configuration to get the project going. So Docker would drastically simplify that process and increase scalability. Using Docker Stack would require you to be on a Docker Swarm. If you’ve never used Docker or Docker swarm before, I would recommend checking out the Docker Doc. Here is a snippet of what you might wanna do:
我使用Docker来对这个项目进行容器化,因为客户端框架与服务器端框架有很大的不同,并且要进行该项目需要大量的设置和配置。 因此,Docker将大大简化该流程并提高可扩展性。 使用Docker Stack将要求您使用Docker Swarm。 如果您以前从未使用过Docker或Docker swarm,则建议您查阅Docker Doc。 以下是您可能想要做的一小段:
docker --version // Make sure it's at least 1.24
docker swarm init // This will init a docker swarm on your machine
Containerize Client-side app (Vue)
容器化客户端应用程序(Vue)
Vue has a pretty nice document on how to containerize Vue projects. I used this exact snippet from here (https://vuejs.org/v2/cookbook/dockerize-vuejs-app.html) to containerize the Vue project.
Vue关于如何容器化Vue项目的文档非常不错。 我从这里使用了这个确切的片段( https://vuejs.org/v2/cookbook/dockerize-vuejs-app.html )来容器化Vue项目。
Containerize Server Side App (Java Spring Boot)
容器化服务器端应用程序(Java Spring Boot)
Containerizing the server-side app is a little more complicated.
容器化服务器端应用程序要复杂一些。
First I created a startup script to dynamically generate properties file based on the environment variables from the compose file. This startup script will generate the necessary properties file and then start the server.
首先,我创建了一个启动脚本,用于根据组合文件中的环境变量动态生成属性文件 。 此启动脚本将生成必要的属性文件,然后启动服务器。
Then I used the multi-stage build feature from Docker to build the image. This is mainly to minimize the image size and make the Dockerfile easier to read. You can learn more about multi-stage builds here.
然后,我使用了Docker的多阶段构建功能来构建映像。 这主要是为了最小化映像大小并使Dockerfile更易于阅读。 您可以在此处了解有关多阶段构建的更多信息。
In the first stage of the build, a Maven image is used to build the application, so that the later stage can access the built package. Then all I need to do on the second stage is to copy the built files from the first stage and the startup script. And I’m good to go.
在构建的第一阶段,将使用Maven映像来构建应用程序,以便以后的阶段可以访问所构建的包。 然后,我在第二阶段需要做的就是从第一阶段和启动脚本中复制构建的文件。 而且我很好。
NGINX
NGINX
If you’re only playing with this project and just want to start the project up locally, you can skip this step, you don’t need an Nginx service.
如果您只是在玩这个项目,而只想在本地启动该项目,则可以跳过此步骤,不需要Nginx服务。
To deploy this project on your server and make it accessible from your domain, I decided to use NGINX as a proxy server to forward different requests to different applications.
为了在您的服务器上部署该项目并使其可从您的域访问,我决定将NGINX用作代理服务器,以将不同的请求转发到不同的应用程序。
To start an Nginx service, the first step is to add a basic Nginx service to the compose file.
要启动Nginx服务,第一步是将基本Nginx服务添加到撰写文件中。
A few things to note here:
这里需要注意的几件事:
./nginx/templates dir contains all the .template files that are read by Nginx upon startup.
./nginx/templates目录包含启动时Nginx读取的所有.template文件。
./nginx/certs dir contains the SSL certificate for Https.
./nginx/certs目录包含Https的SSL证书。
- NGINX_HOST can be passed as an environment variable and you can reference it in the template file as ${NGINX_HOST} NGINX_HOST可以作为环境变量传递,您可以在模板文件中将其引用为$ {NGINX_HOST}
After the docker file is ready, then we can proceed to add the Nginx conf template.
在docker文件准备好之后,我们就可以继续添加Nginx conf 模板了 。
- This configuration file will forward all Http requests to Https and will forward different requests paths to different applications. 此配置文件会将所有Http请求转发到Https,并将不同的请求路径转发到不同的应用程序。
- Docker stack allows you to reach another service within the same stack by specifying the service name and port. That’s why we have apiservice:8080 and webgui:8080 Docker堆栈允许您通过指定服务名称和端口来访问同一堆栈内的另一服务。 这就是为什么我们有apiservice:8080和webgui:8080的原因
Finally, you can bring up the whole stack with just one command:
最后,您仅需一个命令即可启动整个堆栈:
docker stack deploy -c stack.yml stack_name
翻译自: https://medium.com/@zezhanchen/fullstack-with-vue-spring-boot-and-nginx-d22d13898d3e