How to navigate in ServiceStatck Razor pages + ServiceStack api? - razor

In my webapp
webApp
\Views
\Views\School
\Views\School\School.cshtml
\Views\School\Schools.cshtml
In Request and Response classes:
[Route("/v1/school", Verbs = "POST")]
[DefaultView("School")]
public class SchoolAddRequest : School, IReturn<SchoolResponse>
{
}
public class SchoolResponse
{
public School School { get; set; }
public SchoolResponse()
{
ResponseStatus = new ResponseStatus();
Schools = new List<School>();
}
public List<School> Schools { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
In SchoolService.cs:
[DefaultView("School")]
public class SchoolService: Service
{
public SchoolResponse Post(SchoolAddRequest request)
{
var sch = new School {Id = "10"};
return new SchoolResponse {School = sch, ResponseStatus = new ResponseStatus()};
}
}
In school.cshtml:
#inherits ViewPage<Test.Core.Services.SchoolResponse>
#{
Layout = "_Layout";
}
<form action="/v1/School" method="POST">
#Html.Label("Name: ") #Html.TextBox("Name")
#Html.Label("Address: ") #Html.TextBox("Address")
<button type="submit">Save</button>
</form>
#if (#Model.School != null)
{
#Html.Label("ID: ") #Model.School.Id
}
On the browser:
This is suppose to work but it is not, i get a blank page
http://test/school/
This works:
http://test/views/school/
On hitting the 'save' btn the required response is returned but the url on the browser is :
http://test/v1/School
I was expecting it to be:
http://test/School
How can i get the url to work right.? Shouldn't it be
http://test/School on request and response.

http://test/school/ is not returning anything because you don't have a request DTO and a corresponding 'Get' service implemented for the route.
What you need is a request DTO:
[Route("/school", Verbs = "GET")]
public class GetSchool : IReturn<SchoolResponse>
{
}
and the service...
public SchoolResponse Get(GetSchool request)
{
var sch = new School {Id = "10"};
return new SchoolResponse {School = sch, ResponseStatus = new ResponseStatus()};
}
When you hit 'Save', a 'POST' request will be made to the server through the route 'v1/school' because the form tag you specified has:
<form action="/v1/School" method="POST">
Hope this helps.

Related

.Net 5 blazor server app property is null in razor but has data after webapi call

I am coding a .Net 5 Blazor server side app and - sorry if I am using the wrong terminology - can't seem to pin down why the razor page's object/property is null, yet the code-behind method that populates that object/property contains data from a webApi. I am trying to use the repository pattern, dto objects, dependency injection, webapi, and efcore.
In Startup.cs > ConfigureServices() I have:
services.AddHttpClient<IDocumentNumbersDataService, DocumentNumbersDataService>
(client => client.BaseAddress = new Uri("https://localhost:44323/"));
For the UI I have Home.razor:
#page "/Home"
<form method="get">
<div class="input-group-append">
<button type="submit" class="btn btn-primary" #onclick="SearchDocumentNumbers">Search</button>
</div></form>
#if (DocumentNumbers == null)
{
<p><em>Loading...</em></p>
}
else
{
```
show table of document numbers using foreach()
```
}
and its code-behind Home.cs:
public partial class Home
{
public IEnumerable<DocumentNumberDto> DocumentNumbers { get; set; }
[Parameter]
public string ProjNumber { get; set; }
[Inject]
public IDocumentNumbersDataService DocumentNumbersDataService { get; set; }
protected async Task<IEnumerable<DocumentNumberDto>> SearchDocumentNumbers()
{
ProjNumber = "1012100100";
var DocumentNumbers = await DocumentNumbersDataService.GetDocumentNumbersAsync(ProjNumber); //DocumentNumbers gets populated with Dto objects
}
}
The call to GetDocumentNumbersAsync() in DocumentNumberDataService is:
public async Task<IEnumerable<DocumentNumberDto>> GetDocumentNumbersAsync(string ProjNumber)
{
return await JsonSerializer.DeserializeAsync<IEnumerable<DocumentNumberDto>>
(await _httpClient.GetStreamAsync($"api/documentnumbers/{ProjNumber}"),
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}
The problem: DocumentNumbers property is null in Home.razor even though the IEnumerable<DocumentNumbersDto> DocumentNumbers is populated via...
var DocumentNumbers = await DocumentNumbersDataService.GetDocumentNumbersAsync(ProjNumber);
I suspect I overlooked something simple.

TempData does not contains any definition

I have a problem in regarding my TempData["Something"] because I want to customize every content of the Something. Below is my code:
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveLoanContract(ModelLoan lc, string message)
{
var myList = new ModelLoan();
var countLoan = _Context.LoanContract.Count(c => c.EmployeeId == lc.loanContract.EmployeeId && c.LoanTypeId == lc.loanContract.LoanTypeId);
if (ModelState.IsValid)
{
//Some Logic
TempData["SuccessMessage"] = new { CssClassName = "alert-success", Title = "Success!", Message = "You have successfully applied. Please wait for confirmation. Thank you!" };
return RedirectToAction("Index");
}
}
View:
#if(TempData["SuccessMessage"] != null)
{
<div class="alert #TempData["SuccessMessage"].CssClassName">
<strong>#TempData["SuccessMessage"].Title</strong> #TempData["SuccessMessage"].Message
</div>
}
But I got an error on some objects CssClassName, Title, Message in TempData["SuccessMessage"] and I don't have any idea yet why. I know there is a small trick to fix this. Can someone help me with this? Thanks!
you cannot store anonymous type into TempData but you can create a class and convert tempdata to the class like this
public class ClsMsg
{
public string CssClassName { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
in action
TempData["SuccessMessage"] = new ClsMsg { CssClassName = "alert-success", Title = "Success!", Message = "You have successfully applied. Please wait for confirmation. Thank you!" };
and in view
#if (TempData["SuccessMessage"] != null)
{
ClsMsg msg = TempData["SuccessMessage"] as ClsMsg;
<div class="alert #msg.CssClassName">
<strong>#msg.Title</strong> #msg.Message
</div>
}

Posting JSON to WebAPI2 function

I have the following code in an MVC app controller to send some data to be stored in an Archive table using EF6 via a WebAPI2 call.
I'm getting a "Cannot send a content-body with this verb-type" even though I'm setting to POST and the api call is defined to accept only POST.
What in the world am I doing wrong and how can I fix it?
ArchiveUploadModel.ArchiveUpload obj = new ArchiveUploadModel.ArchiveUpload();
obj.LT = LT;
obj.PID = PID.ToString();
obj.Title = "Ex Review";
obj.HTML = message.Body; // the HTML is a rendered HTML email message
if (!string.IsNullOrEmpty(obj.HTML))
{
HttpWebRequest req = HttpWebRequest.Create("http://example.com/MyApp/api/UploadToArchive") as HttpWebRequest;
request.ContentType = "application/json";
request.Method = "POST";
string json = JsonConvert.SerializeObject(obj);
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
}
using (HttpWebResponse webresponse = request.GetResponse() as HttpWebResponse)
{
using (StreamReader reader = new StreamReader(webresponse.GetResponseStream()))
{
string response = reader.ReadToEnd();
}
}
This is the code for my WebAPI call:
[HttpPost]
[Route("api/UploadToArchive")]
[EnableCors("http://example.com", // Origin
"Accept, Origin, Content-Type, Options", // Request headers
"POST", // HTTP methods
PreflightMaxAge = 600 // Preflight cache duration
)]
public IHttpActionResult UploadToArchive(ArchiveUpload upload)
{
string HTML = upload.HTML;
string Title = upload.Title;
string LT = upload.LT;
string lt = getLT(upload.PID); // essentially secure checking to see if it matches passed LT.
if (lt == LT)
{
// Upload the file to the archive using the ArchiveRepository's UpdateArchive() function:
_ArchiveRepository.UpdateArchive(HTML, System.Web.HttpUtility.HtmlDecode(Title), "", upload.PID);
return Ok(PID);
}
else
{
return BadRequest("Invalid LT");
}
}
ArchiveUpload model definition in both applications:
public class ArchiveUpload
{
public string LT { get; set; }
public string PID { get; set; }
public string Title { get; set; }
public string HTML { get; set; }
}
Better try to use the Microsoft Http Client Libraries. You can install it from nuget and here you find examples calling Web API using different HTTP verbs

How does Asp.net Core renders a view

How does MVC 6 renders a view. What's the actual method in Razor ViewEngine that generates the html output? Also if possible please explain the process of rendering a view.
May be you could point me to a file on mvc source on github. thanks!
Here is a complete solution of what you are looking for. I used dependency injection to get the HtmlHelper in the controller. You can inject your own helper if you want too.
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.WebEncoders;
using System.ComponentModel.DataAnnotations;
using System;
public class MyController : Controller
{
private readonly IHtmlGenerator htmlGenerator;
ICompositeViewEngine viewEngine;
IModelMetadataProvider metadataProvider;
private readonly IHtmlHelper helper;
IHtmlEncoder htmlEncoder;
IUrlEncoder urlEncoder;
IJavaScriptStringEncoder javaScriptStringEncoder;
public MyController(IHtmlHelper helper, IHtmlGenerator htmlGenerator, ICompositeViewEngine viewEngine, IModelMetadataProvider metadataProvider, IHtmlEncoder htmlEncoder, IUrlEncoder urlEncoder, IJavaScriptStringEncoder javaScriptStringEncoder)
{
this.htmlGenerator = htmlGenerator;
this.viewEngine = viewEngine;
this.metadataProvider = metadataProvider;
this.htmlEncoder = htmlEncoder;
this.urlEncoder = urlEncoder;
this.javaScriptStringEncoder = javaScriptStringEncoder;
this.helper = helper;
}
[HttpGet]
public IActionResult MyHtmlGenerator()
{
MyViewModel temp = new MyViewModel();
var options = new HtmlHelperOptions();
options.ClientValidationEnabled = true;
ViewDataDictionary<MyViewModel> dic = new ViewDataDictionary<MyViewModel>(this.metadataProvider, new ModelStateDictionary());
ViewContext cc = new ViewContext(ActionContext, new FakeView(), dic, TempData, TextWriter.Null, options);
var type = typeof(MyViewModel);
var metadata = this.metadataProvider.GetMetadataForType(type);
ModelExplorer modelEx = new ModelExplorer(this.metadataProvider, metadata, temp);
ViewData["Description"] = "test desc";
ViewData["Id"] = 1;
this.ViewData = new ViewDataDictionary(this.metadataProvider, new ModelStateDictionary());
IHtmlHelper<MyViewModel> dd = new HtmlHelper<MyViewModel>(this.htmlGenerator, this.viewEngine, this.metadataProvider, this.htmlEncoder, this.urlEncoder, this.javaScriptStringEncoder);
((ICanHasViewContext)dd).Contextualize(cc);
dd.ViewContext.ViewData = this.ViewData;
var desc = GetString(dd.TextBoxFor(m => m.ID));
var ID = GetString(dd.TextBoxFor(m => m.Description));
// Do whatever you want with the ID and desc
return new ContentResult() { Content = ID + desc };
}
public static string GetString(IHtmlContent content)
{
var writer = new System.IO.StringWriter();
content.WriteTo(writer, new HtmlEncoder());
return writer.ToString();
}
}
public class MyViewModel : BaseAssetViewModel
{
// [RegularExpression(#"^-?\d{1,13}(\.\d{0,5})?$|^-?\.\d{1,5}$")]
[Required]
public int ID { get; set; }
[MinLength(2)]
public string Description { get; set; }
// Property with no validation
public string Other { get; set; }
}
public class FakeView : IView
{
string IView.Path
{
get
{
throw new NotImplementedException();
}
}
public Task RenderAsync(ViewContext viewContext)
{
throw new InvalidOperationException();
}
Task IView.RenderAsync(ViewContext context)
{
throw new NotImplementedException();
}
}
I don't know if this may be of help, may be you have to start to look at tag helpers:
https://github.com/DamianEdwards/TagHelperStarterWeb
they're working to a different way to create helpers that integrate in the page in a more natural way.

MVC 4 HTML is never decoded on POST

I am using a Kendo editor to create email templates and on POST, once a change to the template has been submitted, always renders in encoded HTML.
This is my razor code on the page:
#model Business.Models.Administration.EmailSetupViewModel
#using Kendo.Mvc.UI;
<h2>Application Stages Portal</h2>
<h4>Email Setup</h4>
#using (Html.BeginForm())
{
if (Model.EmailSaved)
{
<h2>
Email template saved</h2>
}
else
{
#* #Html.DisplayFor(m => m.EmailSavedMsg)*#
}
#Html.DropDownListFor(m => m.EmailTemplateToEdit, Model.EmailTemplatesList)
<input type="submit" name="setup" value="setup" />
if (Model.ShowEmailForm)
{
<div id="email-edit">
#Html.Label("Title")
#Html.TextBoxFor(m => m.EmailTitle, new { style = "width:200px" })
<br />
#(Html.Kendo().Editor()
.Name("editor")
.HtmlAttributes(new { style = "width: 600px;height:440px" })
.Value(#<text>
#Html.Raw(Model.EmailBody)
</text>))
</div>
<input type="submit" id="btnSaveTemplate" name="update" value="update" />
<h2>
Please note</h2>
<p>
The following items are <i>reserved and should not be changed, you may move them
to a different place within the message. </i>
<ul>
<li><*name*> e.g. Fred Flinstone </li>
<li><*membernumber*> e.g. 12345678 </li>
</ul>
</p>
}
}
And this is where the actual editor markup is on the page
#(Html.Kendo().Editor()
.Name("editor")
.HtmlAttributes(new { style = "width: 600px;height:440px" })
.Value(#<text>
#Html.Raw(Model.EmailBody)
</text>))
Model.EmailBody contains the actual string.
When I GET the page, it renders fine. But when I do POST it never decodes so the rendering is wrong. I don't want to see all the HTML tags but the actual formatting.
This is my Controller code:
#region Email template
[HttpGet]
public ActionResult EmailSetup()
{
ViewBag.DisplayName = StaticFunctions.GetDisplayName(this.User.Identity.Name);
EmailSetupViewModel model = new EmailSetupViewModel();
Business.Administration.Email Email = new Business.Administration.Email();
var list = Email.GetTemplateList();
model.EmailTemplatesList = list.OrderBy(o => o.Text).ToList();
return View(model);
}
[HttpPost]
public ActionResult EmailSetup(EmailSetupViewModel model, string value, string editor)
{
ViewBag.DisplayName = StaticFunctions.GetDisplayName(this.User.Identity.Name);
string body = HttpUtility.HtmlDecode(editor); //encode to db
if (Request["update"] != null)
{
Business.Administration.Email Email = new Business.Administration.Email();
model.EmailSaved = Email.SaveTemplate(model, body);
//ModelState.Clear(); // when doing POST - clearing the ModelState will prevent encode of HTML (Default behaviour). This isn't good long term solution.
if (model.EmailSaved)
{
model.EmailSavedMsg = "Template saved";
}
else
{
model.EmailSavedMsg = "Template couldn't be saved";
}
model.EmailTemplatesList = Email.GetTemplateList();
model = Email.GetTemplate(model);
model.EmailBody = HttpUtility.HtmlDecode(model.EmailBody);
return View(model);
}
else
{
Business.Administration.Email Email = new Business.Administration.Email();
model.EmailTemplatesList = Email.GetTemplateList();
model = Email.GetTemplate(model);
model.EmailBody = HttpUtility.HtmlDecode(model.EmailBody);
return View(model);
}
}
#endregion
This is my model, I am using [AllowHtml] attribute on the property.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
namespace Business.Models.Administration
{
public class EmailSetupViewModel
{
public EmailSetupViewModel()
{
this.EmailTemplatesList = new List<SelectListItem>();
}
public string EmailTemplateToEdit { get; set; }
public List<SelectListItem> EmailTemplatesList { get; set; }
public string EmailTitle { get; set; }
[AllowHtml]
public string EmailBody { get; set; }
public bool ShowEmailForm { get; set; }
public bool EmailSaved { get; set; }
public string EmailSavedMsg { get; set; }
}
}
Finally two screenshots, one on GET and one on POST.
I was using ModelState.Clear() as well but when I clicked back on the browser, it wouldn't decode.
So basically I want help rendering the HTML in my editor on post so it renders properly and doesn't show HTML tags in the editor.