【问题标题】:How to make Asynchronous calls with express and node如何使用 express 和 node 进行异步调用
【发布时间】:2020-04-01 09:02:37
【问题描述】:
我有一个小型应用程序,它询问用户他们所在的城市并返回当前天气数据。当用户单击“获取天气”功能时,它会从 OpenWeatherMap api 中提取 Json 并将其返回给用户。截至目前,它刷新页面并显示数据。我需要更改什么,以便在您更改城市时异步加载而不是刷新页面?
Server.js(快速路由)
const express = require('express');
const bodyParser = require('body-parser');
const weatherFunctions = require('./functions/weatherFunctions.js')
const PORT = process.env.PORT || 5000
const app = express()
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
res.render('index', {weather: null, error: null});
})
app.post('/', weatherFunctions.getWeather)
app.listen(PORT, () => console.log(`Listening on ${ PORT }`))
weatherFunctions.js
const request = require('request');
const apiKey = '28af81603ac21f0fe4c75478dad21818';
function currentWeather(req, res) {
let city = req.body.city;
let url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperial&appid=${apiKey}`
request(url, function (err, response, body) {
if (err) {
res.render('index', {
weather: null,
error: 'Error, please try again'
});
} else {
let weather = JSON.parse(body)
if (weather.main == undefined) {
res.render('index', {
weather: null,
error: 'Error, please try again'
});
} else {
let weatherText = `It's ${weather.main.temp} degrees in ${weather.name}! `;
weatherText += `The low for today will be ${weather.main.temp_min} degrees with a high of ${weather.main.temp_max}`;
res.render('index', {
weather: weatherText,
error: null
}); //passes parameters for ejs to read
}
}
});
}
module.exports = {
getWeather: currentWeather
};
index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simple Weather</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300' rel='stylesheet' type='text/css'>
</head>
<body>
<div class="container">
<fieldset>
<form action="/" method="post">
<input name="city" type="text" class="ghost-input" placeholder="Enter a City" required>
<input type="submit" class="ghost-button" value="Get Weather">
</form>
<% if(weather !== null){ %>
<p><%= weather %></p>
<% } %>
<% if(error !== null){ %>
<p><%= error %></p>
<% } %>
</fieldset>
</div>
</body>
</html>
【问题讨论】:
标签:
javascript
node.js
ajax
express
asynchronous
【解决方案1】:
它让您耳目一新的原因是因为您正在渲染 HTML 服务器端。您将需要利用XMLHttpRequest (aka XHR)。您可以使用一个名为 fetch 的 XHR 包装器,这使得使用 XHR 变得更加容易。
使用浏览器获取 API 数据,然后使用该数据修改 DOM,称为“客户端渲染”..
例如,如果您打开浏览器控制台,并将以下代码粘贴到其中,您将看到如何从浏览器发送请求并取回数据(这是客户端渲染的基础):
fetch(`https://api.openweathermap.org/data/2.5/weather?q=New York&units=imperial&appid=28af81603ac21f0fe4c75478dad21818`).then(res => res.json()).then(data => console.log(data));
看下面的例子:
document
.getElementById("getWeather")
.addEventListener("click", () => {
handleGetWeather();
});
async function handleGetWeather() {
const apiKey = "28af81603ac21f0fe4c75478dad21818";
const locationEl = document.getElementById("location");
const weatherDataEl = document.getElementById("weatherData");
const results = await currentWeather(locationEl.value, apiKey);
weatherDataEl.innerHTML = results;
}
async function currentWeather(location, apiKey) {
const city = location.replace(/\s\s+/g, ' '); // Replace multiple spaces with single space
if (city === "" || city === " ") return `<pre>Please enter a location!</pre>`;
let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperial&appid=${apiKey}`;
try {
const res = await fetch(url);
if (res.ok === false) {
return `<pre><i>Location '${encodeURIComponent(city)}' not found!</i></pre><br/><pre>Full Error: ${res.statusText}</pre>`;
}
const weather = await res.json();
const main = weather.main;
const t = Number(main.temp) >= 71.00 ? 'hot' : 'cold';
const min = Number(main.temp_min) >= 71.00 ? 'hot' : 'cold';
const max = Number(main.temp_max) >= 71.00 ? 'hot' : 'cold';
return `
<h1>
It's <span class="${t}">${main.temp}</span> degrees in ${weather.name}!
</h1>
<h1>
The low for today will be
<span class="${min}">${main.temp_min}</span>
degrees with a high of
<span class="${max}">${main.temp_max}</span>
</h1>
`;
} catch {
return `<pre><i>Error, please try again.</i></pre>`;
}
}
pre {
color: red;
margin-bottom: -15px;
}
.hot {
color: red;
}
.cold {
color: blue;
}
<input id="location" />
<button id="getWeather">Get Weather</button>
<div id="weatherData"></div>
【解决方案2】:
快速解答
您需要使用 javascript 和 ajax(前端)直接从您的页面使用 api.openweathermap 端点,而不是通过您(后端)中的 nodejs
说明
在 Web 开发中,有两种方法可以向最终用户提供内容,称为 Server rendering and client side rendering
服务器端渲染 (SSR) — 传统的渲染方法,基本上您页面的所有资源都存放在服务器上。然后,当页面被请求(通常来自网络浏览器)时,Html、JS 和 CSS 被下载。框架也可以动态地根据后端逻辑创建 html 并最终下载它。
技术:java、c#、python、nodejs 等
客户端渲染 (CSR) — 一种更新的渲染方法,它依赖于通过 JavaScript 框架在客户端(浏览器)上执行的 JS。因此,当请求页面时,会下载一个最小的、很少的或空的 index.html、css 和 js。 javascript 负责发送或接收数据并更新页面的最小部分无需刷新整个页面。。最后,当用户单击或触发某些事件时,javascript 将使用异步调用 (ajax) 通常将 数据 发送或接收到 api rest (json)。
技术:react、angular、vue、aurelia、jquery、纯javascript等
在您的情况下,您使用的是后端语言 nodejs 和 ejs framework 在后端动态创建 html,换句话说:服务器渲染
最后,如果您不想要整个页面的刷新效果,您可以在您的 nodejs 应用程序中添加一个 javascript 作为简单资产,以便在您的 index.ejs 和/或底部下载你的
此 javascript 必须检测到 页面完全加载 才能加载将 javascript 函数绑定到您的 .你可以使用:
- jquery:
- XMLHttpRequest
- 获取
- axios
【解决方案3】:
要在不刷新页面的情况下加载新城市,以下是您要执行的操作的概要:
在您的页面中连接客户端 Javascript,以便拦截表单提交,而不是让浏览器提交表单。
通过 Ajax 调用从您的页面 Javascript 将表单发送到您的服务器(可能使用客户端 fetch() 接口。
更改服务器 POST 处理程序,使其呈现页面的一部分(更改的部分),而不是整个页面。
在您的 Javascript 中获取 ajax 调用的结果,并使用 DOM 操作将页面的一部分内容替换为新呈现的部分(可能设置页面中特定父对象的 .innerHTML) .
对于 Ajax 调用的工作方式,您有几种方法。您可以从服务器获取一段呈现的 HTML,然后将该 HTML 插入页面(替换之前的一些 HTML),或者您可以使用 Ajax 调用获取 JSON 数据,然后在浏览器中将其呈现为 HTML,然后插入HTML。您需要模板渲染的客户端版本才能执行第二个选项。
仅供参考,目前尚不清楚您为什么将“异步”这个词作为您问题的重要组成部分。这里几乎所有的东西都已经是异步的了。即使在当前版本的页面中发布表单已经是异步的,如果您按上述方式更改它,通过 Javascript 发布也将是异步的。