'HttpRequest' does not contain a definition for 'CreateResponse' and no accessible extension method - asp.net-core

The below is my code. It looks HttpRequest could not able to access CreateResponse. Kindly help.
using System;
using System.Net;
using System.Net.Http;
using Microsoft.AspNetCore.Mvc;
namespace Abc.Controllers
{
[ApiController]
[Route("[controller]")]
public class PaymentController : Controller
{
public HttpResponseMessage Post()
{
// ... do the job
// now redirect
var response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri("http://www.abcmvc.com");
return response;
}
}
}

HttpResponseMessage and Request.CreateResponse are legacy ways to produce a HTTP response from older ASP.NET days, which do not apply to ASP.NET Core. If you have an ASP.NET Core application, you should use the mechanisms of ASP.NET Core, in particular the action results, to produce responses.
In your case, if you want to produce a redirect to some other location, then you can do it like this in ASP.NET Core:
public IActionResult Post()
{
// ... do the job
return RedirectPermanent("http://www.abcmvc.com");
}
This uses the RedirectPermanent utility method to create a RedirectResult.

Related

415 Unsupported Media Type in ASP.NET core web api

I am trying to experiment with asp.net core web api so I made some simple api with a controller like this:
[ApiController]
[Route("MyController")]
public class MyController : ControllerBase
{
[HttpGet]
[Route("GetResult")]
public IActionResult GetResult(string param1, string param2= null, SomeClassObj obj = null)
{ .... }
}
I ran the api locally and sent this postman GET request:
https://localhost:5001/MyController/GetResult?param1=someString
I got the error: 415 Unsupported Media Type
What am I missing here so it could work?
I was getting the same error after invoking the WEB API from .NET MVC.
As suggested by #zhulien, I have changed from [FromBody] to [FromForm] in WebAPI, it works fine for me.
.NET Core WebAPI method.
public async Task<IActionResult> Login([FromForm] LoginModel loginInfo)
{ // JWT code here }
.Net Core MVC Action Method.
public async void InvokeLoginAPIAsync(string endPoint, string userName, string pwd)
{
configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
baseUrl = configuration["Application:BaseAPI"] ?? throw new Exception("Unable to get the configuration with key Application:BaseAPI");
string targetUrl = string.Format("{0}/{1}", baseUrl, endPoint);
using (HttpClient deviceClient = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, targetUrl);
var data = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("userName", userName),
new KeyValuePair<string, string>("password", pwd)
};
request.Content = new FormUrlEncodedContent(data);
using (var response = await deviceClient.SendAsync(request))
{
if (response.StatusCode == HttpStatusCode.OK)
{
TempData["Response"] = JsonConvert.SerializeObject(response.Content);
}
}
}
}
Which version of .NET Core are you using?
Try doing the request from the browser and see if you have the same result.
Also, are you sure you're doing a GET and not a POST request in Postman? You shouldn't get 415 errors for GET requests, especially when you're not sending any body.
This error mainly occurs when you try to send a body and you haven't specified the media-type through the Content-Type header.
Ensure that the request is GET and your body is empty.
Solution after post edit:
As you're trying to parse a DTO object(SomeClassObj), you should specify where the values should come from. In order to fix your specific case, add the [FromQuery] attribute before SomeClassObj.
Your code should look like this:
[ApiController]
[Route("MyController")]
public class MyController : ControllerBase
{
[HttpGet]
[Route("GetResult")]
public IActionResult GetResult(string param1, string param2= null, [FromQuery]SomeClassObj obj = null)
{ .... }
}
This tells the parser to fetch the data from the query string. This will fix the 415 issue. However, if you want to bind to complex types, especially on get, checkout those topics: ASP.NET CORE 3.1 Model Binding and this issue as you will most probably encounter issues with parsing your DTO object.
Use [FromForm] attribute before each argument in the controller function.

Serve both REST and GraphQL APIs from .NET Core application

I have a .NET Core REST API server that is already serving customers.
Can I configure the API server to also support GraphQL by adding the HotChocolate library and defining the queries? Is it OK to serve both GraphQL and REST APIs from my .NET Core server?
Yes, supporting both REST APIs (controllers) and GraphQL is totally OK.
If you take libraries out of the picture, handling a GraphQL request just means handling an incoming POST to /graphql.
You can write a typical ASP.NET Core controller that handles those POSTs, if you want. Frameworks like Hot Chocolate provide middleware like .UseGraphQl() that make it more convenient to configure, but conceptually you can think of .UseGraphQl() as adding a controller that just handles the /graphql route. All of your other controllers will continue to work just fine!
There is a way you can automate having both APIs up and running at the same time using hotchocolate and schema stitching.
Basically I followed this tutorial offered by Ian webbington.
https://ian.bebbs.co.uk/posts/LessReSTMoreHotChocolate
Ian uses swagger schema from its API to create a graphql schema which saves us time if we think about it. It's easy to implement, however you still need to code to expose graphql endpoints.
This is what I implemented to connect all my graphql and rest APIs in just one API gateway. I'm sharing my custom implementation to have swagger schema (REST) running under hotchocolate (Graphql):
using System;
using HotChocolate;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Playground;
using HotChocolate.AspNetCore.Voyager;
using HotChocolate.AspNetCore.Subscriptions;
using HotChocolate.Stitching;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using SmartGateway.Api.Filters;
using SmartGateway.Api.RestServices.SmartConfig;
namespace SmartGateway.Api.Extensions
{
public static class GraphQlExtensions
{
public static IServiceCollection AddGraphQlApi(this IServiceCollection services)
{
services.AddHttpClient("smartauth", (sp, client) =>
{
sp.SetUpContext(client); //GRAPHQL API
client.BaseAddress = new Uri(AppSettings.SmartServices.SmartAuth.Endpoint);
});
services.AddHttpClient("smartlog", (sp, client) =>
{
sp.SetUpContext(client); //GRAPHQL API
client.BaseAddress = new Uri(AppSettings.SmartServices.SmartLog.Endpoint);
});
services.AddHttpClient("smartway", (sp, client) =>
{
sp.SetUpContext(client); //GRAPHQL API
client.BaseAddress = new Uri(AppSettings.SmartServices.SmartWay.Endpoint);
});
services.AddHttpClient<ISmartConfigSession, SmartConfigSession>((sp, client) =>
{
sp.SetUpContext(client); //REST API
client.BaseAddress = new Uri(AppSettings.SmartServices.SmartConfig.Endpoint);
}
);
services.AddDataLoaderRegistry();
services.AddGraphQLSubscriptions();
services.AddStitchedSchema(builder => builder
.AddSchemaFromHttp("smartauth")
.AddSchemaFromHttp("smartlog")
.AddSchemaFromHttp("smartway")
.AddSchema(new NameString("smartconfig"), SmartConfigSchema.Build())
.AddSchemaConfiguration(c =>
{
c.RegisterExtendedScalarTypes();
}));
services.AddErrorFilter<GraphQLErrorFilter>();
return services;
}
public static IApplicationBuilder UseGraphQlApi(this IApplicationBuilder app)
{
app.UseWebSockets();
app.UseGraphQL("/graphql");
app.UsePlayground(new PlaygroundOptions
{
Path = "/ui/playground",
QueryPath = "/graphql"
});
app.UseVoyager(new PathString("/graphql"), new PathString("/ui/voyager"));
return app;
}
}
}
Set up HttpContext extension:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace SmartGateway.Api.Extensions
{
public static class HttpContextExtensions
{
public static void SetUpContext(this IServiceProvider servicesProvider, HttpClient httpClient)
{
HttpContext context = servicesProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
if (context?.Request?.Headers?.ContainsKey("Authorization") ?? false)
{
httpClient.DefaultRequestHeaders.Authorization =
AuthenticationHeaderValue.Parse(context.Request.Headers["Authorization"].ToString());
}
}
}
}
You need this to handle and pass the HTTPClient to your swagger Sdk.
using System.Net.Http;
namespace SmartGateway.Api.RestServices.SmartConfig
{
public interface ISmartConfigSession
{
HttpClient GetHttpClient();
}
public class SmartConfigSession : ISmartConfigSession
{
private readonly HttpClient _httpClient;
public SmartConfigSession(HttpClient httpClient)
{
_httpClient = httpClient;
}
public HttpClient GetHttpClient()
{
return _httpClient;
}
}
}
This is my graphql Schema:
namespace SmartGateway.Api.RestServices.SmartConfig
{
public static class SmartConfigSchema
{
public static ISchema Build()
{
return SchemaBuilder.New()
.AddQueryType<SmartConfigQueries>()
.AddMutationType<SmartConfigMutations>()
.ModifyOptions(o => o.RemoveUnreachableTypes = true)
.Create();
}
}
public class SmartConfigMutations
{
private readonly ISmartConfigClient _client;
public SmartConfigMutations(ISmartConfigSession session)
{
_client = new SmartConfigClient(AppSettings.SmartServices.SmartConfig.Endpoint, session.GetHttpClient());
}
public UserConfigMutations UserConfig => new UserConfigMutations(_client);
}
}
Finally, this is how you publish endpoints:
using System.Threading.Tasks;
using SmartConfig.Sdk;
namespace SmartGateway.Api.RestServices.SmartConfig.UserConfigOps
{
public class UserConfigMutations
{
private readonly ISmartConfigClient _client;
public UserConfigMutations(ISmartConfigClient session)
{
_client = session;
}
public async Task<UserConfig> CreateUserConfig(CreateUserConfigCommand createUserConfigInput)
{
var result = await _client.CreateUserConfigAsync(createUserConfigInput);
return result.Response;
}
public async Task<UserConfig> UpdateUserConfig(UpdateUserConfigCommand updateUserConfigInput)
{
var result = await _client.UpdateUserConfigAsync(updateUserConfigInput);
return result.Response;
}
}
}
More documentation about hotchocolate and schema stitching here:
https://hotchocolate.io/docs/stitching

API Response is 200 ok when using HttpResponseMessage(HttpStatusCode.BadRequest)

I was using the following class in my API and it appears that returning BadRequest does not return a BadRequest in the actual response. Only in the content of the message. How to make it return 400 in the "correct" place?
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace KardexAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TestController : Controller
{
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
}
I solved this personally by switching to
IActionResult
instead of
HttpResponseMessage
And then
return BadRequest();
But I am still curious how to solve the above issue.

.NET CORE web api Changes not detected

I'm currently working on a VueJS app using .NET Core 2.2 for the back-end part.
I was working on it for a few months but it suddenly stopped working like a charm when I updated from 2.0 to 2.2.
My web API changes are not detected and I don't know why.
For instance, I have a few controllers and whenever I change them, and then use the web API, the changes are not made. I can even delete the whole file and the web API using this file will still be working!
Another problem I get is that when I create new controller files, it's not detected; I'm stuck with my old controllers, which I'm not able to update.
Others files updates are detected (at least if I change the VueJS front-end)
I can also change the providers, delete whatever file used for the web API, changes are not detected. It may be a configuration issue?
Is there anything I could try to make things update again?
Update: I can change whatever I want in the back-end and it will do nothing. Compilations errors are the only problem I have to care about, it's like the app doesn't use the code anymore.
Here is an example I can provide:
I have a controller InterventionController which retrieve data about operations (I am french in a french context so variables names, etc will be in french) :
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Vue2Spa.Models;
using Vue2Spa.Providers;
namespace Vue2Spa.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class InterventionController : Controller
{
private readonly IInterventionProvider interventionProvider;
public InterventionController(IInterventionProvider interventionProvider)
{
this.interventionProvider = interventionProvider;
}
[HttpGet("[action]")]
public IActionResult Interventions([FromQuery(Name = "from")] int from = 0, [FromQuery(Name = "to")] int to = 5000)
{
var quantity = to - from;
if (quantity <= 0)
{
return BadRequest("La quantité doit être positive !");
}
else if (from < 0)
{
return BadRequest("Vous devez spécifier un indice de départ non nul !");
}
var allInterventions = interventionProvider.GetInterventions();
var result = new
{
TotalInterventions = allInterventions.Count,
Interventions = allInterventions.Skip(from).Take(quantity).ToArray()
};
return Ok(result);
}
}
// Others methods not useful for my example
}
It calls a provider which has the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Vue2Spa.Models;
namespace Vue2Spa.Providers
{
public class DBInterventionProvider : IInterventionProvider
{
private List<Intervention> interventions { get; set; }
DbContextOptionsBuilder<DepouillementTestContext> optionsBuilder = new DbContextOptionsBuilder<DepouillementTestContext>();
public DBInterventionProvider()
{
optionsBuilder.UseSqlServer(credentials); // Credentials are correct but not including it there for obvious reasons
using (var context = new LECESDepouillementTestContext(optionsBuilder.Options))
{
interventions = context.Intervention.ToList();
}
}
public List<Intervention> GetInterventions()
{
using (var context = new LECESDepouillementTestContext(optionsBuilder.Options))
{
interventions = context.Intervention.ToList();
}
return interventions;
}
// Others methods not useful for this example
}
}
I can delete these files, and I'm still able to access my operations web API
If needed, here is my startup.cs file:
using System;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Vue2Spa.Models;
namespace Vue2Spa
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
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)
{
// Add framework services.
services.AddMvc();
// Additional code for SQL connection
services.AddDbContext<DepouillementTestContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.
MigrationsAssembly(
typeof(Startup).
GetTypeInfo().
Assembly.
GetName().Name);
//Configuring Connection Resiliency:
sqlOptions.
EnableRetryOnFailure(maxRetryCount: 5,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
// Changing default behavior when client evaluation occurs to throw.
// Default in EFCore would be to log warning when client evaluation is done.
options.ConfigureWarnings(warnings => warnings.Throw(
RelationalEventId.QueryClientEvaluationWarning));
});
// Provider pour les interventions
services.AddSingleton<Providers.IInterventionProvider, Providers.DBInterventionProvider>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Webpack initialization with hot-reload.
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
}
}
Thanks in advance,
Well I've found why I had this problem, and I feel kinda dumb for that but well, it's working now.
I didn't change my launch.json when I upgraded from .NETCORE 2.0 to 2.2, all I had to do was changing
"program": "${workspaceFolder}/content/bin/Debug/netcoreapp2.0/Vue2Spa.dll",
by
"program": "${workspaceFolder}/content/bin/Debug/netcoreapp2.2/Vue2Spa.dll",
For more informations, see : https://learn.microsoft.com/en-us/aspnet/core/migration/21-to-22?view=aspnetcore-2.2&tabs=visual-studio

Can we use Microsoft.AspNet.WebApi.Client from an ASP.NET Core application?

We want to be able to use the package Microsoft.AspNet.WebApi.Client from our ASP.NET Core MVC web application to make an HTTP call to an outside system. It does work but I couldn't find the corresponding source code in .NET core (github). Is it okay to use this library from the ASP.NET road map point of view? Will it be supported in ASP.NET Core going forward? Most importantly, will this package be supported in non-Windows platforms, as part of ASP.NET Core/.NET Core?
You can try what I did for a REST Client. I found that the assembly you have mentioned in it's latest version does not work in the recently released ASP.Net Core 1.0. Instead of "Microsoft.AspNet.WebApi.Client", use "System.Net.Http".
Then where you would have built an Http POST request like this:
using AvailabilityPricingClient.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AvailabilityPricingClient.Core.Model;
using System.Net.Http;
using System.Net.Http.Headers;
namespace AvailabilityPricingClient.Client
{
public class ProductAvailabilityPricing : IProductAvailabilityPricing
{
private HttpClient _client;
public ProductAvailabilityPricing(string apiUrl)
{
_client = new HttpClient();
_client.BaseAddress = new Uri(apiUrl);
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void Dispose()
{
_client.Dispose();
}
public async Task<IEnumerable<Availablity>> GetAvailabilityBySkuList(IEnumerable<string> skuList)
{
HttpResponseMessage response = _client.PostAsJsonAsync("/api/availabilityBySkuList", skuList).Result;
if (response.IsSuccessStatusCode)
{
var avail = await response.Content.ReadAsAsync<IEnumerable<Availablity>>();
return avail;
}
return null;
}
}
}
You will now build like this:
using AvailabilityPricingClient.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AvailabilityPricingClient.Core.Model;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
namespace AvailabilityPricingClient.Client
{
public class ProductAvailabilityPricing : IProductAvailabilityPricing
{
private HttpClient _client;
public ProductAvailabilityPricing(string apiUrl)
{
_client = new HttpClient();
_client.BaseAddress = new Uri(apiUrl);
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public void Dispose()
{
_client.Dispose();
}
public async Task<IEnumerable<Availablity>> GetAvailabilityBySkuList(IEnumerable<string> skuList)
{
var output = JsonConvert.SerializeObject(skuList);
HttpContent contentPost = new StringContent(output, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = _client.PostAsync("/api/availabilityBySkuList", contentPost).Result;
if (response.IsSuccessStatusCode)
{
var avail = await response.Content.ReadAsStringAsync()
.ContinueWith<IEnumerable<Availablity>>(postTask =>
{
return JsonConvert.DeserializeObject<IEnumerable<Availablity>>(postTask.Result);
});
return avail;
}
return null;
}
}
}
This way you interface does not change only the body of your request code changes.
This is working for me....Good luck....