0

I have an ASP.NET Core 5 Web API that delivers some data for two applications. One is an Angular SPA, and this set up works just fine - the Angular app gets its data, can display it, all seems swell.

My other app is an ASP.NET Core 5 MVC app with Razor pages. I can log in using the Microsoft Identity system and OpenID Connect - the user is properly discovered and everything seems fine - but when I make an API call, I get back a "HTTP 406 - Not Acceptable" error.

From my research, this seems to be caused most likely be a mismatch of the API not delivering the kind of data the caller can handle. Which is quite puzzling to me, since my API delivers JSON (and with the Angular app, it works just fine), and my MVC app also expects JSON - so I'm left scratching my head wondering what I might have missed.....

My Web API is set up like this (Startup/ConfigureServices):

services.AddAuthentication(x => {
                                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                                })
        .AddJwtBearer(x => {
                                x.RequireHttpsMetadata = false;
                                x.SaveToken = true;
                                x.TokenValidationParameters = new TokenValidationParameters
                                         {
                                               ValidateIssuerSigningKey = false,
                                               ValidateIssuer = false,
                                               ValidateAudience = true,
                                               ValidAudience = Configuration.GetValue<string>("AzureAd:Audience"),
                                               ValidateLifetime = true,
                                               ClockSkew = TimeSpan.Zero
                                         };
                           });

services.AddMvc();
services.AddControllers().AddJsonOptions(options => { 
                                                        options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
                                                    });

and its controllers all specify the [Produces("application/json")] annotation at the controller level:

[Route("api/data")]
[ApiController]
[Produces("application/json")]
[Authorize]
public class DataController : MyBaseController
{
    // code for the controller

    // this is one of the action methods
    [HttpGet]
    [Route("review")]
    public IActionResult GetListForReview()
    {
        _logger.LogInformation("Get Review List");

        IEnumerable<OverviewListDto> data = _repository.GetOverviewListForReview();
        return Ok(data);
    }   
}

So I'm kind thinking this is all I can do to ensure I'm sending back proper JSON.

My MVC app is using the .NET Core 5 HttpClient and I set this up like this:

_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(_apiConfig.BaseUrl);

_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _apiConfig.AccessToken);

so I am kinda expecting that getting and parsing JSON ought to work just fine - or am I mistaken? What else could cause such a "HTTP 406 - Not Acceptable" error? What more can I check / make sure of?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • Can you share the signature of the endpoint you are trying to hit? Is it more than one endpoint? – Ben Matthews Sep 20 '21 at 14:12
  • @BenMatthews: it's just a simple `GET` request (`GET https://myserver.abc/myapi/ctrl/getdata` - no params - just a simple call). It happens on multiple endpoints, so it doesn't look like it's a specific problem of a single endpoint / URL. – marc_s Sep 20 '21 at 14:13
  • 1
    That is odd... can you check the traffic and see what the accept headers are in the request? – Ben Matthews Sep 20 '21 at 14:15
  • Can you show your API action too pls? – Serge Sep 20 '21 at 14:37
  • @Serge: added one of the methods as a sample - they all look quite similar, basically all delegating the actual work to a repository class, which just returns a list of DTOs that match whatever criteria might be applied for any given call – marc_s Sep 20 '21 at 14:41

2 Answers2

0

406 has nothing to do with authorization JWT. It is all about http response.

Did you try to use a Postman? If it success you can check what headers are Postman uses to get data.

I am using code like this for http get request. It works 100%

using (var client = new HttpClient())
{
    var baseAddress = "http://localhost:44365";

    var api = "/api/data/review";
    client.BaseAddress = new Uri(baseAddress);
    var contentType = new MediaTypeWithQualityHeaderValue("application/json");
    client.DefaultRequestHeaders.Accept.Add(contentType);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    var response = await client.GetAsync(api)
      //var statusCode = response.StatusCode.ToString();
    if (response.IsSuccessStatusCode)
    {
        var stringData = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<object>(stringData);
    }
}

I can't see var response = await client.GetAsync(api) in your code. So be sure that you have a right api path, you can call a different action then you want. And check http or https you need.

Serge
  • 40,935
  • 4
  • 18
  • 45
0

When you do a request the expected type for the response should be set in the Content-Type header.

You can do like this (source: https://stackoverflow.com/a/10679340/8404545)

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com/");
client.DefaultRequestHeaders
      .Accept
      .Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT 
header

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, 
"relativeAddress");
request.Content = new StringContent("{\"name\":\"John Doe\",\"age\":33}",
                                    Encoding.UTF8, 
                                    "application/json");//CONTENT-TYPE header

client.SendAsync(request)
      .ContinueWith(responseTask =>
      {
          Console.WriteLine("Response: {0}", responseTask.Result);
      });
Max
  • 794
  • 3
  • 7
  • So are you saying getting a list of objects, returning them with `return Ok(data);`, and having a `[Produces("application/json")]` annotation on the controller class is not enough / not the proper way to go? This is on the **server-side** in the Web API - your code seems to be about the client-side (MVC app) and I'm not trying to send any data from there.... (just reading it) – marc_s Sep 20 '21 at 15:03
  • @marc_s Your take on what it does is right, but the error you are getting is the client saying that the data it is getting back is not matching the format that was requested. This client code specifies that it should want Json back. Also, on the server instead of Ok(Data), perhaps Json(Data) to be more explicit might help. – Ben Matthews Sep 20 '21 at 15:34