How return a yaml file as result of an asp.net core ViewComponent - asp.net-core

I want to create an asp.net core ViewComponent that dynamically return a yaml file based on some criteria:
For example
namespace MyNameSpace {
[ViewComponent(Name = nameof(MyViewComponent))]
public class MyViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(object input)
{
string yamlDocument = GetYamlDocumentByInput(input);
//how to proceed here so that my yamlDocument is returned with the right content type?
return View(..., yamlDocument);
}
}}

you could search the view component class,and there‘s no method can return a file as result.
you'd better add an action in your controller to download file,and you could send a request to this action after your view has been rendered mannully or automaticlly.
and there's the codes in the action:
public FileResult DownLoad(Person person)
{
var serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
var yaml = serializer.Serialize(person);
byte[] yamlArray = System.Text.Encoding.UTF8.GetBytes(yaml);
return File(yamlArray, "application/x-yml");
}
Result:

Related

How to retrieve current application root URL in .net core within a static method?

I am currently using the Request.Scheme and Request.Host to composite Uri object to get AbsoluteUri for my .net core MVC application.
Uri location = new Uri($"{Request.Scheme}://{Request.Host}");
string applicationRootURL = location.AbsoluteUri;
But this only works in a non-static method.
As I need to re-use this method in another controller, I am thinking to make this action method static. If I do that, the compiler will complaint about the Request.Scheme and Request.Host.
I am wondering what's other options I have to achieve this?
Thank you.
UPDATE:
This is what I have for ControllerA with ActionMethodA
public class ControllerA
{
public bool ActionMethodA()
{
Uri location = new Uri($"{Request.Scheme}://{Request.Host}");
string applicationRootURL = location.AbsoluteUri;
return false;
}
}
And in another ControllerB, I want to ActionMethodB to invoke ActionMethodA from ControllerA:
public class ControllerB
{
public void ActionMethodB()
{
var result = ActionMethodA();
}
}
Is creating an Extension Method to the ControllerA is the most proper way to handle this kind of scenario?
Thank you.
You can also define an extension method directly for the HttpRequest class and use the BuildAbsolute method of the UriHelper class to build the uri.
public static class HttpRequestExtensions
{
public static string GetURI(this HttpRequest request)
{
return UriHelper.BuildAbsolute(request.Scheme, request.Host);
}
}
And use it:
public IActionResult ContollerMethod()
{
var uri = Request.GetURI();
// your code
}
You can write an extension method to a controller or HttpContext object. In the following example I have added an extension method to the controller.
public static class ControllerExtensions
{
public static string GetURI(this Controller controller)
{
Uri location = new Uri($"{ controller.Request.Scheme}://{controller.Request.Host}");
string applicationRootURL = location.AbsoluteUri;
return applicationRootURL;
}
}
Once the extension method is written you can call it in the following manner.
public IActionResult Index()
{
var url = this.GetURI();
return View();
}
Make sure to import namespace of an extension method in your calling code

Return an image from asp.net web api core as IActionResult

What is the best way to return an image file as IActionResult while using asp.net web api core?
I tried returning a base64 string and it works fine. But not considered as efficient.
Is there a way using which we can return an image file object itself as IActionResult.
You can use the various overloads of the File() function in controllers that inherit from Controller or ControllerBase.
For example, you can do:
return File("~/Images/photo.jpg", "image/jpeg");
This uses a virtual path, other options include giving it a byte array or a Stream. You can also give a download file name as a third argument if that is needed.
[Route("getProductImage/v1")]
[HttpGet]
public async Task<IActionResult> getProductImage(GetProductImageQueryParam parammodel)
{
using (HttpClient client = new HttpClient())
{
MNimg_URL = MNimg_URL + parammodel.modelname;
HttpResponseMessage response = await client.GetAsync(MNimg_URL);
byte[] content = await response.Content.ReadAsByteArrayAsync();
//return "data:image/png;base64," + Convert.ToBase64String(content);
return File(content, "image/png", parammodel.modelname);
}
}
In .net core web api you can use the above code
here GetProductImageQueryParam is a class with input parameters
A File result is called FileContentResult in NET Core 3.x.
You can return image using return file with stream or bytes format or using its image path.
There are few overloaded methods for return File(//parameters); which you can use it in mvc controller's action method.
API Controller
[Route("api/[controller]")]
public class FileController : Controller {
//GET api/file/id
[HttpGet("{id}"]
public async Task<IActionResult> GetFile(string id) {
var stream = await {{//__get_stream_here__//}};
var response = File(stream, "application/octet-stream"); // FileStreamResult
return response;
}
}
or
var imageFileStream = System.IO.File.OpenRead("// image path");
return File(imageFileStream, "image/jpeg");
Hope this will help you.

ASP.NET Core Integration Test for controller action

Microsoft documentation (https://learn.microsoft.com/en-us/aspnet/core/testing/integration-testing) explain how to implement an integration test using the TestServer class. It is easy in case we are using WEB API because we get the serialized model as response from the action.
But in case I want to test a Controller action returning an HTML View containing some data, how can I evaluate that the page content is what I expect (avoiding to scan the HTML page contents) ?
One option is to use Automated UI Testing using something like Selenium
In order to append this JSON serialized view model to your page, I implemented the following filter:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Newtonsoft.Json;
using Ticketino.Web.Components.Extensions.Request;
using Ticketino.Web.OnlineShop.Serializations;
using Ticketino.Web.OnlineShop.ViewModels.Base;
namespace Ticketino.Web.OnlineShop.Filters
{
/// <summary>
/// This is a filter used only for integration tests.
/// It format the ViewModel as jSon and appends it to the end of HMTL page, so that it can be deserialized from the test in order to check its values.
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.Filters.ResultFilterAttribute" />
[AttributeUsage(AttributeTargets.Method)]
public class IntegrationTestFilterAttribute : ResultFilterAttribute
{
public const string StartViewModelContainer = "<script type=\"model/json\">";
public const string EndViewModelContainer = "</script>";
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (!filterContext.ModelState.IsValid)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult?.Model is BaseViewModel)
{
var errors = IntegrationTestFilterAttribute.GetModelErrors(filterContext.ModelState);
((BaseViewModel)viewResult.Model).ValidationErrors = errors;
}
}
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult?.Model != null)
{
var jsonViewModel = string.Concat(
IntegrationTestFilterAttribute.StartViewModelContainer,
JsonConvert.SerializeObject(viewResult.Model, Formatting.None, CommonJsonSerializerSettings.Settings()),
IntegrationTestFilterAttribute.EndViewModelContainer);
filterContext.HttpContext.Response.WriteAsync(jsonViewModel);
}
}
base.OnResultExecuted(filterContext);
}
#region Private methods
private static IDictionary<string, string> GetModelErrors(ModelStateDictionary errDictionary)
{
var errors = new Dictionary<string, string>();
//get all entries from the ModelStateDictionary that have any errors and add them to our Dictionary
errDictionary.Where(k => k.Value.Errors.Count > 0).ForEach(i =>
{
foreach (var errorMessage in i.Value.Errors.Select(e => e.ErrorMessage))
{
errors.Add(i.Key, errorMessage);
}
});
return errors;
}
#endregion
}
}
Then, in ConfigureServices(IServiceCollection serviceCollection) method inject it when you run integration test as show:
// Filter to append json serialized view model to buttom html response page, in order to eveluate from integration test class
if (_hostingEnvironment.IsIntegrationTest())
{
mvcBuilder.AddMvcOptions(opt => { opt.Filters.Add(new IntegrationTestFilterAttribute()); });
}

how to handle ActionResult in custome class?

i have a class (in project by mvc4 razor on .net 4.5) and want to handle a Redirecting method on it and do not want inherit from controller class.
how can i handle this?it returns ActionResult to redirecting user in some cases like log outing
my main class:
public class SecuritySrv
{
public ActionResult Redirect()
{
return RedirectToAction("Logout", "Account", new { area = "" });
}
}
and i want to use it in some controllers like below:
public ActionResult AccountHome()
{
SecuritySrv SecurityService =new SecuritySrv()
if(.....)
return SecurityService.Redirect();
return view();
}
You can use this code anywhere, and you don't need an UrlHelper or access to the context, so you don't need to inherit the Controller class.
RouteValueDictionary rvd = new RouteValueDictionary
{
{"controller", "Profile"},
{"action", "Users"},
{"area", ""}
};
return new RedirectToRouteResult(rvd);
The RedirectToAction method of controller is just a helper for creating RedirectToRouteResult, you can create it by yourself in your class:
public class SecuritySrv
{
public ActionResult Redirect()
{
RouteValueDictionary routeValues = new RouteValueDictionary();
routeValues["action"] = "Logout";
routeValues["controller"] = "Account";
routeValues["area"] = "";
return new RedirectToRouteResult(routeValues);
}
}
And call this method from your controller in the way you wanted to.

ASP.NET Web API OData Action on the EDM Model Root

I'm building a Web API service using OData, and would like to expose a method as an Action in the service as follows.
http://myServer/odata/myAction
I'm currently mapping the OData routes as follows:
Dim modelBuilder As ODataModelBuilder = New ODataConventionModelBuilder
modelBuilder.EntitySet(Of Product)("Products")
Dim myAction = modelBuilder.Action("myAction")
myAction.Parameter(Of String)("Parameter1")
myAction.Returns(Of Boolean)()
Dim model As IEdmModel = modelBuilder.GetEdmModel
config.Routes.MapODataRoute("ODataRoute", "odata", model)
This wonderful tutorial shows how to associate an action with an entity like this:
http://myServer/odata/Products(1)/myAction
Following the tutorial, I can then write the method for the action in the ProductsController class after creating the model with the following line:
Dim myAction = modelBuilder.Entity(Of Product).Action("myAction")
However, if I don't want to associate the action with an entity, where would I write the method for the action? Is there a DefaultController class I need to write?
We currently do not have support for this out of the box, but its very easy to do it yourself. Example below (This nice sample is actually from Mike Wasson which is yet to be made public :-))
------------------------------------------------------
// CreateMovie is a non-bindable action.
// You invoke it from the service root: ~/odata/CreateMovie
ActionConfiguration createMovie = modelBuilder.Action("CreateMovie");
createMovie.Parameter<string>("Title");
createMovie.ReturnsFromEntitySet<Movie>("Movies");
// Add a custom route convention for non-bindable actions.
// (Web API does not have a built-in routing convention for non-bindable actions.)
IList<IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault();
conventions.Insert(0, new NonBindableActionRoutingConvention("NonBindableActions"));
// Map the OData route.
Microsoft.Data.Edm.IEdmModel model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model, new DefaultODataPathHandler(), conventions);
--------------------------------------------------------------
// Implements a routing convention for non-bindable actions.
// The convention maps "MyAction" to Controller:MyAction() method, where the name of the controller
// is specified in the constructor.
public class NonBindableActionRoutingConvention : IODataRoutingConvention
{
private string _controllerName;
public NonBindableActionRoutingConvention(string controllerName)
{
_controllerName = controllerName;
}
// Route all non-bindable actions to a single controller.
public string SelectController(ODataPath odataPath, System.Net.Http.HttpRequestMessage request)
{
if (odataPath.PathTemplate == "~/action")
{
return _controllerName;
}
return null;
}
// Route the action to a method with the same name as the action.
public string SelectAction(ODataPath odataPath, System.Web.Http.Controllers.HttpControllerContext controllerContext, ILookup<string, System.Web.Http.Controllers.HttpActionDescriptor> actionMap)
{
if (controllerContext.Request.Method == HttpMethod.Post)
{
if (odataPath.PathTemplate == "~/action")
{
ActionPathSegment actionSegment = odataPath.Segments.First() as ActionPathSegment;
IEdmFunctionImport action = actionSegment.Action;
if (!action.IsBindable && actionMap.Contains(action.Name))
{
return action.Name;
}
}
}
return null;
}
}
--------------------------------------------------
// Controller for handling non-bindable actions.
[ODataFormatting]
[ApiExplorerSettings(IgnoreApi = true)]
public class NonBindableActionsController : ApiController
{
MoviesContext db = new MoviesContext();
[HttpPost]
public Movie CreateMovie(ODataActionParameters parameters)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
string title = parameters["Title"] as string;
Movie movie = new Movie()
{
Title = title
};
db.Movies.Add(movie);
db.SaveChanges();
return movie;
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}