旧代码: 新代码: 旧方法: 新方法: public class TraceExtension : SoapExtension
{
Stream _oldStream;
Stream _newStream;
public override Stream ChainStream(Stream stream)
{
_oldStream = stream;
_newStream = new MemoryStream();
return _newStream;
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type WebServiceType)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
//TestRequest(message);
break;
case SoapMessageStage.AfterSerialize:
WriteRequest(message);
break;
case SoapMessageStage.BeforeDeserialize:
WriteResponse(message);
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalidstage");
}
}
public void WriteRequest(SoapMessage message)
{
try
{
_newStream.Position = 0;
TextReader reader = new StreamReader(_newStream);
string methodName = message.MethodInfo.ToString();
string reqLog = reader.ReadToEnd();
//@@ Async log
Task t = Task.Factory.StartNew(() =>
{
try
{
WebSvcLogger.Logger.Information(string.Format("API - \"{0}\" Request:\r\n{1}", methodName, reqLog));
}
catch
{
//Log Failed
}
});
}
catch
{
//Log Failed
}
finally
{
_newStream.Position = 0;
Copy(_newStream, _oldStream);
}
}
public void WriteResponse(SoapMessage message)
{
try
{
Copy(_oldStream, _newStream);
_newStream.Position = 0;
TextReader reader = new StreamReader(_newStream);
string methodName = message.MethodInfo.ReturnType.FullName;
string respLog = reader.ReadToEnd();
//Async run
Task t = Task.Factory.StartNew(() =>
{
WebSvcLogger.Logger.Information(string.Format("API - \"{0}\" Response:\r\n{1}", methodName, respLog));
});
}
catch
{
//Log Failed
}
finally
{
_newStream.Position = 0;
}
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
}
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
private int priority;
public override Type ExtensionType
{
get { return typeof(TraceExtension); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
}
public class HttpProtocol : IClientMessageInspector
{
private readonly string _serviceName;
public HttpProtocol(string serviceName)
{
_serviceName = serviceName;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
WriteResponse(buffer.CreateMessage().CreateBufferedCopy(Int32.MaxValue));
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
WriteRequest(buffer.CreateMessage().CreateBufferedCopy(Int32.MaxValue));
return null;
}
public Message WriteRequest(MessageBuffer buffer)
{
Message msg = buffer.CreateMessage();
StringBuilder sb = new StringBuilder();
sb.Append(msg.ToString());
string reqLog = sb.ToString();
//@@ Async log
Task t = Task.Factory.StartNew(() =>
{
try
{
WebSvcLogger.Logger.Information(string.Format("API - \"{0}\" Request:\r\n{1}", _serviceName, reqLog));
}
catch
{
//Log Failed
}
});
return buffer.CreateMessage();
}
public Message WriteResponse(MessageBuffer buffer)
{
Message msg = buffer.CreateMessage();
StringBuilder sb = new StringBuilder();
sb.Append(msg.ToString());
string respLog = sb.ToString();
//Async run
Task t = Task.Factory.StartNew(() =>
{
WebSvcLogger.Logger.Information(string.Format("API - \"{0}\" Response:\r\n{1}", _serviceName, respLog));
});
return buffer.CreateMessage();
}
private static Message Clone(Message message)
{
return message.CreateBufferedCopy(int.MaxValue).CreateMessage();
}
}
public class TraceMessageBehavior : IEndpointBehavior
{
private readonly string _serviceName;
public TraceMessageBehavior(string serviceName)
{
_serviceName = serviceName;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new HttpProtocol(_serviceName));
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{ }
}
证书问题
SearchServiceBinding client = new SearchServiceBinding();
client.Url = "http://localhost/";
//Set Basic Auth Authorization
client.Credentials = new NetworkCredential(config.Username, config.Password);
// Send the request
var resp = client.service(req);
return resp;
]]> var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var address = "http://localhost/";
ServicePortTypeClient client = new ServicePortTypeClient(binding, address);
client.Endpoint.EndpointBehaviors.Add(new TraceMessageBehavior("Search"));
//Set Basic Auth Authorization
client.ClientCredentials.UserName.UserName = config.Username;
client.ClientCredentials.UserName.Password = config.Password;
// Send the request
var resp = await client.serviceAsync(req);
return resp;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Linq;
using System.Collections.Generic;
namespace TestCase.EntityFramework
{
public class TestCaseDBConfig : DbConfiguration
{
private static string EFFoldPath = "";
public TestCaseDBConfig() : base()
{
SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
EFFoldPath = AppDomain.CurrentDomain.BaseDirectory + "App_Data\\EFCache";
if (!Directory.Exists(EFFoldPath))
Directory.CreateDirectory(EFFoldPath);
TestCaseDbModelStore cachedDbModelStore = new TestCaseDbModelStore(EFFoldPath);
IDbDependencyResolver dependencyResolver = new SingletonDependencyResolver<DbModelStore>(cachedDbModelStore);
AddDependencyResolver(dependencyResolver);
}
private class TestCaseDbModelStore : DefaultDbModelStore
{
public TestCaseDbModelStore(string location)
: base(location)
{ }
public override XDocument TryGetEdmx(Type contextType)
{
return LoadXml2(contextType, XDocument.Load);
}
private void GetConceptualAssociationNames(List<System.Data.Entity.Core.Metadata.Edm.AssociationType> lstAssociationTypes, List<string> lstNames)
{
int oldCount = lstNames.Distinct().ToList().Count;
foreach (var association in lstAssociationTypes)
{
if (association.AssociationEndMembers != null && association.AssociationEndMembers.Count > 0)
{
var isAssociaEsixt = association.AssociationEndMembers.Where(a => lstNames.Contains(a.GetEntityType().Name)).FirstOrDefault() != null;
if (isAssociaEsixt)
{
if (!lstNames.Contains(association.Name))
lstNames.Add(association.Name);
foreach (var endMember in association.AssociationEndMembers)
{
if (!lstNames.Contains(endMember.GetEntityType().Name))
lstNames.Add(endMember.GetEntityType().Name);
}
}
}
}
if (oldCount != lstNames.Distinct().Count())
{
GetConceptualAssociationNames(lstAssociationTypes, lstNames);
}
}
public override void Save(Type contextType, DbModel model)
{
if (contextType.GenericTypeArguments != null && contextType.GenericTypeArguments.Length > 0)
{
string name = contextType.GenericTypeArguments[0].Name;
List<string> storeModelNames = new List<string>();
storeModelNames.Add(name);
storeModelNames.Add("Entities");
#region StoreModel
List<string> storeModelAssociates = new List<string>();
if (model.StoreModel.AssociationTypes != null && model.StoreModel.AssociationTypes.Count() > 0)
{
GetConceptualAssociationNames(model.StoreModel.AssociationTypes.ToList(), storeModelNames);
var removedStoreAssociates = model.StoreModel.AssociationTypes.Where(a => !storeModelNames.Contains(a.Name)).ToList();
foreach (var removedAssociate in removedStoreAssociates)
{
var tmp = model.StoreModel.AssociationTypes.Where(a => a.Name == removedAssociate.Name).FirstOrDefault();
if (tmp != null)
{
model.StoreModel.RemoveItem(tmp);
}
}
}
storeModelNames = storeModelNames.Distinct().ToList();
var removedStoreModels = model.StoreModel.EntityTypes.Where(a => !storeModelNames.Contains(a.Name)).ToList();
foreach (var removedStoreModel in removedStoreModels)
{
var tmp = model.StoreModel.EntityTypes.Where(a => a.Name == removedStoreModel.Name).FirstOrDefault();
if (tmp != null)
{
model.StoreModel.RemoveItem(tmp);
}
}
var removeEntitySetBase = model.StoreModel.Container.BaseEntitySets.Where(a => !storeModelNames.Contains(a.ElementType.Name)).ToList();
foreach (var removedStoreModel in removeEntitySetBase)
{
var tmp = model.StoreModel.Container.BaseEntitySets.Where(a => a.Name == removedStoreModel.Name).FirstOrDefault();
if (tmp != null)
{
model.StoreModel.Container.RemoveEntitySetBase(tmp);
}
}
#endregion
#region ConceptualModel
List<string> conceptualAssociates = new List<string>();
if (model.ConceptualModel.AssociationTypes != null && model.ConceptualModel.AssociationTypes.Count() > 0)
{
GetConceptualAssociationNames(model.ConceptualModel.AssociationTypes.ToList(), storeModelNames);
var removedStoreAssociates = model.ConceptualModel.AssociationTypes.Where(a => !storeModelNames.Contains(a.Name)).ToList();
foreach (var removedAssociate in removedStoreAssociates)
{
var tmp = model.ConceptualModel.AssociationTypes.Where(a => a.Name == removedAssociate.Name).FirstOrDefault();
if (tmp != null)
{
model.ConceptualModel.RemoveItem(tmp);
}
}
}
storeModelNames = storeModelNames.Distinct().ToList();
var conceptualStoreModels = model.ConceptualModel.EntityTypes.Where(a => !storeModelNames.Contains(a.Name)).ToList();
foreach (var removedStoreModel in conceptualStoreModels)
{
var tmp = model.ConceptualModel.EntityTypes.Where(a => a.Name == removedStoreModel.Name).FirstOrDefault();
if (tmp != null)
{
model.ConceptualModel.RemoveItem(tmp);
}
}
var removeConcepEntitySetBase = model.ConceptualModel.Container.BaseEntitySets.Where(a => !storeModelNames.Contains(a.ElementType.Name)).ToList();
foreach (var removedStoreModel in removeConcepEntitySetBase)
{
var tmp = model.ConceptualModel.Container.BaseEntitySets.Where(a => a.Name == removedStoreModel.Name).FirstOrDefault();
if (tmp != null)
{
model.ConceptualModel.Container.RemoveEntitySetBase(tmp);
}
}
#endregion
#region Mapping
if (model.ConceptualToStoreMapping != null && model.ConceptualToStoreMapping.EntitySetMappings != null && model.ConceptualToStoreMapping.EntitySetMappings.Count() > 0)
{
List<System.Data.Entity.Core.Mapping.EntitySetMapping> lstEntitySetMappings = new List<System.Data.Entity.Core.Mapping.EntitySetMapping>();
bool isMappingExists = false;
foreach (var mapping in model.ConceptualToStoreMapping.EntitySetMappings)
{
isMappingExists = false;
if (mapping.EntityTypeMappings != null && mapping.EntityTypeMappings.Count > 0)
{
foreach (var subMapping in mapping.EntityTypeMappings)
{
if (subMapping.EntityTypes != null && subMapping.EntityTypes.Count > 0)
{
foreach (var ef in subMapping.EntityTypes)
{
if (!storeModelNames.Contains(ef.Name))
{
lstEntitySetMappings.Add(mapping);
isMappingExists = true;
break;
}
}
}
if (isMappingExists)
break;
}
}
}
lstEntitySetMappings = lstEntitySetMappings.Distinct().ToList();
if (lstEntitySetMappings.Count > 0)
{
foreach (var tmpMapping in lstEntitySetMappings)
{
var tmp = model.ConceptualToStoreMapping.EntitySetMappings.Where(a => a == tmpMapping).FirstOrDefault();
if (tmp != null)
{
model.ConceptualToStoreMapping.RemoveSetMapping(tmp);
}
}
}
}
#endregion
}
using (var writer = XmlWriter.Create(GetFilePath2(contextType), new System.Xml.XmlWriterSettings { Indent = true }))
{
EdmxWriter.WriteEdmx(model, writer);
}
}
public string GetFilePath2(Type contextType)
{
var filename = (contextType.GenericTypeArguments.Length > 0 ? contextType.GenericTypeArguments[0].Name : contextType.FullName) + ".edmx";
string path = Path.Combine(EFFoldPath, filename);
return path;
}
public override DbCompiledModel TryLoad(Type contextType)
{
string path = GetFilePath2(contextType);
if (File.Exists(path))
{
DateTime lastWriteTime = File.GetLastWriteTimeUtc(path);
DateTime lastWriteTimeDomainAssembly = File.GetLastWriteTimeUtc(contextType.Assembly.Location);
if (lastWriteTimeDomainAssembly > lastWriteTime)
{
File.Delete(path);
}
}
return LoadXml2(contextType, reader => {
var defaultSchema = GetDefaultSchema(contextType);
return EdmxReader.Read(reader, defaultSchema);
});
}
internal T LoadXml2<T>(Type contextType, Func<XmlReader, T> xmlReaderDelegate)
{
var filePath = GetFilePath2(contextType);
if (!File.Exists(filePath))
{
return default(T);
}
if (!FileIsValid2(contextType, filePath))
{
File.Delete(filePath);
return default(T);
}
using (var reader = XmlReader.Create(filePath))
{
return xmlReaderDelegate(reader);
}
}
public bool FileIsValid2(Type contextType, string filePath)
{
var contextCreated =
File.GetLastWriteTimeUtc(contextType.Assembly.Location);
var storeCreated = File.GetLastWriteTimeUtc(filePath);
return storeCreated >= contextCreated;
}
}
}
}
]]>
经过检查发现是“tempkey.rsa”文件的问题, service.AddDeveloperSigningCredential()会自动生成一个私钥文件,但是发布到IIS的时候由于域名、端口等原因,调用接口会重新生成key,造成登录生成的access_token在其它api不可用,解决方法也很简单:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Authority = "http://192.168.1.1"; // 这里要是具体访问的域名
options.Audience = "api";
options.RequireHttpsMetadata = false;
});
重新发布后删除根目录的 tempkey.rsa 文件,重启IIS即可。
]]>HomeController.cs
public async Task<IActionResult> Index()
{
string path = Request.Path.Value.Trim().ToString();
var r = await _nodeServices.InvokeAsync<string>(@"server.js", path).ConfigureAwait(false);
return Content(r, "text/html");
}
Server.js
const fs = require('fs');
const { createBundleRenderer } = require('vue-server-renderer')
const renderer = createBundleRenderer(require('./vue-ssr-server-bundle.json'), {
runInNewContext: false,
template: fs.readFileSync('./index.html', 'utf-8'),
clientManifest: require('./vue-ssr-client-manifest.json')
})
function renderToString(context) {
//var c = JSON.parse(context.replace(/\\"/g, ""));
//return JSON.stringify(context);
return new Promise((resolve, reject) => {
renderer.renderToString(context, (err, html) => {
resolve(html);
})
})
}
module.exports = async (callback, data) => {
const url = data.replace(/\\"/g, "");
var title = 'Home';
var d = {
title: title,
url: url,
meta: '<meta name="description" content="desc" />'
}
var k = await renderToString(d);
callback(null, k.toString());
}
代码其实很简单,但部署网站的时候还是遇到了一些问题,首先服务器上的.net core版本必须与vs发布所选的版本一致,否则会提示服务器500错误。
我在代码中使用了await,导致页面出现错误的时候不能及时返回,页面会显示超时,callback里面promise返回的结果添加了toString()的方法,是为了避免返回结果可能为 object object 不能由c#解析为string:“_nodeServices.InvokeAsync
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
我们常定义的model类一般都包含"ID"字段,类似:
class Foo {
public int Id { get; set; }
}
在Controller中我们的action也写作:
public FooController {
[HttpPost]
public ActionResult Edit(int id, Foo foo) {
}
}
一般情况下并不会导致什么问题,因为从数据库的内容也是根据当前ID取出的。
特殊情况下,比如用户的权限仅限于修改ID为1的内容(任何页面都会获取ID为1的内容),当用户手动修改URL链接中的ID为2的时候,Controller读到的ID为真实ID(1),但是在View中显示的ID值确实URL中的值(2),这会导致想要修改ID为1的内容,实际上却是修改到ID为2的内容。
总结:MVC中route的ID会覆盖model中的ID,导致页面部分数据显示错误
修改方法也很简单,不使用ID为参数即可
// route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{identifier}", // URL with parameters
new { controller = "Home", action = "Index", identifier = UrlParameter.Optional } // Parameter defaults
);
// controller
public FooController {
[HttpPost]
public ActionResult Edit(int identifier, Foo foo) {
}
}
]]>/opt/cpanel/ea-php70/root/usr/bin/php-config
]]>问题说的很明确,但是基本上找不到解决的办法,谷歌和百度都说用IIS Express即可,但是这根本就解决不了我们的问题,无奈只能从配置文件下手,请看下面:
1.打开项目的.csproj文件,查看是否有这段代码
<IISExpressAnonymousAuthentication>enabled</IISExpressAnonymousAuthentication>
<IISExpressWindowsAuthentication>disabled</IISExpressWindowsAuthentication>
<IISExpressUseClassicPipelineMode>true</IISExpressUseClassicPipelineMode>
如果有这段,恭喜你,找到解决方法了。首先,删除这段代码
2.打开项目.csproj.user(没有这个文件就直接跳过这步)文件,查看是否有跟上面一样或类似的代码,如果有,删掉它。
不想删的话,也可以改成这样
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
3.打开IISExpress配置文件,路径在 C:/Users/xx/Documents/IISExpress/config/applicationhost.config,这是一个xml文件
在节点<system.applicationHost>下找到<sites>,找到你的网站,查看节点application的属性“applicationPool”,错误的属性值是“Clr4ClassicAppPool”,我们要把它改成“Clr4IntegratedAppPool”
4.做完上面这些后,打开项目点击调试,项目终于正常打开了。
]]><?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<responseHeader xmlns:ns2="http://www.org/">
<numberOfHits>2</numberOfHits>
</responseHeader>
</soap:Header>
<soap:Body>
<getItem xmlns:ns2="http://www.org/" />
</soap:Body>
</soap:Envelope>
百度是个坑,查到的结果大部分都是关于请求部分,很少有返回部分的例子,谷歌也搜了半天,最后终于找到解决办法。
下面的代码为 VB,C#可参照转换
<System.Xml.Serialization.XmlNamespaceDeclarations> _
Public Property xmlns() As XmlSerializerNamespacesGet
Dim xsn = New XmlSerializerNamespaces()
xsn.Add("ns2", "http://www.org/")
Return xsn
End Get
Set(value As XmlSerializerNamespaces)End Set
End Property
把上面这段代码添加到需要“ns2”前缀的类里边去,比如我上面的responseHeader和getItem部分:
'''<remarks/>
<System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.17929"), _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://resolvingmo.ws.gs1.org/")> _
Partial Public Class getItemPrivate gepirItemField() As ItemDataLineType
<System.Xml.Serialization.XmlNamespaceDeclarations> _
Public Property xmlns() As XmlSerializerNamespacesGet
Dim xsn = New XmlSerializerNamespaces()
xsn.Add("ns2", "http://resolvingmo.ws.gs1.org/")
Return xsn
End Get
Set(value As XmlSerializerNamespaces)End Set
End Property
'''<remarks/>
<System.Xml.Serialization.XmlArrayAttribute(Form:=System.Xml.Schema.XmlSchemaForm.Unqualified), _
System.Xml.Serialization.XmlArrayItemAttribute("itemDataLine", Form:=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable:=False)> _
Public Property gepirItem() As ItemDataLineType()
Get
Return Me.gepirItemField
End Get
Set(value As ItemDataLineType())
Me.gepirItemField = value
End Set
End Property
End Class
最后生成的结果就行想要的了,xml添加了指定的前缀
]]><?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<ns2:responseHeader xmlns:ns2="http://www.org/">
<numberOfHits>2</numberOfHits>
</ns2:responseHeader>
</soap:Header>
<soap:Body>
<ns2:getItem xmlns:ns2="http://www.org/" />
</soap:Body>
</soap:Envelope>