Any reason why ServiceStack would ignore the Accept header? The service is hosted in a ASP.NET app and running in debug within the IDE. The first 40 or so calls to the service, using a System.Web.WebRequest object causes the service to respond correctly. After approximately 50 calls a 404 error is detected by the client (breakpoint not hit in the service). From that point forward, the Accept header is ignored. All subsequent requests always return XML.
The client being used...
var client = (HttpWebRequest)WebRequest.Create(uri);
client.Method = WebRequestMethods.Http.Post;
client.AllowWriteStreamBuffering = false;
client.SendChunked = true;
client.ContentType = "multipart/form-data;";
client.Timeout = int.MaxValue; // HACK:REMOVE
client.Accept = "application/json";
The call is a bit messy right now (trying to debug the failure)...
using (FileStream fileStream = File.OpenRead(filePaths[i]))
{
fileStream.Copy(client.GetRequestStream());
}
var responseString = string.Empty;
try { responseString = new StreamReader(client.GetResponse().GetResponseStream()).ReadToEnd(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (String.IsNullOrWhiteSpace(responseString)) { continue; }
PutFileResponse response = null;
try { response = responseString.FromJson<PutFileResponse>(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (response == null)
{
try { response = responseString.FromXml<PutFileResponse>(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (response == null)
{
continue;
}
}
I left this as-is to show the the response. The first 50 (approx) calls return JSON as requested. After the 404 all subsequent calls always return XML.
Any thoughts?
Edit (2014-02-25 10:35 EST):
After looking at Fiddler this is a bit more odd than I had thought. Of 559 requests, 34 of them result in 404 errors. However the service continues to respond both before and after the error without issue. The 404 error is the first puzzling part. The second item (the switch between XML and JSON is a bit less puzzling but strange nonethless.
The app is a file storage app and is recursing a test directory to push files to the service. Some of the files it is encountering are actual XML files. All files are sent in a Stream, nested in a DTO, with the client adding an Accept header for "application/json" for each request. If an XML file is sent, even though the Accept header has been sent, the service responds with XML.
Example Request Header (session 94):
POST
http://localhost:50205/Files/Put/8178F94DBDBC4AB18F42118AFD01D1A2/AA10C004D624DA892171F8A7E8CD8D05/201760/ServiceStack.xml HTTP/1.1
Content-Type: multipart/form-data;
Accept: application/json
Host: localhost:50205
Transfer-Encoding: chunked
Expect: 100-continue
1000
<?xml version="1.0"?>
<doc>
[SNIP]
</doc>
0
Example Response Header (session #94):
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/xml
Server: Microsoft-IIS/8.0
X-Powered-By: ServiceStack/4.011 Win32NT/.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcX1NvdXJjZVxGSUwwMVx0cnVua1xTcG90bGVzc1xGcmFtZXdvcmtzXEZpbGVNYW5hZ2VtZW50XFByb2plY3RzXEZNRi5TdG9yYWdlU2VydmVyLkhvc3RpbmcuUHVibGljXEZpbGVzXFB1dFw4MTc4Rjk0REJEQkM0QUIxOEY0MjExOEFGRDAxRDFBMlxBQTEwQzAwNEQ2MjREQTg5MjE3MUY4QTdFOENEOEQwNVwyMDE3NjBcU2VydmljZVN0YWNrLnhtbA==?=
X-Powered-By: ASP.NET
Date: Tue, 25 Feb 2014 15:19:06 GMT
Content-Length: 563
<?xml version="1.0" encoding="utf-8"?><PutFileResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/FMF.StorageServer.Services.Messages.Files"><ResponseStatus xmlns:d2p1="http://schemas.servicestack.net/types" i:nil="true" /><Status><FileSignature><Checksum>AA10C004D624DA892171F8A7E8CD8D05</Checksum><SizeBytes>201760</SizeBytes></FileSignature><IsAvailable i:nil="true" /><IsKnown i:nil="true" /><IsOnDisk i:nil="true" /><IsSuccessful i:nil="true" /><StatusMessage i:nil="true" /></Status></PutFileResponse>
The unfortunate part of this is that I would have to detect the inner structure of every file before sending it to the server and could never trust the file extension. Either that or always assume that the server might decide to send back XML when I didn't expect it.
A more pressing concern would be why the 404 errors are being detected for only SOME of the requests. In 559 requests, the items producing a 404 error are 77, 232, 235, 238, 246, 275, etc... so the service or client is just failing on random requests.
Edit (2014-02-25 12:20 EST):
It appears as if ALL of the files that failed (404 error) were text-based. For example...
Example Request Header (session #560):
POST http://localhost:50205/Files/Put/060C976372174F51BEB84FE524E57C57/1931975CE8E1090A6D66738A560888AD/1426/AssemblyInfo.cs HTTP/1.1
Content-Type: multipart/form-data;
Accept: application/json
Host: localhost:50205
Transfer-Encoding: chunked
Expect: 100-continue
592
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Utility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Utility")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1071992e-2d4c-49df-9526-6d4d29f979b4")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
0
Example Response Header (session #560):
HTTP/1.1 404 Not Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?RDpcX1NvdXJjZVxGSUwwMVx0cnVua1xTcG90bGVzc1xGcmFtZXdvcmtzXEZpbGVNYW5hZ2VtZW50XFByb2plY3RzXEZNRi5TdG9yYWdlU2VydmVyLkhvc3RpbmcuUHVibGljXEZpbGVzXFB1dFwwNjBDOTc2MzcyMTc0RjUxQkVCODRGRTUyNEU1N0M1N1wxOTMxOTc1Q0U4RTEwOTBBNkQ2NjczOEE1NjA4ODhBRFwxNDI2XEFzc2VtYmx5SW5mby5jcw==?=
X-Powered-By: ASP.NET
Date: Tue, 25 Feb 2014 15:24:10 GMT
Connection: close
Content-Length: 5106
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IIS 8.0 Detailed Error - 404.7 - Not Found</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;}
code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;}
.config_source code{font-size:.8em;color:#000000;}
pre{margin:0;font-size:1.4em;word-wrap:break-word;}
ul,ol{margin:10px 0 10px 5px;}
ul.first,ol.first{margin-top:5px;}
fieldset{padding:0 15px 10px 15px;word-break:break-all;}
.summary-container fieldset{padding-bottom:5px;margin-top:4px;}
legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;}
legend{color:#333333;;margin:4px 0 8px -12px;_margin-top:0px;
font-weight:bold;font-size:1em;}
a:link,a:visited{color:#007EFF;font-weight:bold;}
a:hover{text-decoration:none;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;}
h4{font-size:1.2em;margin:10px 0 5px 0;
}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif;
color:#FFF;background-color:#5C87B2;
}#content{margin:0 0 0 2%;position:relative;}
.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
.content-container p{margin:0 0 10px 0;
}#details-left{width:35%;float:left;margin-right:2%;
}#details-right{width:63%;float:left;overflow:hidden;
}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF;
background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal;
font-size:1em;color:#FFF;text-align:right;
}#server_version p{margin:5px 0;}
table{margin:4px 0 4px 0;width:100%;border:none;}
td,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:normal;border:none;}
th{width:30%;text-align:right;padding-right:2%;font-weight:bold;}
thead th{background-color:#ebebeb;width:25%;
}#details-right th{width:20%;}
table tr.alt td,table tr.alt th{}
.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;}
.clear{clear:both;}
.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;}
-->
</style>
</head>
<body>
<div id="content">
<div class="content-container">
<h3>HTTP Error 404.7 - Not Found</h3>
<h4>The request filtering module is configured to deny the file extension.</h4>
</div>
<div class="content-container">
<fieldset><h4>Most likely causes:</h4>
<ul> <li>Request filtering is configured for the Web server and the file extension for this request is explicitly denied.</li> </ul>
</fieldset>
</div>
<div class="content-container">
<fieldset><h4>Things you can try:</h4>
<ul> <li>Verify the configuration/system.webServer/security/requestFiltering/fileExtensions settings in applicationhost.config and web.config.</li> </ul>
</fieldset>
</div>
<div class="content-container">
<fieldset><h4>Detailed Error Information:</h4>
<div id="details-left">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="alt"><th>Module</th><td> RequestFilteringModule</td></tr>
<tr><th>Notification</th><td> BeginRequest</td></tr>
<tr class="alt"><th>Handler</th><td> ServiceStack.Factory</td></tr>
<tr><th>Error Code</th><td> 0x00000000</td></tr>
</table>
</div>
<div id="details-right">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="alt"><th>Requested URL</th><td> http://localhost:50205/Files/Put/060C976372174F51BEB84FE524E57C57/1931975CE8E1090A6D66738A560888AD/1426/AssemblyInfo.cs</td></tr>
<tr><th>Physical Path</th><td> D:\_Source\FIL01\trunk\Spotless\Frameworks\FileManagement\Projects\FMF.StorageServer.Hosting.Public\Files\Put\060C976372174F51BEB84FE524E57C57\1931975CE8E1090A6D66738A560888AD\1426\AssemblyInfo.cs</td></tr>
<tr class="alt"><th>Logon Method</th><td> Not yet determined</td></tr>
<tr><th>Logon User</th><td> Not yet determined</td></tr>
<tr class="alt"><th>Request Tracing Directory</th><td> C:\Users\Fred\Documents\IISExpress\TraceLogFiles\FMF.STORAGESERVER.HOSTING.PUBLIC</td></tr>
</table>
<div class="clear"></div>
</div>
</fieldset>
</div>
<div class="content-container">
<fieldset><h4>More Information:</h4>
This is a security feature. Do not change this feature unless the scope of the change is fully understood. If the file extension for the request should be allowed, remove the denied file extension from configuration/system.webServer/security/requestFiltering/fileExtensions.
<p>View more information »</p>
</fieldset>
</div>
</div>
</body>
</html>
Edit (2014-02-25 14:27 EST):
Continuing to test and finding that ServiceStack ignoring the Accept header IS a bigger problem than I hoped. Since all files must be persisted, and since those files must include both HTML and XML files, I need to ensure ServiceStack only sends back the response that was requested. Some of the files sent in my last test included HTML files and, quite unfortunately, ServiceStack sent back an HTML document as the response.
The temp folder contains a bunch of random files. And, as you might expect, because I have a ton of source files on hand, the temp folder includes a few C#/VS2K12 solutions. For example, I copied in the source of DoFactory's solution and several of its .Config, .cs, .csproj files fail while others of the same type go through.
The DTO being used...
//[Route("/Files/Put/{Token}/{Checksum}/{SizeBytesText}/{FileNameOrExtension}", "POST")]
[Route("/Files/Put/{PathInfo*}", "POST")]
public class PutFileRequest : IReturn<PutFileResponse>, IRequiresRequestStream
{
public string Token { get; set; }
public string Checksum { get; set; }
public string SizeBytesText { get; set; }
public string FileNameOrExtension { get; set; }
public System.IO.Stream RequestStream { get; set; }
}
I've intentionally included the original route I was using. Note that the URI is constructed using a set of variables and the name of the file. The name of the file is used for convenience on the server to allow the file to be persisted using the original file extension.
Below is the Main method from a test app which fails consistently. Any attempt to post this file will cause a 404 error. Note that the file is intact and that the FileSteam is able to open and copy the contents of the file successfully. Had a problem existed with the file then this should have failed.
static void Main(string[] args)
{
var filePath = #"D:\Temp\_Source\DoFactory\CS_4.5\Spark\Art.Web\Areas\Shop\Models\ProductsModel.cs";
var fileInfo = Files.GetInfo(filePath, calculateChecksum: true);
var uri = #"http://localhost:50205/Files/Put/" +
Guid.NewGuid().ToString("N") + "/" +
fileInfo.Checksum + "/" +
fileInfo.SizeBytes.Value + "/" +
System.IO.Path.GetFileName(filePath);
var client = (HttpWebRequest) WebRequest.Create(uri);
client.Method = WebRequestMethods.Http.Post;
client.AllowWriteStreamBuffering = false;
client.SendChunked = true;
client.ContentType = "multipart/form-data;";
client.Timeout = int.MaxValue;
client.Accept = "application/json";
using (FileStream fileStream = File.OpenRead(filePath))
{
fileStream.CopyTo(client.GetRequestStream());
}
var responseString = string.Empty;
try { responseString = new StreamReader(client.GetResponse().GetResponseStream()).ReadToEnd(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (String.IsNullOrWhiteSpace(responseString)) { Environment.Exit(1); }
PutFileResponse response = null;
try { response = responseString.FromJson<PutFileResponse>(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
if (response == null)
{
try { response = responseString.FromXml<PutFileResponse>(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
}
if (response == null)
{
try { response = responseString.FromJsv<PutFileResponse>(); }
catch (Exception ex) { Debug.WriteLine(ex.Message); }
}
if (response == null) { Environment.Exit(2); }
Console.ReadLine();
}
After a day+ of testing, and somewhere around 1,000 tests, I have found a way around the problem. However, in the findings leave me with a request to the ServiceStack team to find a way around this.
If a file is posted to a path which appears to contain a file name at the end, ServiceStack will respond with a result matching the contents of the item contained within the RequestStream. If the item is HTML the response will be an HTML response. If the item is XML the response will be an XML response. In these scenarios the Accept header is always ignored.
To navigate around this problem I have broken the filename into separate parts within the URL. A path that would originally resolve as:
http://localhost:1234/Files/Put/ABC123/MyFile.xml
... is now posting as this ...
http://localhost:1234/Files/Put/ABC123/F/MyFile/X/xml
... or, if I just need the extension, the parts of the extension are broken ...
http://localhost:1234/Files/Put/ABC123/X/tar/gz
In these examples, the service knows how to reassemble the file name and/or extension.
The end result to this change is that XML and HTML files are posted, as expected, and ServiceStack obeys the Accept header. In all tests after this change ServiceStack responded with the needed JSON object. Additionally, no 404 errors are encountered.
Related
I have a well functioning Azure Function App running multiple http triggered functions that provide JSON payloads.
I am trying to call a specific function from my Blazor WASM hosted on Azure Static Web App. I have linked the Function App to the Static Web App and all functions are visible in the Static Web App.
The Function App name displayed in the Static Web Apps Functions blade is my-existing-function-appand in the name list all functions are formatted: my-existing-function-app/GetMyStuff, my-existing-function-app/SetMyStuff, etc
The original uri for the function looks like this: https://my-existing-function-app.azurewebsites.net/api/GetMyStuff?code=ASDFkt5346345ywSDGSTy45734gowjgwWERT==. Calling that endpoint delivers my data as expected.
In my Blazor App I can activate the original endpoint when debugging locally by running this line: mystuff = await Http.GetFromJsonAsync<MyStuff[]>('https://my-existing-function-app.azurewebsites.net/api/GetMyStuff?code=ASDFkt5346345ywSDGSTy45734gowjgwWERT==');
My Static Web App url looks like this: https://red-sea-123.azurestaticapps.net
I have followed the documentation found here: https://learn.microsoft.com/en-us/azure/static-web-apps/functions-bring-your-own
According to the documentation it is a bit unclear, what I should write here: mystuff = await Http.GetFromJsonAsync<MyStuff[]>(HERE)
I have tried different variations like 'api/GetMyStuff', 'my-existing-function-app/GetMyStuff' and 'my-existing-function-app/api/GetMyStuff', but none of them get access to the endpoint. It is clear from the browsers developer console, that i get some error page back.
How should I construct the HERE string in my call?
Please check if the below steps for adding the await Http GetFromJsonAsync Method in the Blazor App and helps to workaround:
According to the documentation it is a bit unclear, what I should write here: mystuff = await Http.GetFromJsonAsync<MyStuff[]>(HERE)
Here is an article contains about the calling Azure Function App from the Blazor WASM.
This is the code where you have to include your await Http GetFromJsonAsync operation in the razor app:
#page "/covidfaq"
#inject HttpClient Http
<div class="d-flex justify-content-center">
<img src="../Images/COVID_banner.jpg" alt="Image" style="width:80%; height:300px" />
</div>
<br />
<div class="d-flex justify-content-center">
<h1>Frequently asked Questions on Covid-19</h1>
</div>
<hr />
#if (questionList == null)
{
<p><em>Loading...</em></p>
}
else
{
#foreach (var question in questionList)
{
<div class="card">
<h3 class="card-header">
#question.Question
</h3>
<div class="card-body">
<p class="card-text">#question.Answer</p>
</div>
</div>
<br />
}
}
#code {
private FAQ[] questionList;
protected override async Task OnInitializedAsync()
{
questionList = await Http.GetFromJsonAsync<FAQ[]>("https://faqfunctionapp20200611160123.azurewebsites.net/api/covidFAQ");
}
public class FAQ
{
public string Id { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
}
Inside the OnInitializedAsync method, the Azure Function app API Endpoint is hitting and the returned data stored in questionList array type variable.
Please visit the above article for more information.
In my ASP.NET Core 3.1 web application, I allow users to upload images that are stored in local directories within the application itself. Whilst, this could be better served using blob storage on Azure, this particular project has called for them to be stored locally, so I have to work with that:
wwwroot/images/products/whatever_id/whatever_name.jpg
and
wwwroot/images/companies/whatever_id/whatever_name.jpg
When a user uploads an image, the processing of the image is handled by ImageSharp from SixLabors where the image is resized a few times for use across the platform and saved to the relative directory which is separated by the Id.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
The Problem
The problem I face is that, whilst this process works when I test my application locally, it doesn't work when I deploy my application to Azure and there are no errors of any kind reported back. This has left me high and dry when trying to work out what is going on.
Assumptions
Due to the nature of this issue and the location of these images, I can only assume that it's azure preventing the overwriting of images within the directories for security reasons, or perhaps it's the ImageSharp library itself.
It's important to note that, the actual creation of products and adding of images when the directories don't exist, so, a new product, works perfectly. It's only if you try to overwrite the image that it doesn't work.
Here is my code, I've removed all none essential elements, leaving on the image processing specific code.
Edit(View)
#model Products
<form asp-action="Edit" asp-controller="Products" method="POST" enctype="multipart/form-data">
<div class="card m-4">
<div class="card-header">
<h3 class="card-title">Add Product</h3>
</div>
<div class="card-body">
<div class="container-fluid">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Product Images</label>
<kendo-upload name="ProductImages" multiple="true" show-file-list="true">
</kendo-upload>
</div>
</div>
</div>
<div class="row">
<div class="col">
<button type="submit" class="btn btn-purple">Submit</button>
</div>
</div>
</div>
</div>
</div>
</form>
Edit(Controller)
[HttpPost]
public IActionResult Edit(Products product)
{
if (ModelState.IsValid && product != null)
{
try
{
//Process the Images
if (product.ProductImages != null)
{
ProcessImages(product, product.Id);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return RedirectToAction("Index");
}
return View();
}
ProcessImages(Stream)
private readonly int[] sizeArray = new int[] { 700, 350, 150 };
public Stream ProcessImages(Products model, int? id)
{
try
{
//Get the images and define the directory structure
var images = model.ProductImages;
var root = _env.WebRootPath;
var folderName = Path.Combine(root, "images", "products", id.ToString());
//If the ID Directory doesn't exist, create it first.
if (!Directory.Exists(folderName))
{
Directory.CreateDirectory(folderName);
}
//Interate over the images and process them
foreach (var item in images)
{
foreach (var imageSize in sizeArray)
{
string imageSizeName = "image" + imageSize + ".jpg";
string fullPath = Path.Combine(folderName, imageSizeName);
//Create the stream and process the image
using FileStream fileStream = new FileStream(fullPath, FileMode.Create);
try
{
Stream inStream = item.OpenReadStream();
using Image image = Image.Load(inStream);
int width = imageSize;
int height = 0;
var clone = image.Clone(i => i.Resize(width, height));
clone.SaveAsJpeg(fileStream);
inStream.Position = 0;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return (null);
}
As you can see, there is a size array, where the sizes we need are defined and then looped over and processed. We create the file name and save as a jpg. The height is set to 0 so that it automatically sets it when the width is defined and then the stream is reset.
Products.cs(model)
public class Products : BaseEntity
{
public string Title { get; set; }
[NotMapped]
public IFormFileCollection ProductImages { get; set; }
}
So, the question remains, why can I not overwrite my images once my application is live in Azure? Is it an Azure security concern, something simple like an ImageSharp library issue or is my application not performing this action correctly?
That code does not look correct. In ProcessImages
You're loading the image then cloning it for each size (load outside the loop)
You're not disposing of the clone after saving it to the stream.
You're always returning null.
#LazZiya is correct here though, regarding caching. Since you're reusing the same name over again, the browser will simply request the same cached image. If you add any querystring parameter in your e.g. v=[PRODUCTRECORD_LASTWRITETIME] you will get the new image.
For simplicities sake I recommend you simply upload the source image and use the ImageSharp.Web middleware to serve your resized images.
This will automatically handle source image changes and reduce storage overheads. You can host your source images on the server and the cached result in blob storage.
requests become as simple as
https://PATH_TO_IMAGE?with=[NUMBER]
I'm using ServiceStack with the Razor plugin and returning response objects from my service methods. If the service uses a default view then the response is fine, but if I specify a View in the service method then the http response includes additional data surrounding the content.
Here's a test service class:
[DefaultView("TestView")]
public class TestService : RestServiceBase<Req>
{
public override object OnGet(Req request)
{
var response = new Req { Data = "Hello" };
if (string.IsNullOrEmpty(request.Data))
{
return response;
}
return new HttpResult(response) { View = "TestView" };
}
}
An example raw response including the additional content data looks like this:
HTTP/1.1 200 OK
Server: nginx/1.2.3
Date: Thu, 03 Jan 2013 12:46:33 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: ServiceStack/3.928 Unix/Mono
X-AspNet-Version: 4.0.30319
Cache-Control: private
Set-Cookie: ss-id=KvFQKAyOkkKMajEFeaLiKQ==; path=/
Set-Cookie: ss-pid=nrv3gwEL6EuRE9B8VpAWZA==; path=/; expires=Mon, 03 Jan 2033 12:43:57 GMT
b1
a4
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Hello</p>
</body>
</html>
0
0
They look like content length values and end markers. I guess I have something mis-configured, but don't know what. Any ideas?
I have a merge task in the publisher area of my CruiseControl.NET project that successfully copies a collection of html and png files to the artifacts directory. I've enabled the HtmlReportPlugin in the dashboard like this:
<buildPlugins>
<buildReportBuildPlugin>
<xslFileNames>
<xslFile>xsl\header.xsl</xslFile>
<xslFile>xsl\compile.xsl</xslFile>
<xslFile>xsl\modifications.xsl</xslFile>
<xslFile>xsl\MsTestSummary2010.xsl</xslFile>
</xslFileNames>
</buildReportBuildPlugin>
<buildLogBuildPlugin />
<xslReportBuildPlugin description="MSTest2010 Report" actionName="MSTestBuildReport2010" xslFileName="xsl\MsTestReport2010.xsl"></xslReportBuildPlugin>
<xslReportBuildPlugin description="MSTest Coverage 2010" actionName="MSTestBuildCoverReport2010" xslFileName="xsl\MsTestCover2010.xsl"></xslReportBuildPlugin>
<htmlReportPlugin description="Html Report" actionName="HtmlReport" htmlFileName="index.html" />
</buildPlugins>
The index.html is served just fine, but relative links within index.html don't seem to work. Here's the source to index.html:
<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN'
'http://www.w3.org/TR/html4/frameset.dtd'>
<html><head><title>
all.cov
</title>
<meta http-equiv=Content-Type content='text/html;charset=utf-8'>
</head>
<frameset cols='25%,75%'>
<frame src=nav-folder.html>
<frame name=right src=p0.html>
<noframes>
<p>Your browser does not support frames. See <a href=nav-folder.html>nav-folder.html</a>
</noframes>
</frameset>
</html>
And here's one of the errors that I get when loading index.html (replace 'nav-folder' with 'p0' for the other error message):
Server Error in '/' Application.
Unknown object name : nav-folder
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ApplicationException: Unknown object name :
nav-folder
Source Error:
An unhandled exception was generated during the execution of the
current web request. Information regarding the origin and location of
the exception can be identified using the exception stack trace below.
Stack Trace:
[ApplicationException: Unknown object name : nav-folder]
Objection.ObjectionStore.GetByName(String name) +307
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.CruiseActionFactory.CreateHandler(String
actionName) +231
ThoughtWorks.CruiseControl.WebDashboard.MVC.Cruise.CruiseActionFactory.Create(IRequest
request) +38
ThoughtWorks.CruiseControl.WebDashboard.MVC.RequestController.Do()
+155 ThoughtWorks.CruiseControl.WebDashboard.MVC.ASPNET.HttpHandler.ProcessRequest(HttpContext
context) +651
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
+625 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +27
If I use RetrieveBuildFile.aspx on either of the reference files (for example, http://localhost/server/local/project/Code_Coverage/build/log20120809181334Lbuild.168.xml/RetrieveBuildFile.aspx?file=p0.html), the files load without a problem, but as in the first example, any relative files will fail to load.
Is there something I have to do in my CruiseControl.NET web.config to get IIS to correctly resolve relative file paths? I am using CruiseControl.NET 1.8.0 and I am running IIS 7 running on Windows 2008, and I have verified that the same issue occurs on a CruiseControl.NET 1.6?? on IIS running on Windows 7.
Unfortunately HTML tidy didn't like the Bullseye CodeCoverage HTML as it contains invalid HTML. So that solution won't work.
CruiseControl uses a regular expression to find source links, but this isn't perfect because it misses the case where HTML attributes don't have quotes and it could match string fragments that are not HTML attributes. I've modified the CruiseControl.NET source code to use the HtmlAgilityPack to parse the HTML and this seems to work very well (at least on my test cases.)
If you need to solve this problem (without looking on your own where CCnet parses html). I'm using ReportGenerator with CCnet. Here's an example:
In class:
BuildFileDownload (namespace ThoughtWorks.CruiseControl.WebDashboard.Plugins.BuildReport)
Modify Execute procedure by using this code:
HtmlDocument doc = new HtmlDocument();
doc.OptionAddDebuggingAttributes = false;
doc.OptionAutoCloseOnEnd = false;
doc.OptionCheckSyntax = false;
doc.OptionFixNestedTags = false;
doc.OptionOutputOptimizeAttributeValues = false;
doc.LoadHtml(htmlData);
var nodes = doc.DocumentNode.SelectNodes("//a[#href]");
if (nodes != null)
{
foreach (var link in nodes)
{
HtmlAttribute att = link.Attributes["href"];
if (!att.Value.StartsWith("data:") && !att.Value.StartsWith("#") && !att.Value.StartsWith("http://"))
att.Value = #"RetrieveBuildFile.aspx?file=coverageReport\" + att.Value;
}
htmlData = doc.DocumentNode.WriteTo();
}
nodes = doc.DocumentNode.SelectNodes("//link[#href]");
if (nodes != null)
{
foreach (var link in nodes)
{
HtmlAttribute att = link.Attributes["href"];
if (!att.Value.StartsWith("data:") && !att.Value.StartsWith("#") && !att.Value.StartsWith("http://"))
att.Value = #"RetrieveBuildFile.aspx?file=coverageReport\" + att.Value;
}
htmlData = doc.DocumentNode.WriteTo();
}
Remember to get rid of old regEx parsing first. It's a sample code but it fits all my needs. This solution uses HtmlAgilityPack. Note that path given as a prefix for new attribute value may differ.
The issue was that CruiseControl.NET doesn't like HTML with attributes with values that aren't quoted. I'm using output generated by Bullseye CodeCoverage and that output has attributes with values that aren't quoted. I'm using HTML Tidy to add quotes.
Here my problem:
I have an input text and behind a date picker ui: and I would like to get the datepicker's value in razor:
Index.cshtml
<input id="datePickerCalendar" type= "text"/>
<script type="text/javascript">
$(document).ready(function () {
$('#datePickerCalendar').datepicker({
altFormat: "dd-mm-yy",
dayNamesMin: ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"],
monthNames: ["Janvier", "Fevrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
changeMonth: true,
onSelect: function () {
/*('#datePickerCalendar').change(loadCalendar());*/
}
});
});
</script>
<table border="1" class="tableCalendar" id="calendar">
<caption> Veuillez sélectionner l'horaire souhaité </caption>
<th id="court"></th>
#foreach(var item in Model) {
foreach(var court in item.TennisCourts){
if (court.Outside == true)
{
<td id="court" class="court">Court n°#court.Number (Extérieur)</td>
}
else
{
<td id="court" class="court">Court n°#court.Number (Intérieur)</td>
}
}
}
#foreach (var item in Model)
{
var chooseDate = $('#datePickerCalendar').value; // here ! This instruction is not correct...
}
I'm Building a dynamic calendar that allow the user to make a reservation for a tennis court...
So, my questions are:
1)How to get the value from the datepicker in razor ?
2)How can I get the value every time when the user change the date ?
Thanks in advance
You need to post your value to a action method on the controller, surround the field with a form
#using (Html.BeginForm("Controller", "Action", FormMethod.Post))
{
}
Then change your field into a server side rendered one (So the model binder can capture the new value)
#Html.TextBoxFor(m => m.MyDate)
The action method needs to take the model as argument and it needs to have the DateTime property named MyDate
edit:
If you will be sending values from the server you need to be sure the client datepicker and the serer uses the same date format. This is a bit tricky, but I did this with the globalize jquery plugin, you have to choose if you want to hardcode the ui culture, or if the sever will use the client culture. This is done in web.config
Hardcoded
<globalization culture="se-SE" uiCulture="se-SE" enableClientBasedCulture="false" />
Client chooses
<globalization enableClientBasedCulture="true" />
edit2
Sorry for all my edits :D
A good way for sending server settings like datetime and such is to create a settings razor view and change its mime type to javascript, also be sure to have caching otherwise the client will load it every time
#{
Layout = null;
Response.Expires = 120;
Response.CacheControl = "public";
Response.ContentType = "text/javascript";
}
MyAppName = {};
MyAppName.settings = {
culture: "#Thread.CurrentThread.CurrentCulture.Name",
timeFormat: "#Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortTimePattern.ToLower()",
dateFormat: "#Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern.ToLower().Replace("yyyy", "yy")",
}