最近解决了这么一个需求.
nuxt的项目要获取客户端用户的所在地址,然后提交表单的时候提交给后端保存.将来对特定地区的用户做特殊的活动推送.
刚拿到这需求的时候觉得这不都是后端接口返回就好了嘛~出人意料的,后端找了个nodeipdb库要求前端来实现. 反正也没做过.也有兴趣研究研究,就接下来了.
刚开始做的时候想着用第三方接口来实现比如 搜狐的接口来实现如下:
<script src="http://pv.sohu.com/cityjson?ie=utf-8"></script>
<script type="text/javascript">
console.log(returnCitySN["cip"]+','+returnCitySN["cname"])
</script>
很轻松的实现了,但是发现有被代理过的ip不准确.
接着搜索了各种文章,进行了各种尝试,我们来一步一步来解决.
1.第一步,首先要获取用户的真实ip地址.经过查询各方资料发现nuxt的上下文能拿到 客户端向服务端请求页面时候的req. 在这里面可以获取到真实的ip
function getClientIp(req) {
var ipAddress
var forwardedIpsStr = req.headers['x-forwarded-for'] //判断是否有反向代理头信息
if (forwardedIpsStr) {
//如果有,则将头信息中第一个地址拿出,该地址就是真实的客户端IP;
var forwardedIps = forwardedIpsStr.split(',')
ipAddress = forwardedIps[0]
}
if (!ipAddress) {
//如果没有直接获取IP;
ipAddress = req.connection.remoteAddress
}
return ipAddress
}
这样我们就能获取到真实的用户ip了~
2.第二步把ip扔进库里查询返回对应的地址.我这边用的是 node版的ipip-ipdb
地址是:github.com/ipipdotnet/…
这个得在服务端才能使用.
export const actions = {
async nuxtServerInit({ commit }, { req, res, route }) {
//他有个地址库的文件,需要读取.所以这边把文件存放的路径传上去了.因为node里读取文件路径必须是绝对地址.
let url = process.cwd() + '/assets/mydata4vipday4_cn.ipdb'
// 根据ip获取客户端地址
let city = new ipdb.City(url)
let area = city.findInfo(getClientIp(req), 'CN')
commit('setArea', {
area: area.countryName + area.regionName + area.cityName,
ip: getClientIp(req)
})
})
}
把地址存入vuex里,随时随地可以调用很舒服呀~.如此一来问题貌似完美解决了.
但是上线后发现.线上有做服务器缓存.导致我看到的地址是10分钟前别人的ip获取的.这下就蛋疼了.
思考良久想了一个方案如下: 新建一个vue页面.然后在获取地址的组件里对新建页面发起请求.在新建页面里进行ip地址查询.并返回地址给请求的页面.
这是一个新建页面 getIp
<template>
<div class="ipClass">ipBox {{ area.countryName + area.regionName + area.cityName}} ipBox</div>
</template>
<script>
import ipdb from 'ipip-ipdb'
export default {
data() {
return {
area: ''
}
},
head: {
title: '获取ip',
meta: []
},
async asyncData({ req, res }) {
//响应头部加上no-cache 不缓存
res.setHeader(
'Cache-Control',
'max-age=0, must-revalidate, private, no-cache, no-store'
)
function getClientIp(req) {
var ipAddress
var forwardedIpsStr = req.headers['x-forwarded-for'] //判断是否有反向代理头信息
if (forwardedIpsStr) {
//如果有,则将头信息中第一个地址拿出,该地址就是真实的客户端IP;
var forwardedIps = forwardedIpsStr.split(',')
ipAddress = forwardedIps[0]
}
if (!ipAddress) {
//如果没有直接获取IP;
ipAddress = req.connection.remoteAddress
}
return ipAddress
}
let url = process.cwd() + '/assets/mydata4vipday4_cn.ipdb'
// 根据ip获取客户端地址
let city = new ipdb.City(url)
let area = city.findInfo(getClientIp(req), 'CN')
return {
area: area
}
},
created() {},
methods: {}
}
</script>
<style scoped>
.ipClass {
height: 500px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
这是请求的方法
async getIp() {
const res = await this.$axios.$post(`./getIp`, {})
let b = JSON.stringify(res).split('ipBox')
this.area = b[1]
},
如此完美解决!