別の開発者は、Crystal レポートの大規模なコレクションを維持しています。完全な Crystal Reports Server 製品を必要とせずに、ASP.NET MVC3 ページでこれらのレポートを利用できるようにする必要があります。
現在のレポート サイトは、Prompt0&Prompt1 など、すべての引数が渡された従来の ASP ページです。
そのために、MVC アプリにある aspx ページを作成し、これらのレポートをアプリのディレクトリから次のように提供します。
パブリック部分クラス レポート: System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e)
{
iLogger logger = LoggingFactory.CreateLogger();
ReportDocument rd = new ReportDocument();
string fileName = Request.QueryString["reportfile"];
if(!Regex.IsMatch(fileName,@"^[ 0-9a-zA-Z-_\\]+.rpt$"))
{
//log and throw
}
if(Path.IsPathRooted(fileName))
{
//log and throw
}
string rootPath = Server.MapPath("~/Reports/");
string path = Path.Combine(rootPath, fileName);
if (File.Exists(path))
{
rd.Load(path);
}
//get all keys starting with Prompt
var prompts = Request.QueryString.AllKeys.Where(q => q.StartsWith("Prompt"));
foreach (string promptKey in prompts)
{
//try to convert the rest of the string to an int
//yes, this should probably not just be a replace here...
string withoutPrompt = promptKey.Replace("Prompt", "");
int promptVal;
if (int.TryParse(withoutPrompt, out promptVal))
{
rd.SetParameterValue(promptVal, Request.QueryString[promptKey]);
}
//rd.SetParameterValue(promptKey, Request.QueryString[promptKey]);
}
CrystalReportViewer1.ReportSource = rd;
}
}
これは、労力の割に驚くほどうまく機能します (レポート デザイナーは、レポート/クエリ ページ内のリンクを、たとえば mywebserver.foo/Report1.rpt?Prompt....etc.etc から mywebserver.foo/mymvcreport/ に変更するだけで済みます)。 report.aspx?Filename=report1.rpt&Prompt... など
とてもすばらしいので、すぐに MVC アプリに移行して、10 のサイトを出向いて Crystal Server を購入する必要がなくなります。
私の明らかな懸念は、ファイル名の引数に、「C:\foo\bar」や「../bla/blah」など、誰かがそこに何でも入れることができるということです。これらのファイル名をエスケープし、それがアプリへのローカル パスであることを確認するための単一のベスト プラクティスはありますか?
たとえば、 /Sales/Quarterly/Quarterly.rpt のパラメーターを取得できるようにしたいと思います
私の最初の考えは、たとえば [0-9a-zA-z-_\]+ の正規表現を使用して、コロンまたはドット文字を使用できないようにすることです。これを処理する最も完全な方法に関する提案はありますか?
ありがとう!
編集:
私が入れた予備チェックで更新されました...