Hi,
Before telling you about my issue, I want to show my gratitude and appreciation for this product, which I have recently discovered, and what I have been able to see really pleased very much. Thank you very much!!!
And now the issue.
I am trying to create web pages from content obtained through requests to the GraphQL API. The CMS project is published on an IIS Server and I make successful requests to the API from Postman. However, when I try to fetch the API from JavaScript (hosted in the same server) I cannot get the content.
I have made serveral tests changing the JavaScript code with no success:
const url = "./cms/api/graphql";
const opts = {
method: 'POST',
mode: 'no-cors',
credentials: 'include',
//credentials: 'omit',
//headers: { 'Content-Type': 'application/json' },
headers: { 'Content-Type': 'application/graphql' },
body: JSON.stringify({"query": myQuery})
//body: JSON.stringify({ query: myQuery })
//body: myQuery
};
//require('isomorphic-fetch');
fetch(url, opts)
.then(function (response) {
return response.json();
})
.then(function (data) {
console.log(JSON.parse(data));
})
.catch(function (error) {
console.error('Unable to query api/graphql.', error);
});
The browser console shows this:
Failed to load resource: the server responded with a status of 400 (Bad Request)
SyntaxError: Unexpected end of JSON input
Going to the Event Viewer the request to the CMS raises the following error:
Category: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware
EventId: 1
RequestId: 8000aa2b-0000-eb00-b63f-84710c7967bb
RequestPath: /sala-prensa/cms/api/graphql
SpanId: |690af6cf-4c3e9963535f227e.
TraceId: 690af6cf-4c3e9963535f227e
ParentId:An unhandled exception has occurred while executing the request.
Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at OrchardCore.Apis.GraphQL.GraphQLMiddleware.ExecuteAsync(HttpContext context, ISchemaFactory schemaService) in C:projectsorchardcoresrcOrchardCore.ModulesOrchardCore.Apis.GraphQLGraphQLMiddleware.cs:line 146
at OrchardCore.Apis.GraphQL.GraphQLMiddleware.Invoke(HttpContext context, IAuthorizationService authorizationService, IAuthenticationService authenticationService, ISchemaFactory schemaService) in C:projectsorchardcoresrcOrchardCore.ModulesOrchardCore.Apis.GraphQLGraphQLMiddleware.cs:line 63
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at OrchardCore.Diagnostics.DiagnosticsStartupFilter.<>c__DisplayClass3_0.<b__1>d.MoveNext() in C:projectsorchardcoresrcOrchardCore.ModulesOrchardCore.DiagnosticsDiagnosticsStartupFilter.cs:line 36
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
Any help would be appreciated. Best regards.
You are sending a POST request which will require an antiforgery token.
Another option with graphql is to use GET requests, which is documented here:
https://docs.orchardcore.net/en/dev/docs/reference/modules/Apis.GraphQL/#get-request
Thank you very much for your answer and I am sorry I was late in answering.
I have create a MVC project and included the following code in ConfigureServices
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = "https://localhost:44396/connect/token";
options.ClientId = "e0f660a2cf2a47babac40a4a8c24e7e0";
options.ClientSecret = "76945d3917a4456db5a41fc2949d6439";
options.ResponseType = "code id_token";
options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
});
I include the js code to fetch the GraphQL API using a POST request. I tried several combinations
const opts = {
method: "POST",
mode: 'no-cors',
credentials: 'include',
//headers: { "Content-Type": "application/json" },
headers: { "Content-Type": "application/graphql" },
//body: JSON.stringify({query: myQuery1})
body: JSON.stringify({ query: myQuery1 })
//body: JSON.stringify({ "query": myQuery1 })
//body: myQuery1
};.
In the developer tools of the brower I see three cookies related to authoritation: orchauth_Default, orchantiforgery_Default and .AspNetCore.Antiforgery.QrPYhbOUQ0o.
The request fetched seems to include the authoritation cookies information:
General
Request URL: https://localhost:44396/api/graphql
Request Method: POST
Status Code: 500 Internal Server Error
Remote Address: [::1]:44396
Referrer Policy: no-referrer-when-downgradeResponse Headers
Content-Length: 4962
Content-Type: text/plain
Date: Sat, 30 May 2020 14:33:42 GMT
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NETRequest Headers
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en,es-ES;q=0.9,es;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 396
Content-Type: text/plain;charset=UTF-8
Cookie: _ga=GA1.1.625725641.1589660747; .AspNetCore.Antiforgery.QrPYhbOUQ0o=CfDJ8GAc_xY9fJ...; orchantiforgery_Default=CfDJ8Nx_bbMNqvtLnrArzxb7Ojys...; orchauth_Default=CfDJ8Nx_bbMNqvtLnrArzxb7Oj...
Host: localhost:44396
Origin: https://localhost:44326
Pragma: no-cache
Referer: https://localhost:44326/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
And the response is:
System.NullReferenceException: Object reference not set to an instance of an object.
at OrchardCore.Apis.GraphQL.GraphQLMiddleware.ExecuteAsync(HttpContext context, ISchemaFactory schemaService) in C:projectsorchardcoresrcOrchardCore.ModulesOrchardCore.Apis.GraphQLGraphQLMiddleware.cs:line 146
at OrchardCore.Apis.GraphQL.GraphQLMiddleware.Invoke(HttpContext context, IAuthorizationService authorizationService, IAuthenticationService authenticationService, ISchemaFactory schemaService) in C:projectsorchardcoresrcOrchardCore.ModulesOrchardCore.Apis.GraphQLGraphQLMiddleware.cs:line 70
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at OrchardCore.Diagnostics.DiagnosticsStartupFilter.<>c__DisplayClass3_0.<b__1>d.MoveNext() in C:projectsorchardcoresrcOrchardCore.ModulesOrchardCore.DiagnosticsDiagnosticsStartupFilter.cs:line 47
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
at OrchardCore.Modules.ModularTenantRouterMiddleware.Invoke(HttpContext httpContext) in C:projectsorchardcoresrcOrchardCoreOrchardCoreModulesModularTenantRouterMiddleware.cs:line 83
at OrchardCore.Environment.Shell.Scope.ShellScope.UsingAsync(Func`2 execute) in C:projectsorchardcoresrcOrchardCoreOrchardCore.AbstractionsShellScopeShellScope.cs:line 103
at OrchardCore.Modules.ModularTenantContainerMiddleware.Invoke(HttpContext httpContext) in C:projectsorchardcoresrcOrchardCoreOrchardCoreModulesModularTenantContainerMiddleware.cs:line 61
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)HEADERS
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en,es-ES;q=0.9,es;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 396
Content-Type: text/plain;charset=UTF-8
Cookie: _ga=GA1.1.625725641.1589660747; .AspNetCore.Antiforgery.QrPYhbOUQ0o=CfDJ8GAc_xY9fJ...; orchantiforgery_Default=CfDJ8Nx_bbMNqvtLnrArzxb7Ojys...; orchauth_Default=CfDJ8Nx_bbMNqvtLnrArzxb7Oj...
Host: localhost:44396
Pragma: no-cache
Referer: https://localhost:44326/
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Origin: https://localhost:44326
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: empty
However, making the request from Postman (including the Bearer token I get by requesting https://localhost:44396/connect/token) everything goes fine.
I also tried with the GET request. Everything goes fine with Postman but does not work when fetching from JavaScript.
Any idea about what I am doing wrong? I am kind of stuck
I am sorry if the question is silly, but I am new at these things. Thanks in advance!
Is your website the same one that is running the graphql service?
You need to be authenticated to do the queries. Unless you allow "Anonymous" to run the queries. I believe that in the case of Postman there is a valid auth cookie, but not as the user of your app.
We might want to create a guide showing how to configure an implement openid such that we can use apis from a web app.
The graphql service and the website are different web apps. We tried hosting them in the same server and in different servers.
After many attempts and thanks to the collaboration of a colleague we achieved a satisfactory solution. We don't solve everything in javascript, but the way it works is fine for us. Here is what we have done (assuming we included things that could be done better):
We followed this guide (https://medium.com/swlh/orchard-core-open-id-connect-and-graphql-f003a222260a) to configure OpenId.
We create our Net Core MVC app.
We include a service to get the token used in the GraphQL request.
public class TokenService : ITokenService
{
private readonly IHttpClientFactory _clientFactory;
private readonly IOptions<OrchardConfig> _config;
public TokenService(IOptions<OrchardConfig> config)
{
_config = config;
}
public async Task<string> GetTokenAsync()
{
IList<KeyValuePair<string, string>> nameValueCollection = new List<KeyValuePair<string, string>> {
{ new KeyValuePair<string, string>("client_id", _config.Value.ClientId) },
{ new KeyValuePair<string, string>("client_secret", _config.Value.ClientSecret) },
{ new KeyValuePair<string, string>("grant_type", _config.Value.GrantType) }
};
var client = new HttpClient();
var response = await client.PostAsync(_config.Value.UrlToken, new FormUrlEncodedContent(nameValueCollection));
var tokenResponse = await response.Content.ReadAsStringAsync();
return tokenResponse;
}
}
Where the UrlToken (url of the service to get the token
services.AddScoped<ITokenService, TokenService>();
public async Task<IActionResult> Index()
{
var token = JsonConvert.DeserializeObject<BearerToken>(await _tokenService.GetTokenAsync());
ViewBag.Token = $"{token.token_type} {token.access_token}";
ViewBag.UrlApi = _config.Value.UrlApi;
return View();
}
where BeareToken is a class we have created. We include in the ViewBag the toke and URL of the GraphQL service (also in the appsettings.json)
public class BearerToken
{
public string token_type { get; set; }
public string access_token { get; set; }
public int expires_in { get; set; }
}
<script>
var urlApi = "@ViewBag.UrlApi";
var myToken = "@ViewBag.Token";
</script>
const myQuery1 = `{
paginaNotasPrensa {
bag {
contentItems {
bla bla bla
}
}
}
}`;
const url = urlApi;
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", myToken);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: JSON.stringify({ query: myQuery1 })
};
fetch(url, requestOptions)
.then(function (response) {
return response.json();
})
.then(function (res) {
salaPrensa.drawNotasPrensa(res.data);
})
.catch(function (error) {
console.error('Unable to query api/graphql (POST).', error);
});