'Call external api between C# and ReactJS status 200. VM100:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0

Instead of Json with movie list it returns Status Code: 200 OK and a HTML response as outlined below. Screenshot from postman attached. I can't seem to figure out how to properly return list of movies from the api when I go to e.g. https://localhost:3000/movie/Scream. Not sure if I have exhausted all the options when it comes to CORS. It feels like there might be some issue with headers and I am not using them correctly. Any suggestions?

Code:

Reactjs frontend:

async getMovieData(title) {
    const response = await fetch('movie/' + title);
    const data = await response.json();
    this.setState({ movieData: data});
}

C# .NET6

service:

public async Task<List<MovieModel>> GetMovieSearchListAsync(string title)
{
    string url;
    if (!string.IsNullOrWhiteSpace(title))
    {
        url = string.Format("https://www.omdbapi.com/?apikey=#####&s={0}", title);

    }
    else
    {
        throw new MovieAppExceptions();
    }

    var client = _httpFactory.CreateClient("Movie");

    client.BaseAddress = new Uri(url);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Add("Content-Type", "application/json; charset=utf-8");

    var request = new HttpRequestMessage(HttpMethod.Get, url);

    var response = await client.SendAsync(request);

    var movieDataList = new List<MovieModel>();

    if (response.IsSuccessStatusCode)
    {
        using var contentStream = await response.Content.ReadAsStreamAsync();

        var movieModelDataResponse = await JsonSerializer.DeserializeAsync<MovieResponseModel>(contentStream);

        if (movieModelDataResponse != null)
        {
            foreach (Search model in movieModelDataResponse.Search)
            {
                movieDataList.Add(new MovieModel
                {
                    Id = Guid.NewGuid(),
                    Title = model.Title,
                    Year = model.Year,
                    ImdbID = model.ImdbID,
                    Type = model.Type,
                    Poster = model.Poster,
                });
            }
        }
        return movieDataList;
    }
    else
    {
        return movieDataList;
        //throw new MovieAppExceptions(response.StatusCode, "Error response from the Api: " + response.ReasonPhrase);
    }
}

Controller:

[HttpGet(Name = "GetMovies")]
public async Task<ActionResult<IEnumerable<MovieModel>>> Get(string title)
{
    try
    {
        if (string.IsNullOrWhiteSpace(title))
        {
            _logger.LogError("Parameter is missing.");
            return BadRequest("Parameter is missing.");
        }

        List<MovieModel> movieDataList;

        movieDataList = await _movieService.GetMovieSearchListAsync(title);

        return movieDataList;

    }
    catch (Exception ex)
    {
        return StatusCode(500, "Internal server error");
    }
}

Program.cs:

   var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddHttpClient();
    builder.Services.AddScoped<IMovieService, MovieService>();
    //builder.Services.AddSingleton<ILoggerManager, LoggerManager>();
    
    
    builder.Services.AddControllers();
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    builder.Services.AddTransient<IRepositoryWrapper, RepositoryWrapper>();
    
    
    var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
    builder.Services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
            builder => builder.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .WithOrigins("https://www.omdbapi.com/"));
    });
    
    //builder.Services.AddCors(options =>
    //{
    //    options.AddPolicy(name: MyAllowSpecificOrigins,
    //                      policy =>
    //                      {
    //                          policy.WithOrigins("https://www.omdbapi.com/");
    //                      });
    //});
    
    
    var connectionString = builder.Configuration.GetConnectionString("MovieContext");
    
    builder.Services.AddDbContext<MovieContext>(options =>
            options.UseSqlServer(connectionString));
    //builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    //    .AddEntityFrameworkStores<MovieContext>();
    
    
    // Auto Mapper Configurations
    MapperConfig autoMapperConfig = new MapperConfig();
    var mapper = autoMapperConfig.Initialize();
    builder.Services.AddSingleton(mapper);
    
    
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    
    app.UseCors(MyAllowSpecificOrigins);
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();]

MovieResponseModel

public class MovieResponseModel
{
    public Guid Id { get; internal set; }

    [JsonProperty("Search")]
    public List<Search>? Search { get; set; }

    [JsonProperty("totalResults")]
    public string? TotalResults { get; set; }

    [JsonProperty("Response")]
    public string? Response { get; set; }
}

public class Search
{
    [JsonProperty("Title")]
    public string? Title { get; set; }

    [JsonProperty("Year")]
    public string? Year { get; set; }

    [JsonProperty("imdbID")]
    public string? ImdbID { get; set; }

    [JsonProperty("Type")]
    public string? Type { get; set; }

    [JsonProperty("Poster")]
    public string? Poster { get; set; }
}

Console:

VM100:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0

Api: https://www.omdbapi.com and it's response:

{
Search: [
{
Title: "Scream",
Year: "1996",
imdbID: "tt0117571",
Type: "movie",
Poster: "https://m.media-amazon.com/images/M/MV5BMjA2NjU5MTg5OF5BMl5BanBnXkFtZTgwOTkyMzQxMDE@._V1_SX300.jpg"
},
{
Title: "Scream 2",
Year: "1997",
imdbID: "tt0120082",
Type: "movie",
Poster: "https://m.media-amazon.com/images/M/MV5BMDNlM2E2OTUtZTRhZi00ZDU1LWIxODAtN2E5OGZiNmIwMDIwXkEyXkFqcGdeQXVyMTQxNzMzNDI@._V1_SX300.jpg"
},
{
Title: "Scream 4",
Year: "2011",
imdbID: "tt1262416",
Type: "movie",
Poster: "https://m.media-amazon.com/images/M/MV5BMTk5OTcxNTUyNF5BMl5BanBnXkFtZTcwMDczMzE0NA@@._V1_SX300.jpg"
},
{
Title: "Scream 3",
Year: "2000",
imdbID: "tt0134084",
Type: "movie",
Poster: "https://m.media-amazon.com/images/M/MV5BMDljNmI1YzctNjJlZC00NzZlLWFlZTgtMDE4MjJiMDk0ZGY4XkEyXkFqcGdeQXVyMjg3MDQ0Mjk@._V1_SX300.jpg"
}
],
totalResults: "426",
Response: "True"
}

From Devtools

Headers:

General:

Request URL: https://localhost:3000/movie/Scream
Request Method: GET
Status Code: 200 OK
Remote Address: 127.0.0.1:3000
Referrer Policy: strict-origin-when-cross-origin

Response Headers:

Accept-Ranges: bytes
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: *
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Mon, 11 Apr 2022 08:46:30 GMT
ETag: W/"6af-+M4OSPFNZpwKBdFEydrj+1+V5xo"
Keep-Alive: timeout=5
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: Express

Request Headers:

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: keep-alive
Host: localhost:3000
Referer: https://localhost:3000/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36

Preview: You need to enable JavaScript to run this app.

Response:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="/manifest.json" />
    <!--
      Notice the use of  in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  <script defer src="/static/js/bundle.js"></script></head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source