我在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修复以及添加明文流量,但没有效果。如有任何建议,欢迎。
也许你还需要在LoginService的顶部添加CORS策略属性。
[EnableCors("<YOUR_CORS_POLICY_NAME_GOES_HERE>")]
public class LoginService : BaseService
{ ... }
localhost只能在windows应用程序中工作,而不能在操作系统中运行如果您使用模拟器或真实设备进行测试&调试你最运行的visual studio iis express web服务器的IP地址
- 请午餐visual studio以管理员身份运行
- 进入你的visual studio项目文件夹,找到sln文件所在的。vs隐藏文件夹。
- 找到applicationhost。配置文件并通过编辑器打开
- 在sites标签中找到子标签site并设置bindingInformation *:xxxx:
绑定协议="http"bindingInformation ="*:4732:">
例如4732=>xxxx是我的项目。
运行asp应用程序,您可以通过Lan,WLan设置的任何本地ip访问iis express web服务器