how to upload images using web api 2 REST API - asp.net-web-api2

I am new to web api 2. I tried a lot but did not find proper working code for uploading images in web api 2.
I have a working code that is working in web api (old), but in web api 2, this particular value is null. HttpContext.Current
Can someone provide me working code?

Here is the controller in one of my Web API 2 projects:
namespace WebApi.Controllers
{
public class FilesController : ApiController
{
// POST api/<controller>
public async Task<HttpResponseMessage> Post()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Debug.Listeners[0].WriteLine(file.Headers.ContentDisposition.FileName);
Debug.Listeners[0].WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
...
}
}
You can try by adding a Web API Controller class (v2.1).

Related

How to add SoapEndPoint after server is started on ASP.NET Core?

I'm using simple console app to expose a soap web service. It works as expected.
Now i want to add another web service after the server is started. How to make it work?
I have following simple console application:
static void Main(string[] args)
{
var host = WebApplication.CreateBuilder();
_App = host.Build();
_App.UseRouting();
_App.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface>("/SimpleService.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
_App.Urls.Add("http://*:5000");
_App.RunAsync();
Console.WriteLine("Server has been started successfully ...");
AddNewService();
Console.ReadLine();
}
Server starts and i can access the wsdl http://localhost:5000/SimpleService.asmx?wsdl)
Now the AddNewService method simple try to define a new SoapEndPoint after service started.
Code looks like this:
static private void AddNewService()
{
try
{
System.Threading.Thread.Sleep(5000); // Wait 5 seconds to make sure web application is running
Console.WriteLine("Adding new service ..."); // Add new Soap service now, after startup
_App?.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface2>("/SimpleService2.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
Console.WriteLine("Added new service.");
}
catch(Exception ex)
{
Console.WriteLine("Failed to Add new service. Error=" + ex.Message);
}
}
This works ok if first request to url is done after the service is created: (http://localhost:5000/SimpleService2.asmx?wsdl)
But if a request is sent before the service is created. Then any request done after the creation of the service will fail:
I'm guessing i need to raise some event or something to the web server to get it refreshed or something.
How can i do that?
Also is there a way to remove a SoapEndPoint once is has been defined/exposed?
Idea is basically being able to add/remove/update SoapEndPoint on the fly.
Any help will be appreciated. Thanks in advance
Progess a little bit on this.
I basically need to register IActionDescriptorChangeProvider class to be able to notify the web application.
I also needed to slightly change my main routine.
Here is the main function:
static void Main(string[] args)
{
var host = WebApplication.CreateBuilder();
host.Services.AddControllers();
host.Services.AddSingleton<IActionDescriptorChangeProvider>(MyActionDescriptorChangeProvider.Instance);
host.Services.AddSingleton(MyActionDescriptorChangeProvider.Instance);
host.Services.AddSingleton<ISimpleServiceInterface, SimpleService>();
_App = host.Build();
_App.MapControllers();
_App.UseRouting();
_App.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface>("/SimpleService.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
_App.Urls.Add("http://*:5000");
_App.RunAsync();
Console.WriteLine("Server has been started successfully ...");
AddNewService();
Console.ReadLine();
}
Then the AddService function (note the 2 lines added to make the notification):
static private void AddNewService()
{
try
{
System.Threading.Thread.Sleep(5000); // Wait 5 seconds to make sure web application is running
Console.WriteLine("Adding new service ..."); // Add new Soap service now, after startup
_App?.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface2>("/SimpleService2.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
// Notify the web application of the changes
MyActionDescriptorChangeProvider.Instance.HasChanged = true;
MyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();
Console.WriteLine("Added new service.");
}
catch(Exception ex)
{
Console.WriteLine("Failed to Add new service. Error=" + ex.Message);
}
}
and class implementing IActionDescriptorChangeProvider:
public class MyActionDescriptorChangeProvider : IActionDescriptorChangeProvider
{
public static MyActionDescriptorChangeProvider Instance { get; } = new MyActionDescriptorChangeProvider();
public CancellationTokenSource TokenSource { get; private set; } = new CancellationTokenSource();
public bool HasChanged { get; set; }
public IChangeToken GetChangeToken()
{
TokenSource = new CancellationTokenSource();
return new CancellationChangeToken(TokenSource.Token);
}
}
Once you do that, it will work fine on the second request (wsdl request).
Problem is that the wsdl may be accessible but the function itself (route to the actual method on the singleton) is not there.
Registration of the singleton for ISimpleServiceInterface2 need to be done but not sure how to achieve this.
My end goal is to be able to add/remove/update soap web service after server is built.
Basically idea is to update the soap service with a newer assembly.
If anybody has some idea, comments, response, please post them here. That will be appreciated.

Blazor server and API in same project, 404 not found when app.UserAuth is activate

I have a Blazor server project with some API controllers in same project.
In my Program.cs I have this code :
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
..
app.UseAuthentication();
app.UseAuthorization();
When I call my API from my blazor component I got 404 not found response.
If I comment out app.UseAuthentication() and app.UseAuthorization my component can call my API and it works.
I'm a newbie on auth and API and don't know where to start.
My API has no [Auth] tags in it. I can reach the API with Swagger without problems.
My code in component (it works without "UseAuth" but not when it's activate):
string filnamn = WebUtility.UrlEncode(fil.Namn);
string reqUri = $"delete/{filnamn}";
Http.BaseAddress = new Uri("https://localhost:7285/");
Http.DefaultRequestHeaders.Accept.Clear();
HttpResponseMessage response = await Http.DeleteAsync(reqUri);
My API controller :
[ApiController]
public class UploadController : ControllerBase
{
private readonly string grundPath = #"G:\Testfolder";
private readonly string ulPath = "Upload";
[HttpDelete("delete/{filename}")]
public IActionResult Delete(string filename)
{
try
{
var filePath = Path.Combine(grundPath, ulPath, filename);
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
return StatusCode(200);
}
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
return StatusCode(500);
}
Do you see some obvious wrong/missing part I do or could give me some direction on what I should google for?

Blazor WASM ViewModel

I did a lot of Razor pages the past year, and a couple of weeks ago I started to transform all to a ViewModel for my Blazor Server App.
Now I thought it's time to make a new Blazor WebAssembly App.
But I struggle to build a POC with a ViewModel, based on the WeatherForecast example.
But whatever I do, I have errors. And so far I did not find a a good basic example.
Unhandled exception rendering component: Unable to resolve service for type 'fm2.Client.Models.IFetchDataModel' while attempting to activate 'fm2.Client.ViewModels.FetchDataViewModel'.
System.InvalidOperationException: Unable to resolve service for type 'fm2.Client.Models.IFetchDataModel' while attempting to activate 'fm2.Client.ViewModels.FetchDataViewModel'.
Example: https://github.com/rmoergeli/fm2
namespace fm2.Client.ViewModels
{
public interface IFetchDataViewModel
{
WeatherForecast[] WeatherForecasts { get; set; }
Task RetrieveForecastsAsync();
Task OnInitializedAsync();
}
public class FetchDataViewModel : IFetchDataViewModel
{
private WeatherForecast[] _weatherForecasts;
private IFetchDataModel _fetchDataModel;
public WeatherForecast[] WeatherForecasts
{
get => _weatherForecasts;
set => _weatherForecasts = value;
}
public FetchDataViewModel(IFetchDataModel fetchDataModel)
{
Console.WriteLine("FetchDataViewModel Constructor Executing");
_fetchDataModel = fetchDataModel;
}
public async Task RetrieveForecastsAsync()
{
_weatherForecasts = await _fetchDataModel.RetrieveForecastsAsync();
Console.WriteLine("FetchDataViewModel Forecasts Retrieved");
}
public async Task OnInitializedAsync()
{
_weatherForecasts = await _fetchDataModel.RetrieveForecastsAsync();
}
}
}
namespace fm2.Client
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<IFetchDataViewModel, FetchDataViewModel>();
await builder.Build().RunAsync();
}
}
}
Additional note:
Here how I did it previously for Blazor Server App: https://github.com/rmoergeli/fm2_server
Here I try the same for the Blazor WebAssembly App:
https://github.com/rmoergeli/fm2_wasm (Constructor is not initialized).
This POC is different comapred to the first link at the top. Here I tried to just do the same like I did for the Blazor Server App.
I pulled the latest code from Github. It looks like the wrong api was getting called.
When I changed from this:
WeatherForecast[] _weatherForecast = await _http.GetFromJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
to this:
WeatherForecast[] _weatherForecast = await _http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
in WeatherViewModel.cs
I could get the weather data to be displayed.

Multiple Access Denied Pages

I'm creating an application that has two different authorization scenarios: admin and site.
If you try to access a /admin route without the policy succeeding the user should be redirected to an access denied page. At this point there's no action the user can take. They can't access the resource and there's nothing for them to do.
If you try to access a /site/joes-super-awesome-site route without the policy suceeding the user should be redirected to a different access denied. At this point the user should be able to request access. There is an action they can take.
What's the best way to achieve this? I know I can override the default OnRedirectToAccessDenied action but that will require some ugly string parsing (untested example below).
.AddCookie(options => {
options.Events.OnRedirectToAccessDenied = context => {
// parsing this kinda sucks.
var pathParts = context.Request.Path.Value.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (pathParts?[0] == "site") {
context.Response.Redirect($"/{pathParts[0]}/request-access");
} else {
context.Response.Redirect("/account/access-denied");
}
return Task.CompletedTask;
};
})
Doing some searching, I found the following information:
Someone with the same question on this GitHub issue
Tracking of authorization-related improvements in this GitHub issue
Unfortunately these improvements didn't make it to ASP.NET Core 2.1.
It seems that at this point, another option (apart from your suggestion of parsing the request URL) is to imperatively invoke the authorization service in your MVC actions.
It could go from:
// Highly imaginary current implementation
public class ImaginaryController : Controller
{
[HttpGet("site/{siteName}")]
[Authorize("SitePolicy")]
public IActionResult Site(string siteName)
{
return View();
}
[HttpGet("admin")]
[Authorize("AdminPolicy")]
public IActionResult Admin()
{
return View();
}
}
to:
public class ImaginaryController : Controller
{
private readonly IAuthorizationService _authorization;
public ImaginaryController(IAuthorizationService authorization)
{
_authorization = authorization;
}
[HttpGet("site/{siteName}")]
public Task<IActionResult> Site(string siteName)
{
var sitePolicyAuthorizationResult = await _authorization.AuthorizeAsync(User, "SitePolicy");
if (!sitePolicyAuthorizationResult.Success)
{
return Redirect($"/site/{siteName}/request-access");
}
return View();
}
[HttpGet("admin")]
public Task<IActionResult> Admin()
{
var adminPolicyAuthorizationResult = await _authorization.AuthorizeAsync(User, "AdminPolicy");
if (!adminPolicyAuthorizationResult.Success)
{
return Redirect("/account/access-denied");
}
return View();
}
}

Add Web Api controllers to an existing ASP.NET 4 web application

Following the steps from this question How to add Web API to an existing ASP.NET MVC 4 Web Application project? , I have added web api support to my application.
In my original scenario I have the following web mvc controller:
public class FranchiseController : Controller
{
public ActionResult List()
{
return View();
}
[DataContext]
public ActionResult GetAllFranchises()
{
var franchiseInfoViewModelList = new List<FranchiseInfoViewModel>();
var franchiseInfoList = _franchiseService.GetAll();
foreach (var franchiseInfo in franchiseInfoList)
{
franchiseInfoViewModelList.Add(new FranchiseInfoViewModel(franchiseInfo, p => p.IsImportant));
}
var jsonNetResult = new JsonNetResult
{
Formatting = Formatting.Indented,
Data = franchiseInfoViewModelList
};
return jsonNetResult;
}
}
When the user navigates to the List view, I am calling
$.getJSON("/franchise/GetAllFranchises")
.done(function (data) {
});
to go to the GetAllFranchises action method and return the json data. So far so good.
I have created the following web api controller:
public class FranchiseController : ApiController
{
public IEnumerable<FranchiseInfoViewModel> GetAllFranchises()
{
var allFranchises = new List<FranchiseInfoViewModel>();
var franchiseInfoList = _franchiseService.GetAll();
foreach (var franchiseInfo in franchiseInfoList)
{
allFranchises.Add(new FranchiseInfoViewModel(franchiseInfo, p => p.IsImportant));
}
return allFranchises;
}
}
and I am trying to get to its action method like this:
$.getJSON("api/franchise")
.done(function (data) {
});
I am getting 404 error and the app is trying to reach the following url:
/Franchise/api/franchise
instead of api/franchise.
Global Asax:
protected void Application_Start()
{
Log.StartSession();
ElmahExtension.SetCurrentApplication(this);
ViewEngines.Engines.Add(new OmegaViewEngine());
AreaRegistration.RegisterAllAreas();
SerializerConfig.Register(GlobalConfiguration.Configuration);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
Bootstrapper.Initialise();
FluentValidationModelValidatorProvider.Configure();
}
Default route:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
In my Controllers folder I have my web mvc controller:
Controller\FranchiseController
and I have made a new folder WebAPI to hold my web api controller
Controller\WebAPI\FranchiseController
What am I doing wrong ? Thanks!
I'm not sure if it's the right move to name "FranchiseController" both to the MVC Internet Application Controller and to the MVC Web API Controller (totally different things). After renaming one of them I think you should put the Web Api Controller in the root of the directory (Controller).