[英]Fetch Command Between React Native and C# WebAPI on Localhost Returns Network Request Failed error
I'm having an issue in React Native in Android and iOS where I'm trying to run a fetch
command to point to a C# WebAPI endpoint running on my localhost
.我在 Android 和 iOS 中的 React Native 中遇到问题,我试图运行
fetch
命令以指向在我的localhost
上运行的 C# WebAPI 端点。 I keep getting the following error:我不断收到以下错误:
[TypeError: Network request failed]
I notice that if I have the fetch
command point to an external source (see comment in the code below) that the fetch
works just fine.我注意到,如果我让
fetch
命令指向外部源(请参阅下面代码中的注释),那么fetch
就可以正常工作。 But for a localhost
connection, I can't get this thing to work at all.但是对于
localhost
连接,我根本无法让这个东西工作。
Here's the code for my login page in React Native:这是我在 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,
},
});
Here's the AndroidManifest.xml
for the React Native app:这是 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>
Here's the code for the API endpoint found at https://localhost:44371/api/Login :这是在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();
}
}
}
}
}
Here's the Startup.cs
file that enables CORS:这是启用 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
});
}
}
}
Does anybody here have any idea how to fix this issue?这里有人知道如何解决这个问题吗? As you've seen I've tried setting up CORS fixes as well as adding cleartext traffic, but no juice.
如您所见,我尝试设置 CORS 修复以及添加明文流量,但没有任何效果。 Any suggestion is appreciated.
任何建议表示赞赏。
May be you need to add CORS policy attribute on top of LoginService also.可能您还需要在 LoginService 之上添加 CORS 策略属性。
[EnableCors("<YOUR_CORS_POLICY_NAME_GOES_HERE>")]
public class LoginService : BaseService
{ ... }
localhost only work in your windows application than run in your OS if you use emulator or real device for test & debug you most run visual studio iis express web server by ip address如果您使用模拟器或真实设备进行测试和调试,localhost 只能在您的 Windows 应用程序中运行,而不是在您的操作系统中运行
binding protocol="http" bindingInformation="*:4732:"绑定协议="http" 绑定信息="*:4732:"
for example 4732=>xxxx is my project.例如 4732=>xxxx 是我的项目。
run asp application you can access to iis express web server by any local ip set in Lan,WLan运行 asp 应用程序,您可以通过 LAN、WLan 中设置的任何本地 ip 访问 iis express web 服务器
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.