Noob question. I have a project with a self-hosted Web Api. I'm using the RazorEngine package so that I can serve up HTML pages using the views/razor scheme.
Within the HTML page there are links to .css, .JS, and images. How does the page get these embedded resources?
As I understand it, http://localhost:8080/api/home in the browser causes the project to 'call' the page at /Views/Home.html and pass through the Value object. This results in HTML appearing in the browser rather than the usual JSON/XML that you normally get with WebAPi.
For the page to retrieve the embedded javascript, I guess I would create another WebApi controller that would respond to the URL, but how do I get it to transmit the javascript page? Ie how do I get it to look in a folder called 'Scripts' and not 'Views', not attempt to convert to HTML, and not bother with an associated model?
public class HomeController : ApiController
{
//http://localhost:8080/api/home
public Value GetValues()
{
return new Value() { Numbers = new int[] { 1, 2, 3 } };
}
}
[View("Home")]
public class Value
{
public int[] Numbers { get; set; }
}
home.cshtml...
<html>
<head>
<script src="/Scripts/script1.js"></script>
</head>
<body>
<img src="/Images/image1.png">
....
</body>
</html>
In case anyone else has this issue, this is how I did it in the end....
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Diagnostics;
using WebApiContrib.Formatting.Html;
using System.IO;
using System.Net;
using System.Drawing;
using System.Resources;
using System.Reflection;
using System.Text.RegularExpressions;
namespace Owin_Test1.Controllers
{
public class PageResourcesController : ApiController
{
//
// An HTML page will have references to css, javascript and image files
// This method supplies these file to the browser
// These files are saved in the Visual Studio project as linked resources
// Make sure the resources are names correctly (and correct case) i.e.:
// <fileName> = <resourceName>.<fileExtension>
// http://localhost:8080/api/PageResources/<fileName>
// The fileExtension is used to determine how to extract & present the resource
// (Note, <filename> is the reference in the HTML page
// - it needed be the same as the name of the actual file.)
//
public HttpResponseMessage Get(string filename)
{
String projectName = "Owin_Test1";
//Obtain the resource name and file extension
var matches = Regex.Matches(filename, #"^\s*(.+?)\.([^.]+)\s*$");
String resourceName = matches[0].Groups[1].ToString();
String fileExtension = matches[0].Groups[2].ToString().ToLower();
Debug.WriteLine("Resource: {0} {1}",
resourceName,
fileExtension);
//Get the resource
ResourceManager rm = new ResourceManager(
projectName + ".Properties.Resources",
typeof(Properties.Resources).Assembly);
Object resource = rm.GetObject(resourceName);
ImageConverter imageConverter = new ImageConverter();
byte[] resourceByteArray;
String contentType;
//Generate a byteArray and contentType for each type of resource
switch (fileExtension)
{
case "jpg":
case "jpeg":
resourceByteArray = (byte[])imageConverter.ConvertTo(resource, typeof(byte[]));
contentType = "image/jpeg";
break;
case "png":
resourceByteArray = (byte[])imageConverter.ConvertTo(resource, typeof(byte[]));
contentType = "image/png";
break;
case "css":
resourceByteArray = Encoding.UTF8.GetBytes((String)resource);
contentType = "text/css";
break;
case "js":
resourceByteArray = Encoding.UTF8.GetBytes((String)resource);
contentType = "application/javascript";
break;
case "html":
default:
resourceByteArray = Encoding.UTF8.GetBytes((String)resource);
contentType = "text/html";
break;
}
//Convert resource to a stream, package up and send on to the browser
MemoryStream dataStream = new MemoryStream(resourceByteArray);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(dataStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
return response;
}
}
}
Related
I wanted to easily turn some json schema files into classes. So googling I found NJsonSchema and I implemented this in a visual studio custom tool so I can set this on relevant json files and get my classes out. This al works and I pasted the code below. This code comes from this very answer. Though it does need a little updating for VS2022.
I find that documentation on how to do this is rather rare and the thing I am missing is how I can add something like configuration options for the custom tool.
Take for example the line "ClassStyle = CSharpClassStyle.Record," that is something one might want configurable for the user. But I cannot find anything on how to do that. Anyone have a good pointer/answer on this?
Preferably a way to add take the config from some custom properties in the file its properties that are available when the custom tool is configured on a project file.
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System.Text;
using NJsonSchema.CodeGeneration.CSharp;
using NJsonSchema;
namespace ClassGeneratorForJsonSchema
{
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("GenerateClassesFromJsonSchema", "Use NJsonSchema to generate code from a json schema.", "1.0")]
[Guid("202E7E8B-557E-46CB-8A1D-3024AD68F44A")]
[ComVisible(true)]
[ProvideObject(typeof(ClassGeneratorForJsonSchema))]
[CodeGeneratorRegistration(typeof(ClassGeneratorForJsonSchema), "ClassGeneratorForJsonSchema", "{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}", GeneratesDesignTimeSource = true)]
public sealed class ClassGeneratorForJsonSchema : IVsSingleFileGenerator
{
#region IVsSingleFileGenerator Members
public int DefaultExtension(out string pbstrDefaultExtension)
{
pbstrDefaultExtension = ".cs";
return pbstrDefaultExtension.Length;
}
public int Generate(string wszInputFilePath, string bstrInputFileContents,
string wszDefaultNamespace, IntPtr[] rgbOutputFileContents,
out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
{
try
{
var schema = JsonSchema.FromJsonAsync(bstrInputFileContents).Result;
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings()
{
JsonLibrary = CSharpJsonLibrary.SystemTextJson,
ClassStyle = CSharpClassStyle.Record,
Namespace = wszDefaultNamespace
});
byte[] bytes = Encoding.UTF8.GetBytes(generator.GenerateFile());
int length = bytes.Length;
rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(length);
Marshal.Copy(bytes, 0, rgbOutputFileContents[0], length);
pcbOutput = (uint)length;
}
catch (Exception ex)
{
pcbOutput = 0;
}
return VSConstants.S_OK;
}
#endregion
}
}
I'm running LINQ queries against a DB model of traffic data.
The html page has a dropdown menu from which I will get a selectedInjury value to modify the query results (it is mocked here). I cannot figure out how to pass the value to the Controller.
Each variation I've tried responds with
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
This is the Javascript I'm trying to get working:
var mockSelectedInjury = "FATAL";
$.getJSON("/api/query1/selectedInjury", mockSelectedInjury)
.done(function (data) {
$.each(data, function (key, crash) {
$('<li>', { text: formatCrashReport(crash) }).appendTo($('#crashTable'));
});
});
This is the method I've written in the controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using final.Models;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace final.Controllers
{
public class CrashController : ApiController
{
ChicagoTrafficEntities myEntities = new ChicagoTrafficEntities();
[Route("api/query1")]
[HttpGet]
public IHttpActionResult GetQuery(string selectedInjury)
{
List<CrashReport> returnList = new List<CrashReport>();
//get a selection of all the data where injury matches
var records = from report in myEntities.CrashReports
where report.MostSevereInjury == selectedInjury
select new
{
PrimeContributingCause = report.PrimeContributingCause,
};
// add the query items to a list
foreach (var item in records)
{
CrashReport temp = new CrashReport
{
PrimeContributingCause = item.PrimeContributingCause
};
returnList.Add(temp);
}
// create a new query from that set that includes a count of the
// occurrences of the injury per the primeCause.
var foo = returnList.GroupBy(n => n.PrimeContributingCause).
Select(group =>
new
{
PrimeContributingCause = group.Key,
NumberInjuries = group.Count()
}).OrderByDescending(n => n.NumberInjuries);
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, Json(foo)));
}
}
}
If your Web API expects a URL that looks like api/query1/FATAL, then you should construct the URL in JavaScript accordingly:
var mockSelectedInjury = "FATAL";
$.getJSON("/api/query1/" + mockSelectedInjury)
.done(function (data) {
// ...
});
Alternative: Using query parameters
If you want to send Query-Parameters (instead of parameters being part of the path), then you construct an object and pass that as the second parameter:
var paramContainer = {'selectedInjury': 'FATAL'};
$.getJSON("/api/query1", paramContainer)
.done(function (data) {
// ...
});
The signature of your API method would look like this:
[Route("api/query1")]
[HttpGet]
public IHttpActionResult GetQuery(string selectedInjury)
{
}
I have seen these SO answers:
Return an image from asp.net web api core as IActionResult
Response.Body.WriteAsync base64 string not working
but these either serve physical files directly or serve from a byte array.
Please is there a way for our Web API Endpoint to return content-type: image/png directly from data:image/png;base64,(our base64 string)
It is quite straight forward to read a file from a folder. The trick is to use IHostingEnvironment in ASP.NET Core to get the current web root path.
FilesController
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace DemoWebApiCore.Controllers
{
[Route("api/[controller]")]
public class FilesController : Controller
{
private readonly IHostingEnvironment _hostingEnvironment;
public FilesController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
// GET api/files/sample.png
[HttpGet("{fileName}")]
public string Get(string fileName)
{
string path = _hostingEnvironment.WebRootPath + "/images/" + fileName;
byte[] b = System.IO.File.ReadAllBytes(path);
return "data:image/png;base64," + Convert.ToBase64String(b);
}
}
}
Usage
HomeController.cs
using Microsoft.AspNetCore.Mvc;
namespace DemoWebApiCore.Controllers
{
public class HomeController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return View();
}
}
}
Index.cshtml
<html>
<body>
<img id="sample-img" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
var url = "/api/files/sample.png";
$.get(url, function (data) {
console.log(data);
$("#sample-img").attr('src', data);
});
})
</script>
</body>
</html>
If you want to return images as base64 string you can do it like this:
public IHttpActionResult GetImage()
{
var image = GetImageData();
var base64 = Convert.ToBase64String(image);
return Ok(base64);
}
I am developing an ASP.NET application that uses ODataApiController. The application shows users a grid by querying data and showing it in a table. I would like the ability to export to a number of different formats, including CSV and a custom XML format. Ideally, I would just take the same OData query the grid uses, set the Accepts header, and get back CSV or XML.
I've created MediaTypeFormatters to do what I need, but they only work with "regular" ApiController, not ODataApiController. Looking at the code in github, I see that OData has it's own MediaTypeFormatter scheme to handle various cases, and built in XML and JSON formatters. But I can't see how to hook into this to provide custom formats. I've attempted inheriting ODataMediaTypeFormatter, but a breakpoint set on it never hits.
I am only really interested in output formats here. How can I extend OdataApi to output different formats?
You can use MediaTypeFormatter on OData queries as well. Just add a new class to your project that inherit MediaTypeFormatter. Then add this to your WebApiConfig file on Register:
config.Formatters.Add(new JSONPFormatter(new QueryStringMapping("$format","jsonp","application/javascript")));
If you then query your entity with the $format=jsonp it will return the entity as JSONP. You can also request it with the contenttype application/javascript to get a JSONP return.
Here is a full example for a MediaFormatter for JSONP return. You could easily change it for your need:
using MyProject.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.ServiceModel.Syndication;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using Newtonsoft.Json;
namespace SMSIdent.Modules.Formatter
{
/// <summary>
/// Adds a $format=jsop to all odata query
/// </summary>
public class JSONPFormatter : MediaTypeFormatter
{
private readonly string jsMIME = "application/javascript";
public JSONPFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue(jsMIME));
}
public JSONPFormatter(MediaTypeMapping mediaTypeMapping) : this()
{
MediaTypeMappings.Add(mediaTypeMapping);
}
//THis checks if you can POST or PUT to this media-formater
public override bool CanReadType(Type type)
{
return false;
}
//this checks if you can GET this media. You can exclude or include your Resources by checking for their types
public override bool CanWriteType(Type type)
{
return true;
}
//This actually takes the data and writes it to the response
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
//you can cast your entity
//MyType entity=(MyType) value;
var callback=HttpContext.Current.Request.Params["callback"];
return Task.Factory.StartNew(() =>
{
using (StreamWriter sw = new StreamWriter(writeStream))
{
if (string.IsNullOrEmpty(callback))
{
callback = "values";
}
sw.Write(callback + "(" + JsonConvert.SerializeObject(value, Newtonsoft.Json.Formatting.None,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}) + ")");
}
});
}
}
}
Note: I'am using Web API 2. I don't know exactly if it also works in Web Api 1.
I need to write a function that help me do something in some of my Controllers so I decided to creat a class called Helper for that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace HocVuiDiary.Helper
{
public class CookiesHelper
{
public void UpdateSubkey(string name, string subkey, string subvalue)
{
HttpCookie cookie;
if (Request.Cookies[name] == null)
{
cookie = new HttpCookie(name);
cookie[subkey] = subvalue;
}
else
{
cookie = Request.Cookies[name];
cookie[subkey] = subvalue;
}
cookie.Expires = DateTime.Now.AddDays(30);
Response.Cookies.Add(cookie);
}
}
}
The issue is I cannot Access to Request or Response any more!
PLease show me the right way!
You can use HttpContext.Current.Request and HttpContext.Current.Response in your helper class.
While the first answer is technically accurate, I am running into issues of inconsistency with creation of the cookie using an external .DLL. The code behind class calls the methods in the external .dll, the cookie is created, but after navigating to the next page the cookie does not exist, sometimes.
public void CreateCookie(string cookieName, string key, string value)
{
int minutes = 95;
string encryptedValue = utilities.EncryptString(value);
HttpCookie cookie = new HttpCookie(cookieName);
cookie[key] = encryptedValue;
cookie.Expires = DateTime.Now.AddMinutes(minutes);
HttpContext.Current.Response.Cookies.Add(cookie);
}
Other calls to the external class are working as expected.
public bool CheckCookieExists(string cookieName)
{
bool exists = true;
HttpCookie cookie = HttpContext.Current.Request.Cookies[cookieName];
if (cookie != null)
{
return exists;
}
return exists = false;
}
It's basically the same as accessing the session. Use httpcontext.current although it is frowned upon at times there is mention here of cleaning it up:
Can I use ASP.NET Session[] variable in an external DLL
Here you could define an interface like IRequest to abstract the specific implementation out but that's up to you.