[NUXT]如何不通过后端获取客户端所在城市地区.

1,006 阅读2分钟

最近解决了这么一个需求.
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]
},

如此完美解决!