当前位置: 首页 > 面试题库 >

HTTP状态代码为401时,IErrorHandler返回错误的消息正文

丌官玺
2023-03-14
问题内容

我已经实现了IErrorHandler来处理在我的宁静WCF服务的构造函数中引发的授权异常。当捕获到一般异常时,将按预期返回我的自定义类型,但是ContentType标头不正确。

HTTP/1.1 500 Internal Server Error
Content-Type: application/xml;
...

{"ErrorMessage":"Error!"}

但是,当错误处理程序尝试返回401未经授权的http状态代码时,消息正文将覆盖为默认类型,但ContentType标头应为原样。

HTTP/1.1 401 Unauthorized
Content-Type: application/json; 
...

{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"}

显然这里有问题,但我不确定是什么。

如何实现IErrorHandler,使其以正确的标头返回json中的自定义类型?

BaseDataResponseContract对象:

[Serializable]
[DataContract( Name = "BaseDataResponseContract" )]
public class BaseDataResponseContract
{
    [DataMember]
    public string ErrorMessage { get; set; }

} // end

这是我要返回的对象。应用程序中的所有其他对象都从该对象继承。当引发异常时,我们真正关心的就是http状态代码和错误消息。

IErrorHandler实现(为简便起见,未显示日志):

namespace WebServices.BehaviorsAndInspectors
{
    public class ErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            return true;

        } // end

        public void ProvideFault(Exception ex, MessageVersion version, ref Message fault)
        {
            // Create a new instance of the object I would like to return with a default message
            var baseDataResponseContract = new BaseDataResponseContract { ErrorMessage = "Error!" };

            // Get the outgoing response portion of the current context 
            var response = WebOperationContext.Current.OutgoingResponse;

            // Set the http status code 
            response.StatusCode = HttpStatusCode.InternalServerError;

            // If the exception is a specific type change the default settings
            if (ex.GetType() == typeof(UserNotFoundException))
            {
                 baseDataResponseContract.ErrorMessage = "Invalid Username!";
                 response.StatusCode = HttpStatusCode.Unauthorized;
            }

            // Create the fault message that is returned (note the ref parameter)
            fault = Message.CreateMessage(version, "", baseDataResponseContract, new DataContractJsonSerializer(typeof(BaseDataResponseContract)));

            // Tell WCF to use JSON encoding rather than default XML
            var webBodyFormatMessageProperty = new WebBodyFormatMessageProperty(WebContentFormat.Json);
            fault.Properties.Add(WebBodyFormatMessageProperty.Name, webBodyFormatMessageProperty);

            // Add ContentType header that specifies we are using json 
            var httpResponseMessageProperty = new HttpResponseMessageProperty();
            httpResponseMessageProperty.Headers[HttpResponseHeader.ContentType] = "application/json";
            fault.Properties.Add(HttpResponseMessageProperty.Name, httpResponseMessageProperty);

        } // end

    } // end class

} // end namespace

IServiceBehavior实现:

namespace WebServices.BehaviorsAndInspectors
{
    public class ErrorHandlerExtensionBehavior : BehaviorExtensionElement, IServiceBehavior
    {
        public override Type BehaviorType
        {
            get { return GetType(); }
        }

        protected override object CreateBehavior()
        {
            return this;
        }

        private IErrorHandler GetInstance()
        {
            return new ErrorHandler();
        }

        void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } // end

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var errorHandlerInstance = GetInstance();

            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(errorHandlerInstance);
            }
        }

        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } // end

    } // end class

} // end namespace

Web.Config:

<system.serviceModel>

    <services>      
      <service name="WebServices.MyService">
        <endpoint binding="webHttpBinding" contract="WebServices.IMyService" />
      </service>
    </services>

    <extensions>      
      <behaviorExtensions>        
        <!-- This extension if for the WCF Error Handling-->
        <add name="ErrorHandlerBehavior" type="WebServices.BehaviorsAndInspectors.ErrorHandlerExtensionBehavior, WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />      
      </behaviorExtensions>    
    </extensions>

    <behaviors>          
      <serviceBehaviors>        
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <ErrorHandlerBehavior />
        </behavior>     
      </serviceBehaviors>    
    </behaviors>

    ....
</system.serviceModel>

最后,使用WebFaultException时我看到类似的行为。我的想法是,这是一些深埋的.Net恶作剧的结果。我选择实现IErrorHandler,以便可以捕获可能无法处理的任何其他异常。

参考:

https://msdn.microsoft.com/zh-
CN/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.100).aspx

http://www.brainthud.com/cards/5218/25441/which-four-behavior-interfaces-
exist-for-interacting-with-a-service-or-client-description-what-methods-do-
they-实施和


问题答案:

经过一整天的努力,我发现这是由IIS设置引起的。

在IIS中的API项目下,在“身份验证”菜单下,将“表单身份验证”设置为“启用”。我关闭了此“功能”,上面的代码按预期开始工作。我发现这是由于团队中的另一个开发人员将代码放入web.config文件中,从而更改了IIS中的设置。特别:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    ...
    <system.web>
        <authentication mode="Forms" />
    </system.web>
    ...
</configuration>

此外,通过使用WebOperationContext OutgoingResponse对象上的ContentType属性,我能够使Content-
Type标头正确显示。

// Get the outgoing response portion of the current context
var response = WebOperationContext.Current.OutgoingResponse;

// Add ContentType header that specifies we are using JSON
response.ContentType = new MediaTypeHeaderValue("application/json").ToString();


 类似资料:
  • 像“addSomething()”这样的类也有方法。这个可以成功也可以不成功。因此,成功的状态可以用一个布尔返回值来显示。但是有时方法调用会因为几个原因而失败。“false”显示了这一点,但只是以一般方式显示。有时程序员想知道失败的原因。为此,提供一个自己的报告类来提供这样的功能有用吗? 然后,您可以决定是否要使用“wassucsuccess()”获取一般成功报告,或者是否还要使用“getMess

  • 我的服务使用WebApi。它有一个带有GET方法的控制器。当我从客户端调用它并返回未授权的状态代码时,我在HttpResponseMessage中没有得到未授权,而是没有找到。 这是我从客户端调用它的方式: < code >响应的内容: 我在 GetTest() 处放置了一个断点,它在那里停止,所以它到达了控制器方法。 为什么我在客户端中收到“未找到”,而不是“未经授权”?

  • 问题内容: 我在这我想从服务器获取与$ HTTP GET XML数据的AngularJS应用说http://example.com/a/b/c/d/getDetails?fname=abc&lname=def(此通过在浏览器中输入链接进行手动访问时显示XML文件的树状结构)。 当我运行应用程序时,不会从该链接获取数据。而是显示 状态为0 的错误。 我不确定$ http.get为什么会失败并转到返回

  • 使用一个自定义的Spring Security过滤器,如果HTTP头不包含特定的键值对,我想返回一个HTTP401错误代码。 示例: 据我所知,我可以做到以下几点: 或 (2)抛出一个异常,最终导致Spring Security向客户端抛出一个401错误。 如果#1是更好的选项,我如何在对响应调用之后跳过筛选器链? 或者,如果#2是正确的方法,我应该抛出哪个异常?

  • 在Spring MVC@RequestMapping注释中,我返回JSP页面名称作为结果。这将返回HTTP状态代码200OK。如何将此状态代码更改为创建的201? @ResponseStatus不起作用。另外,HttpServletResponse也不能工作,因为我只需要返回自定义JSP页面。

  • 问题内容: 我试图按照此链接中的建议将错误返回到对控制器的调用,以便客户端可以采取适当的措施。javascript通过jqueryAJAX调用控制器。仅在不将状态设置为error的情况下,我才可以重新获得Json对象。这是示例代码 如果没有设置状态码,我会得到Json。如果设置状态代码,则会返回状态代码,但不会返回Json错误对象。 更新 我想将Error对象作为JSON发送,以便可以处理ajax