Serving dynamic XML file from .NET Core Web Application - asp.net-core

In my .NET Core 2.2 website, I have a controller method within my BlogController that generates a sitemap.xml file:
public ActionResult SiteMap()
{
// logic here
return Content("<sitemap>...</sitemap>", "text/xml");
}
I have this route set up so that the sitemap will be output at https://mysite/sitemap
routes.MapRoute(
name: "sitemap",
template: "sitemap",
defaults: new { controller = "Blog", action = "SiteMap" });
That works, in that accessing /sitemap results in the XML content being served up.
However, when I access https://mysite/sitemap.xml, I get a 404 error.
I'm pretty sure this is something to do with static file handling, but I'm not sure how to set it up so that /sitemap.xml works.

You could try to dynamtically build xml like this
[Route("/sitemap.xml")]
public void SitemapXml()
{
string host = Request.Scheme + "://" + Request.Host;
Response.ContentType = "application/xml";
using (var xml = XmlWriter.Create(Response.Body, new XmlWriterSettings { Indent = true }))
{
xml.WriteStartDocument();
xml.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
xml.WriteStartElement("url");
xml.WriteElementString("loc", host);
xml.WriteElementString("changefreq", "daily");
xml.WriteElementString("lastmod", DateTime.Now.ToString("yyyy-MM-dd"));
xml.WriteEndElement();
var categories = _categoryService.GetAllCategories(inclTopMenu: true);
foreach (var c in categories)
BuildXml(xml, c.GetSeName(), host);
xml.WriteEndElement();
}
}
private void BuildXml(XmlWriter xml, string url, string host)
{
xml.WriteStartElement("url");
xml.WriteElementString("loc", host + "/" + url);
xml.WriteElementString("changefreq", "weekly");
xml.WriteElementString("lastmod", DateTime.Now.ToString("yyyy-MM-dd"));
xml.WriteEndElement();
}
or create a page like https://forums.asp.net/t/2160949.aspx?Create+Robots+txt+and+sitemap+xml+dynamically+in+asp+net+core

Here is a simple demo about how to generate xml file to server and show xml file by using url like https://mysite/sitemap.xml:
1.View:
<form asp-action="Create" enctype="multipart/form-data">
<div class="form-group">
<input type="file" name="file" id="file" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
2.Controller:
public class UsersController : Controller
{
private IHostingEnvironment _env;
public UsersController(IHostingEnvironment env)
{
_env = env;
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(IFormFile file)
{
var fileName = System.IO.Path.GetFileName(file.FileName);
var filePath = System.IO.Path.Combine(_env.WebRootPath, fileName);
if (file.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
return View("Index");
}
}
3.Be sure to add app.UseStaticFiles(); like below,then you could access https://mysite/sitemap.xml:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
app.UseStaticFiles();
//...
}
4.Result:

Related

Where i must get Ajax Helper or how to create it to use #AjaxExtensions.ActionLink

I want to use #AjaxExtensions.ActionLink(AjaxHelper, String, String, String, RouteValueDictionary, AjaxOptions) in cshtml file (ASP.Net 6 MVC).
#model List<Shift>
#using System.Web.Mvc.Ajax;
#{
AjaxOptions deleteOptions = new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "table"
};
}
#foreach (Shift shift in Model)
{<td>
#AjaxExtensions.ActionLink( ???,"Delete", "DeleteShift", "MainDoctor", new{id=shift.Id}, deleteOptions)
</td>
}
As this document said, AjaxExtensions.ActionLink is only applies to ASP.NET MVC 5.2, and the correct usage should be:
#Ajax.ActionLink("Delete", "DeleteShift", "MainDoctor", new { id = shift.Id }, deleteOptions)
In ASP.NET 6, you need use an alternative way like below:
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#table" data-ajax-url="#Url.Action("DeleteShift", "MainDoctor", new { id = shift.Id})">Delete</a>
A simple working demo you could follow:
Model:
public class Test
{
public string Id{ get; set; }
}
View(Views/Home/Index.cshtml):
#model Test
<div id="table">
#Model.Id
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#table" data-ajax-url="#Url.Action("DeleteShift", "MainDoctor", new { id = 1 })">Delete</a>
</div>
#section Scripts
{
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js" integrity="sha256-v2nySZafnswY87um3ymbg7p9f766IQspC5oqaqZVX2c=" crossorigin="anonymous"></script>
}
Controller:
public class MainDoctorController:Controller
{
public IActionResult DeleteShift(int id)
{
var model = new Test()
{
Id = "bb"
};
return PartialView("/Views/Home/Index.cshtml", model);
}
}
public class HomeController : Controller
{
public async Task<IActionResult> Index()
{
var model = new Test()
{
Id = "aaa"
};
return View(model);
}
}
Result:

System.InvalidOperationException: 'The response headers cannot be modified because the response has already started.'

I am using Blazor server side and want to set Cookie for that i am using SignInAsync function of HttpContext but giving me this error "The response headers cannot be modified because the response has already started".
The error flashes when it comes to the following line
await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
I have tried every this but still facing the error
My Startup.cs Page
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using power_SLIC.Data;
using System.Net.Http;
using power_SLIC.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace power_SLIC
{
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.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
// services.AddProtectedBrowserStorage();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
options => {
options.LoginPath = "/";
});
//.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, act => {
// act.LoginPath = "/";
// act.AccessDeniedPath = "/";
// act.SlidingExpiration = true;
//}
// services.AddHttpClient();
services.AddSingleton<HttpClient>();
// services.AddScoped<AuthenticationStateProvider, TokenAuthenticationServices>();
//services.AddScoped<ILoginservices, TokenAuthenticationServices>();
/// services.AddSingleton<HttpClient>();
services.AddAuthorization(option =>
{
option.AddPolicy("Employee", policy => policy.RequireClaim("IsUserEmployee", "true"));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
And My Razor Page looks like this
Loginscreen.razor
#layout LoginLayout
#page "/"
#using System.Threading.Tasks
#using Microsoft.AspNetCore.Authentication
#using Microsoft.AspNetCore.Authentication.Cookies
#using Microsoft.AspNetCore.Http
#using Microsoft.AspNetCore.Mvc
#using Newtonsoft.Json
#using System.Text
#using Newtonsoft.Json.Linq
#using Microsoft.AspNetCore.ProtectedBrowserStorage
#using System.Security.Claims
#using System.Web
#*#inject ProtectedSessionStorage ProtectedSessionStore*#
#inject IJSRuntime JsRuntime;
#inject NavigationManager NavigationManager
#inject HttpClient http
#inject IHttpContextAccessor httpContextAccessor
<div style="margin-left:39%;margin-right:17%">
<div class="card">
<h4 class="card-header">Login</h4>
<div class="card-body">
<EditForm Model="#model" >
#*<DataAnnotationsValidator />*#
<div class="form-group">
<label>Username</label>
<InputText #bind-Value="model.SSam_User" class="form-control" />
#*<ValidationMessage For="#(() => model.Username)" />*#
</div>
<div class="form-group">
<label>Password</label>
<InputText #bind-Value="model.SSam_Pwd" type="password" class="form-control" />
#*<ValidationMessage For="#(() => model.Password)" />*#
</div>
<button class="btn btn-primary" #onclick = "AddItem" >
Login
</button>
#* <NavLink href="account/register" #onClick = "window.location.href = 'home'" class="btn btn-link">Register</NavLink>*#
</EditForm>
</div>
</div>
</div>
#code{
private bool isConnected = false;
private Model.LoginModel model = new Model.LoginModel();
class Values
{
public string result {get; set;}
}
class Result{
public List<Values> result { get; set;}
};
public async void AddItem()
{
var addItem = new Model.LoginModel {SSam_User=model.SSam_User,SSam_Pwd=model.SSam_Pwd, UnitSname = "HFHO" };
string output = JsonConvert.SerializeObject(addItem);
var Stringcontent = new StringContent(output,Encoding.UTF8,"application/json");
var op = await http.PostAsync("https://localhost:44392/api/outputs/getpowerbi_token", Stringcontent);
var resultcontent = op.Content.ReadAsStringAsync().Result;
dynamic oit = JsonConvert.DeserializeObject<dynamic>(resultcontent);
var accesstoken = oit["result"]["Token_status"].Value;
var op1 = oit["result"]["login_response"][0]["status"].Value;
if(op1 == "success")
{
var reo = httpContextAccessor.HttpContext.Request.Headers;
var prerender = !httpContextAccessor.HttpContext.Response.HasStarted;
if (!httpContextAccessor.HttpContext.Request.Headers.ContainsKey("Header"))
{
if (!httpContextAccessor.HttpContext.Response.HasStarted)
{
string result;
httpContextAccessor.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
result = JsonConvert.SerializeObject(new { error = "Request doesn't contains header" });
httpContextAccessor.HttpContext.Response.ContentType = "application/json";
await httpContextAccessor.HttpContext.Response.WriteAsync(result);
}
else
{
await httpContextAccessor.HttpContext.Response.HttpContext.Response.WriteAsync(string.Empty);
}
var reo1 = httpContextAccessor.HttpContext.Response.HttpContext.Response;
AuthenticateResult.Fail("request doesn't contains header");
}
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, model.SSam_User),
new Claim("Token", accesstoken),
new Claim(ClaimTypes.Role, "User"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
RedirectUri = "/",
ExpiresUtc = DateTime.UtcNow.AddSeconds(30)
};
var j1 = CookieAuthenticationDefaults.AuthenticationScheme;
await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
NavigationManager.NavigateTo("/powerbi",true);
}
}
}
The problem is async void. This is only meant for asynchronous event handlers, which aren't used in Blazor. Such methods can't be awaited, which means the caller won't start and wait for them to complete. Since the runtime has no idea that AddItem is still running, it starts sending the response to the client.
Asynchronous methods that have no result should return async Task, not async void :
public async Task AddItem()
{
...
}
Study the template app with Authentication enabled:
The Login page is a Razor page outside your app. Forcing you to leave and restart the SPA app when logging in.
The Blazor app can only read cookies from the single HTTP response that started it. You cannot write to those cookies.
So if you want to DIY an Authentication system you will have to store the status information some other way (Session Storage).

Sending cshtml view mails with mailkit

I am using ASP.NET Core and Mailkit to send emails. Take the following (Basic) code:
var bodyBuilder = new BodyBuilder();
bodyBuilder.HtmlBody = GetBody();
var m = new MimeMessage();
m.To.Add(new MailboxAddress("gurdip.sira#gmail.com"));
m.From.Add(new MailboxAddress("Sender Name", "gurdip.sira#gmail.com"));
string s = GetBody();
// m.Body = bodyBuilder.ToMessageBody();
m.Body = new TextPart(MimeKit.Text.TextFormat.Html) {Text = s};
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
{
smtp.Connect("smtp.gmail.com", 587);
smtp.AuthenticationMechanisms.Remove("XOAUTH2");
smtp.Authenticate("gurdip.sira#gmail.com", "December5!");
smtp.Send(m);
}
The GetBody() method just reads a html document (streamreader).
What I'd like to do is use razor views and cshtml as my emails may contain dynamic content (e.g. an unknown sized collection of certain items).
I can't seem to find definitive documentation on how to do this. The idea is to then just read the cshtml view as plain html but resolve the razor syntax and model variables.
Anyone done anything like this?
One solution is from your controller to pass the content.
public void TestAction(){
var content = PartialView("your_partial_view").ToString();
your_SendEmailFunction(content)
}
So basically you use the partial view as a string that you pass as a content to your method.
Here is a simple demo based on jmal73's comment in Paris Polyzos' blog like below:
1.custom interface:
public interface IViewRenderService
{
Task<string> RenderToStringAsync(string viewName, object model);
}
2.implement interface:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
public class ViewRenderService : IViewRenderService
{
private readonly IRazorViewEngine _razorViewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly HttpContext _httpContext;
public ViewRenderService(IRazorViewEngine razorViewEngine,
ITempDataProvider tempDataProvider,
IHttpContextAccessor httpContextAccessor)
{
_razorViewEngine = razorViewEngine;
_tempDataProvider = tempDataProvider;
_httpContext = httpContextAccessor.HttpContext;
}
public async Task<string> RenderToStringAsync(string viewName, object model)
{
var actionContext = new ActionContext(_httpContext, new RouteData(), new ActionDescriptor());
var viewEngineResult = _razorViewEngine.FindView(actionContext, viewName, false);
if (viewEngineResult.View == null || (!viewEngineResult.Success))
{
throw new ArgumentNullException($"Unable to find view '{viewName}'");
}
var view = viewEngineResult.View;
using (var sw = new StringWriter())
{
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
viewDictionary.Model = model;
var tempData = new TempDataDictionary(_httpContext, _tempDataProvider);
var viewContext = new ViewContext(actionContext, view, viewDictionary, tempData, sw, new HtmlHelperOptions());
viewContext.RouteData = _httpContext.GetRouteData(); //set route data here
await view.RenderAsync(viewContext);
return sw.ToString();
}
}
}
3.read .cshtml file and return string:
public class HomeController : Controller
{
private readonly IViewRenderService _viewRenderService;
public HomeController(IViewRenderService viewRenderService)
{
_viewRenderService = viewRenderService;
}
public IActionResult Index()
{
var data = new Users()
{
UserId = 1
};
return View(data);
}
public async Task<IActionResult> Privacy()
{
var data = new Users()
{
UserId = 1
};
var result = await _viewRenderService.RenderToStringAsync("Home/Index", data);
return Content(result);
}
4.Index.cshtml:
#model Users
<form>
<label asp-for="UserId"></label>
<br />
<input asp-for="UserId" class="form-control" maxlength="4" />
<span asp-validation-for="UserId" class="text-danger"></span>
<input type="submit" value="create" />
</form>
5.Register service:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IViewRenderService, ViewRenderService>();

Can't update image in ASP.NET Core MVC

I'm beginner to ASP.NET Core, I am trying to update an image in a database, I have some doubts about my code, image added to database but cannot update
AdminController
public async Task<ViewResult> Edit(int productId)
{
return View(await _repository.Products
.FirstOrDefaultAsync(p => p.ProductID == productId));
}
[HttpPost]
public async Task<IActionResult> Edit(Product product, IFormFile image)
{
if (ModelState.IsValid)
{
if(image != null && image.Length > 0)
{
var fileName = Path.GetFileName(image.FileName);
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\images\\items", fileName);
using(var fileSteam = new FileStream(filePath, FileMode.Create))
{
await image.CopyToAsync(fileSteam);
}
product.Image = fileName;
}
_repository.SaveProduct(product);
TempData["message"] = $"{product.Name} has been edit";
return RedirectToAction("Index");
}
else
{
return View(product);
}
}
Edit.cshtml
<form asp-action="Edit" method="post" enctype="multipart/form-data">
<input type="hidden" asp-for="ProductID" />
<div class="form-group">
<h4><label asp-for="Image"></label></h4>
<input asp-for="Image" type="file" class="form-control" />
</div>
I'm found where I was wrong, thanks Xing Zou! I updated the SaveProduct(product)
public Product SaveProduct(Product productDataUpdate)
{
var prod = _context.Products.Attach(productDataUpdate);
prod.State = Microsoft.EntityFrameworkCore.EntityState.Modified;
_context.SaveChanges();
return productDataUpdate;
}

asp.net mvc My Form action is empty

I have define my routeconfig as :
routes.MapRoute(
"addcomments",
"addcomments/{urlLanguage}",
new { controller = "Home", action = "addcomments", urlLanguage = "h" }
);
and in Homecontroller :
public ActionResult addcomments(string urlLanguage)
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public string addcommentssave(int id, string yourName, string youremail, string website, string yourComment)
{
string message = " Thanks for sharing your comment! If your comment doesn't appear right away, please be patient as it may take a few minutes to publish or may require moderation.";
DateTime WishDateTime = DateTime.UtcNow;
string szRemoteAddr = System.Web.HttpContext.Current.Request.UserHostAddress;
Comments commit = new Comments();
commit.comdate = WishDateTime;
commit.comments = yourComment;
commit.email = youremail;
commit.Name = yourName;
commit.Website = website;
commit.IpAddress = szRemoteAddr;
commit.PostId = id;
commit.IsVisible = 0;
_session.Comment.Add(commit);
_session.SaveChanges();
//ViewBag.result= "<div class='message success'>" + message + "</div>";
// return View();
return "<div class='message success'>" + message + "</div>";
}
in addcomments view
#using (Ajax.BeginForm("addcommentssave", "home", null,
new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "commentmessage",
OnBegin = "wait()",
OnSuccess = "success()",
LoadingElementId = "updating" },
new { id = "_commitForm" }))
{
}
but when i run it my form action become empty like
form action="" ....`
how to i solve it, help me
I created a simple test site to try and see if I could replicate your issue.
Here is my route config:
namespace Comments
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Use LocalDB for Entity Framework by default
Database.DefaultConnectionFactory = new SqlConnectionFactory(#"Data Source=(localdb)\v11.0; Integrated Security=True; MultipleActiveResultSets=True");
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
Here is my HomeController.cs:
namespace Comments.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return RedirectToAction("AddComment");
}
[HttpGet]
public ActionResult AddComment()
{
return View();
}
[HttpPost]
public ActionResult AddComment(int id, string yourName, string youremail, string website, string yourComment)
{
return Content(String.Format("Got a comment, id = {0}, yourName = {1}, youremail = {2}, website = {3}, yourComment = {4}",
id,
yourName,
youremail,
website,
yourComment));
}
}
}
Here is my AddComment.cshtml:
#{
ViewBag.Title = "Add Comment";
}
<h2>Add Comment</h2>
#using (Ajax.BeginForm("AddComment", "Home", null, new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "commentmessage", OnBegin = "wait()", OnSuccess = "success()", LoadingElementId = "updating" }, new { id = "_commitForm" }))
{
<b>Form Here</b>
}
Here is the view source:
<DOCTYPE html>
<html>
<head>
<title>Add Comment</title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
</head>
<body>
<h2>Add Comment</h2>
<form action="/Home/AddComment" data-ajax="true" data-ajax-begin="wait()" data-ajax-loading="#updating" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-success="success()" data-ajax-update="#commentmessage" id="_commitForm" method="post"> <b>Form Here</b>
</form>
</body>
</html>
So you can see the form path rendering. Try simplifying your route config (I didn't do anything special).