Cara membaca ASP.NET Inti Respon.Tubuh?
I've telah berjuang untuk mendapatkan Respon.Tubuhproperti dari ASP.NET Inti aksi dan satu-satunya solusi I've telah mampu mengidentifikasi tampaknya sub-optimal. Solusi membutuhkan swapping out
Respon.Tubuhdengan
MemoryStreamsaat membaca stream ke dalam variabel string, kemudian menukar kembali sebelum dikirim ke client. Dalam contoh di bawah ini, saya'm mencoba untuk mendapatkan Respon.Tubuh
nilai di custom middleware kelas. Respon.Tubuh
adalah set satu-satunya properti di ASP.NET Inti untuk beberapa alasan? Aku hanya kehilangan sesuatu di sini, atau ini merupakan pengawasan/bug/masalah desain? Apakah ada cara yang lebih baik untuk membaca Respon.Tubuh`?
Saat ini (sub-optimal) solusi:
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
using (var swapStream = new MemoryStream())
{
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await _next(context);
swapStream.Seek(0, SeekOrigin.Begin);
string responseBody = new StreamReader(swapStream).ReadToEnd();
swapStream.Seek(0, SeekOrigin.Begin);
await swapStream .CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
}
}
}
Berusaha larutan menggunakan EnableRewind():
Ini hanya bekerja untuk Permintaan.Tubuh
, bukan Respon.Tubuh
. Ini hasil dalam membaca sebuah string kosong dari Respon.Tubuh
daripada yang sebenarnya respon tubuh isi.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifeTime)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
app.UseMyMiddleWare();
app.UseMvc();
// Dispose of Autofac container on application stop
appLifeTime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}
MyMiddleWare.cs
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next(context);
string responseBody = new StreamReader(context.Request.Body).ReadToEnd(); //responseBody is ""
context.Request.Body.Position = 0;
}
}
Di awal saya respon saya telah benar-benar salah membaca pertanyaan dan pemikiran poster bertanya bagaimana membaca
Permintaan.Tubuh
Tapi dia bertanya bagaimana membacaRespon.Tubuh
. I'm meninggalkan saya asli jawab untuk melestarikan sejarah, tetapi juga memperbarui itu untuk menunjukkan bagaimana saya akan menjawab pertanyaan setelah membaca dengan benar.Jawaban asli
Jika anda ingin buffered aliran yang mendukung membaca beberapa kali anda perlu untuk mengatur
Idealnya lakukan ini di awal middleware sebelum apa-apa yang perlu dibaca dalam tubuh.
Jadi misalnya anda bisa letakkan kode berikut di awal
Configure
metode Startup.cs file:Sebelum mengaktifkan Mundur stream yang terkait dengan
Permintaan.Tubuh
maju-satunya aliran yang doesn't dukungan yang mencari atau membaca aliran yang kedua kalinya. Hal ini dilakukan untuk membuat konfigurasi default dari penanganan permintaan seperti yang ringan dan performant mungkin. Tapi setelah anda mengaktifkan mundur stream upgrade ke aliran yang mendukung mencari dan membaca beberapa kali. Anda dapat mengamati hal ini "meng-upgrade" dengan mengatur breakpoint sebelum dan setelah panggilan untukEnableRewind
dan mengamatiPermintaan.Tubuh
properties. Jadi misalnyaPermintaan.Tubuh.CanSeek
akan berubah daripalsu
untukbenar
.update: Mulai ASP.NET Inti 2.1
Permintaan.EnableBuffering()
tersedia yang upgradePermintaan.Tubuh
keFileBufferingReadStream
sepertiPermintaan.EnableRewind()
dan karenaPermintaan.EnableBuffering()
adalah di publik namespace daripada internal salah satu hal yang harus diutamakan di atas EnableRewind(). (Terima kasih untuk @ArjanEinbu untuk menunjuk keluar)Kemudian untuk membaca tubuh stream anda bisa misalnya melakukan hal ini:
Don't bungkus
StreamReader
penciptaan dalam menggunakan pernyataan atau meskipun itu akan menutup mendasari tubuh stream pada akhir menggunakan blok dan kode kemudian dalam siklus hidup tidak akan mampu membaca tubuh.Juga hanya untuk menjadi aman, itu mungkin ide yang baik untuk mengikuti instruksi di atas baris kode yang berbunyi tubuh isi dengan baris kode untuk reset tubuh's stream posisi kembali ke 0.
Dengan cara itu setiap kode kemudian dalam siklus hidup akan menemukan permintaan.Tubuh dalam keadaan seperti ini masih't pernah baca belum.
Diperbarui Jawaban
Maaf saya awalnya salah membaca pertanyaan anda. Konsep upgrade terkait aliran menjadi buffered stream masih berlaku. Namun anda harus melakukannya secara manual, saya'm tidak menyadari apapun yang dibangun di .Net fungsionalitas Inti yang memungkinkan anda membaca respon stream sekali tertulis dalam cara yang
EnableRewind()
memungkinkan seorang pengembang membaca permintaan stream setelah itu's telah baca.Anda "hacky" pendekatan ini mungkin benar-benar sesuai. Anda pada dasarnya mengkonversi aliran yang dapat't berusaha untuk salah satu yang bisa. Pada akhir hari
Respon.Tubuh
stream telah mendapatkan bertukar keluar dengan aliran yang buffered dan mendukung apapun. Berikut ini adalah mengambil middleware untuk melakukan itu, tetapi anda akan melihat itu's sangat mirip dengan pendekatan anda. Namun aku memilih untuk menggunakan akhirnya blok sebagai perlindungan tambahan untuk menempatkan aliran asli kembali padaRespon.Tubuh
dan saya menggunakanPosisi
milik stream daripadaMencari
metode sejak sintaks ini sedikit lebih sederhana tetapi efeknya tidak berbeda dari pendekatan anda.Apa yang anda jelaskan sebagai hack ini benar-benar disarankan pendekatan tentang bagaimana untuk mengelola respon sungai di custom middleware.
Karena pipa sifat middle ware desain di mana masing-masing tengah ware tidak menyadari sebelumnya atau berikutnya handler di dalam pipa. Tidak ada jaminan bahwa saat ini tengah ware akan menjadi salah satu yang menulis respon kecuali berpegang pada respon aliran itu diberikan sebelum lewat di sungai itu (saat ini tengah ware) kontrol. Desain ini terlihat di OWIN dan akhirnya dipanggang dalam asp.net-core.
Setelah anda mulai menulis untuk respon aliran ini mengirimkan body dan header (respon) untuk klien. Jika yang lain handler bawah pipa itu sebelum saat handler punya kesempatan maka itu tidak akan mampu menambahkan sesuatu untuk respon setelah itu telah sudah dikirim.
Yang lagi-lagi tidak dijamin untuk menjadi aktual respon stream jika sebelumnya middleware dalam pipa mengikuti strategi yang sama lewat aliran lain ke bawah garis.
Referensi ASP.NET Inti Middleware dasar-Dasar
Contoh dibangun di dasar middlewares dari aspnet/BasicMiddleware Github repo
ResponseCompressionMiddleware.cs
Anda dapat menggunakan middleware dalam permintaan pipa, dalam rangka untuk log permintaan dan tanggapan.
Namun peningkatan bahaya
kebocoran memori
, karena facth bahwa:dapat berakhir dengan Tumpukan Besar Objek (dalam kasus tubuh dari permintaan atau respon yang lebih besar dari 85.000 byte). Hal ini meningkatkan bahaya dari kebocoran memori dalam aplikasi anda. Dalam rangka untuk menghindari LOH, memori aliran dapat diganti dengan Didaur ulang Memori streaming menggunakan relevan perpustakaan.
Sebuah implementasi yang menggunakan Didaur ulang memori aliran:
NB. Bahaya LOH ini tidak sepenuhnya memberantas karena
textWriter.ToString()
di sisi lain anda dapat menggunakan login client library yang mendukung terstruktur logging (ie. Serilog) dan inject contoh yang dapat Didaur ulang Memori Streaming.