本地主机上的 React Native 和 C# WebAPI 之间的获取命令返回网络请求失败错误
Posted
技术标签:
【中文标题】本地主机上的 React Native 和 C# WebAPI 之间的获取命令返回网络请求失败错误【英文标题】:Fetch Command Between React Native and C# WebAPI on Localhost Returns Network Request Failed error 【发布时间】:2021-10-29 14:52:36 【问题描述】:我在 android 和 ios 中的 React Native 中遇到问题,我正在尝试运行 fetch
命令以指向在我的 localhost
上运行的 C# WebAPI 端点。我不断收到以下错误:
[TypeError: Network request failed]
我注意到,如果我将 fetch
命令指向外部源(请参阅下面代码中的注释),fetch
就可以正常工作。但是对于localhost
连接,我根本无法让这个东西工作。
这是我在 React Native 中登录页面的代码:
import "react-native-gesture-handler";
import StatusBar from "expo-status-bar";
import React, useState, Component from "react";
import
StyleSheet,
Text,
TextInput,
View,
Button,
Dimensions,
TouchableOpacity,
from "react-native";
import NavigationContainer from "@react-navigation/native";
import createStackNavigator from "@react-navigation/stack";
import NetInfo from "@react-native-community/netinfo";
import getUniqueId from "react-native-device-info";
import API_URL, PRIMARY_COLOR, QUATERNARY_COLOR from "../env.json";
import * as Linking from "expo-linking";
var width = Dimensions.get("window").width - 20;
const LogInTheUser = (emailAddress, password) =>
console.log(`$API_URL/Login`);
let userInfo = fetch(`https://localhost:44371/api/Login`,
//"https://devapi.flouriish.io/api/Login",
method: "POST",
cache: "no-cache",
mode: "cors",
headers:
"Content-Type": "application/json",
,
body: JSON.stringify(
emailAddress: emailAddress,
password: password,
ipAddress: ipAddress,
deviceUUID: deviceUUID,
),
)
.then((response) => response.json())
.then((responseData) =>
console.log(responseData);
return responseData;
)
.catch((error) => console.warn(error));
;
const deviceUUID = getUniqueId();
let ipAddress = "127.0.0.1";
NetInfo.fetch().then((state) =>
ipAddress = state.details.ipAddress;
);
export default class Login extends Component
state =
emailAddress: "",
password: "",
;
static navigationOptions =
title: "ResetPassword",
;
render()
return (
<View style=styles.login>
<Text style=styles.label>Email Address</Text>
<TextInput
style=styles.input
placeholder="Email Address"
placeholderTextColor=PRIMARY_COLOR
onChangeText=(value) => this.setState( emailAddress: value )
value=this.state.emailAddress
/>
<Text style=styles.label>Password</Text>
<TextInput
style=styles.input
placeholder="Password"
placeholderTextColor=PRIMARY_COLOR
onChangeText=(value) => this.setState( password: value )
value=this.state.password
secureTextEntry=true
/>
<Text style=styles.label>IP Address: ipAddress</Text>
<Text style=styles.label>Device UUID: deviceUUID</Text>
<Button
title="Login"
color=PRIMARY_COLOR
onPress=() =>
LogInTheUser(this.state.emailAddress, this.state.password)
/>
<TouchableOpacity
style=styles.label
onPress=() => this.props.navigation.navigate("ForgotPassword")
>
<Text style= color: PRIMARY_COLOR >Forgot Password?</Text>
</TouchableOpacity>
<StatusBar style="auto" />
</View>
);
const styles = StyleSheet.create(
login:
flex: 1,
backgroundColor: QUATERNARY_COLOR,
alignItems: "center",
,
label:
marginTop: 10,
color: PRIMARY_COLOR,
marginBottom: 10,
,
input:
height: 40,
width: width,
margin: 12,
borderWidth: 1,
borderColor: PRIMARY_COLOR,
color: PRIMARY_COLOR,
,
);
这是 React Native 应用程序的 AndroidManifest.xml
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.akmaziofrontend">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@akmazio/akmazio-frontend"/>
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="41.0.0"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.App.SplashScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name = "android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme = "flouriish" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
</manifest>
这是在https://localhost:44371/api/Login 找到的 API 端点的代码:
using akmazio_api.Classes;
using akmazio_api.Contexts;
using akmazio_api.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace akmazio_api.Services
public class LoginService : BaseService
public LoginService(IConfiguration configuration, DatabaseContext context)
: base(configuration, context)
public User Login(Login login)
var user = Context.Users
.Include(x => x.UserRole)
.Include(x => x.UserType)
.Include(x => x.Business)
.Include(x => x.Business.Address)
.Include(x => x.Address)
.Single(x => x.EmailAddress == login.EmailAddress && x.IsVerified);
var computedHash = Cryptography.ComputeHash(login.Password, user.Salt);
if (!user.PasswordHash.Equals(computedHash))
throw new SecurityException("User login credentials are not valid");
else
var userLoginTokens = Context.UserLoginTokens
.Include(x => x.User)
.Where(x => x.User.UserId == user.UserId &&
x.IPAddress == login.IPAddress && x.DeviceUUID == login.DeviceUUID);
if (userLoginTokens.Any())
var token = userLoginTokens.First().Token;
user.Token = token;
return user.GetScrubbedUser();
else
var userLoginToken = new UserLoginToken()
User = user,
Token = Guid.NewGuid().ToString(),
IPAddress = login.IPAddress,
DeviceUUID = login.DeviceUUID
;
Context.UserLoginTokens.Add(userLoginToken);
Context.SaveChanges();
user.Token = userLoginToken.Token;
return user.GetScrubbedUser();
这是启用 CORS 的 Startup.cs
文件:
using akmazio_api.Contexts;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace akmazio_api
public class Startup
public Startup(IWebHostEnvironment env)
var builder = new ConfigurationBuilder();
if (!env.IsProduction())
builder
.SetBasePath(env.ContentRootPath)
.AddJsonFile($"appsettings.env.EnvironmentName.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
else
builder
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
public IConfiguration Configuration get;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.AddSingleton(Configuration);
services.AddControllers();
services.AddSwaggerDocument(settings =>
settings.Version = "v1";
settings.Title = "Akmazio Flouriish API";
);
services.AddCors(o => o.AddPolicy("DevPolicy", builder =>
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
));
services.AddCors(o => o.AddPolicy("ProdPolicy", builder =>
builder.WithOrigins("https://flouriish.io")
.AllowAnyMethod()
.AllowAnyHeader();
));
services.AddDbContext<DatabaseContext>(option =>
option.UseSqlServer(Configuration["ConnectionStrings:DatabaseConnection"]));
services.AddDatabaseDeveloperPageExceptionFilter();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment() || env.IsEnvironment("Local"))
app.UseOpenApi();
app.UseSwaggerUi3();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "akmazio_api v1"));
app.UseExceptionHandler("/error-local-development");
else
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
if (env.IsDevelopment() || env.IsEnvironment("Local"))
app.UseCors("DevPolicy");
else if (env.IsProduction())
app.UseCors("ProdPolicy");
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
);
app.UseForwardedHeaders(new ForwardedHeadersOptions
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
);
这里有人知道如何解决这个问题吗?正如您所见,我尝试设置 CORS 修复以及添加明文流量,但没有任何效果。任何建议表示赞赏。
【问题讨论】:
【参考方案1】:您可能还需要在 LoginService 之上添加 CORS 策略属性。
[EnableCors("<YOUR_CORS_POLICY_NAME_GOES_HERE>")]
public class LoginService : BaseService
...
【讨论】:
即使EnableCors
属性位于 LoginService
类上方,我仍然遇到 CORS 问题
看起来这是一个问题,Android 设备试图连接到它自己的本地主机环境,而不是主机的本地主机。关于如何在 C# WebAPI 和 React Native 中解决此问题的任何想法?【参考方案2】:
localhost 只能在您的 Windows 应用程序中运行,而不是在您的操作系统中运行 如果您使用模拟器或真实设备进行测试和调试,您最常通过 IP 地址运行 Visual Studio iis Express Web 服务器
-
请午餐 Visual Studio 以管理员身份运行
转到您的 Visual Studio 项目文件夹并找到 .vs 隐藏文件夹,其中是 sln 文件。
找到applicationhost.config文件,用编辑器打开
在sites标签中找到子标签site并设置bindingInformation *:xxxx:
绑定协议="http" bindingInformation="*:4732:"
例如 4732=>xxxx 是我的项目。
运行asp应用程序,您可以通过设置在局域网、无线局域网中的任何本地ip访问iis express web服务器
【讨论】:
以上是关于本地主机上的 React Native 和 C# WebAPI 之间的获取命令返回网络请求失败错误的主要内容,如果未能解决你的问题,请参考以下文章
使用 NextJS + Express 在本地主机上进行 HTTPS