Quantcast
Channel: Active questions tagged rest - Stack Overflow
Viewing all articles
Browse latest Browse all 3667

C# API returns JSON in Swagger but HTML in Postman and React

$
0
0

So I'm trying to render an Accounts component that would just render what returns from a C# back end. In Swagger, the API call returns JSON, great, but if I hit the exact same point in Postman or my React front-end, I get the main.jsx page, which is...less than useful.

The relevant snippet of my API:

var group = routes.MapGroup("/api/tblAccount").WithTags(nameof(tblAccount));// We only want active (not soft-deleted) accounts.group.MapGet("/", async (my_solution_name_Context db) =>{    var accounts = await db.tblAccounts.ToListAsync();    var ret = JsonSerializer.Serialize(accounts.Where(a => a.IsDeleted == false));    return Results.Ok(ret);}).WithName("GetAlltblAccounts").WithOpenApi();

And my component (yeah, with some WIP sections):

import { useEffect, useMemo, useState, Fragment } from 'react';import './App.css';function Accounts() {    const [accounts, showAccounts] = useState([]);    //useEffect(() => {    useMemo(() => {        populateAccountsData();    }, [accounts]);    const contents = accounts === undefined        ? <p><em>Loading... Please refresh once the ASP.NET backend has started.</em></p>        : <table className="table table-striped" aria-labelledby="tableLabel" ><thead><tr><th>First Name</th><th>Last Name</th><th>Assigned Unit</th><th>Primary Account Holder</th><th>Acct. Creation Dt.</th><th>&nbsp;</th></tr></thead><tbody>                {accounts.map(a => { //needs to eventually change to accounts.filter where IsDeleted is false, unless I figure out how to do this better on the API end.                    debugger;                    //return (<Fragment key={a.pk_int_id}><tr><td>a.primary_account_holder.first_name</td><td>a.primary_account_holder.last_name</td><td>a.unit_id</td><td>a.primary_account_holder.primary_account_holder</td><td>a.creation_date</td><td><button className='btn' role="button">Edit</button>&nbsp<button className='button' rule="button">Mark Inactive</button></td></tr></Fragment>                    //);                    })  //end accounts.map (technically below, but JSX)                }</tbody></table>;    return (<div className="container"><h1>My Solution</h1><h2>Accounts</h2>            {contents}<button className="btn btn-primary" id="newAccount" type="button" onClick={addAcct}>Create a new account!</button></div>    );    async function populateAccountsData() {        try {            const response = await fetch('https://localhost:5173/api/tblAccounts/', {                headers: {"Accept": "application/json",                    //"mode": "no-cors","Access-Control-Allow-Origin": "*"                }            });            if (!response.ok) {                console.error('API request failed:', response.statusText);                return; // Prevent further execution if the fetch fails            } //end if            console.log(response.headers);            const contentType = response.headers.get("content-type");            if (!contentType || !contentType.includes("application/json")) {                throw new TypeError("Oops, we haven't got JSON!");            }            const data = await response.json();            debugger; // This should now get hit if the request is successful            showAccounts(data);        } catch (error) {            console.error('Error fetching accounts:', error);        }    } //end populateAccountsData    function addAcct() {    }} //end Accountsexport default Accounts;

As you can see, I've tried messing with headers and etc. The console statement that prints response.headers prints an object that's empty except for the [Prototype] part. My debugger; call in <tbody> isn't being hit, nor the one in my actual call that populates the thing.

I'm an utter React newb; any time I've done something like this in the past, it's been with jQuery and AJAX directly manipulating my DOM.

Side question: is there a way to avoid hard-coding "localhost:port" in the JSX? I just have a really bad feeling about doing that...

I already tried everything listed here that would pertain to this stack: https://www.reddit.com/r/dotnet/comments/193432s/what_causes_apis_to_return_html_content_instead/

EDIT: Per request, posting my package.json (granted with a lot of the metadata parts omitted for privacy.)

"dependencies":{"bootstrap": "^5.3.3","immer": "^10.1.1","prettier": "^3.3.3","react": "^18.3.1","react-bootstrap": "^2.10.4","react-dom": "^18.3.1","use-immer": "^0.10.0"  },"devDependencies": {"@eslint/js": "^9.9.0","@types/bootstrap": "^5.2.10","@types/eslint__js": "^8.42.3","@types/eslint-plugin-jsx-a11y": "^6.9.0","@types/react": "^18.3.3","@types/react-dom": "^18.3.0","@vitejs/plugin-react": "^4.3.1","eslint": "^9.9.0","eslint-plugin-jsx-a11y": "^6.10.0","eslint-plugin-react": "^7.35.2","eslint-plugin-react-hooks": "^5.1.0-rc.0","eslint-plugin-react-refresh": "^0.4.11","glob": "^11.0.0","globals": "^15.9.0","jest": "^29.7.0","lru-cache": "^11.0.0","vite": "^5.4.1"  }

EDIT 2: vite.config.js below:

import { fileURLToPath, URL } from 'node:url';import { defineConfig } from 'vite';import plugin from '@vitejs/plugin-react';import fs from 'fs';import path from 'path';import child_process from 'child_process';import { env } from 'process';const baseFolder =    env.APPDATA !== undefined && env.APPDATA !== ''        ? `${env.APPDATA}/ASP.NET/https`        : `${env.HOME}/.aspnet/https`;const certificateName = "solutionname.client";const certFilePath = path.join(baseFolder, `${certificateName}.pem`);const keyFilePath = path.join(baseFolder, `${certificateName}.key`);if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {    if (0 !== child_process.spawnSync('dotnet', ['dev-certs','https','--export-path',        certFilePath,'--format','Pem','--no-password',    ], { stdio: 'inherit', }).status) {        throw new Error("Could not create certificate.");    }}const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :    env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'https://localhost:7194';// https://vitejs.dev/config/export default defineConfig({    plugins: [plugin()],    resolve: {        alias: {'@': fileURLToPath(new URL('./src', import.meta.url))        }    },    server: {        proxy: {'^/weatherforecast': {                target,                secure: false            }        },        port: 5173,        https: {            key: fs.readFileSync(keyFilePath),            cert: fs.readFileSync(certFilePath),        }    }})

EDIT 3: So I'm getting something JSON-like in Postman, but now Swagger and Postman think the return type is text/plain, rather than application/json. If I take out the check for application/json, the JSX proceeds, buuuuut...why am I getting text/plain instead of application/json?

EDIT 4 (or was it 6?) I found I forgot FKs between two tables so I had EF Core Power Tools regen my Models and controllers. Edited the C# above. Swagger? Perfect, down to getting application/json! Postman and React? 500 error, just says "Internal Server Error."

I guess the more specific question is why Swagger's getting valid returns and nothing else is.Screenshot for Vite


Viewing all articles
Browse latest Browse all 3667

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>