spring ProblemDetail 源码

  • 2022-08-08
  • 浏览 (348)

spring ProblemDetail 代码

文件路径:/spring-web/src/main/java/org/springframework/http/ProblemDetail.java

/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.http;

import java.net.URI;
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Representation for an RFC 7807 problem detail. Includes spec-defined
 * properties, and a {@link #getProperties() properties} map for additional,
 * non-standard properties.
 *
 * <p>For an extended response, an application can add to the
 * {@link #getProperties() properties} map. When using the Jackson library, the
 * {@code properties} map is expanded as top level JSON properties through the
 * {@link org.springframework.http.converter.json.ProblemDetailJacksonMixin}.
 *
 * <p>For an extended response, an application can also create a subclass with
 * additional properties. Subclasses can use the protected copy constructor to
 * re-create an existing {@code ProblemDetail} instance as the subclass, e.g.
 * from an {@code @ControllerAdvice} such as
 * {@link org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler}.
 *
 * @author Rossen Stoyanchev
 * @since 6.0
 *
 * @see <a href="https://datatracker.ietf.org/doc/html/rfc7807">RFC 7807</a>
 * @see org.springframework.web.ErrorResponse
 * @see org.springframework.web.ErrorResponseException
 */
public class ProblemDetail {

	private static final URI BLANK_TYPE = URI.create("about:blank");


	private URI type = BLANK_TYPE;

	@Nullable
	private String title;

	private int status;

	@Nullable
	private String detail;

	@Nullable
	private URI instance;

	@Nullable
	private Map<String, Object> properties;


	/**
	 * Protected constructor for subclasses.
	 * <p>To create a {@link ProblemDetail} instance, use static factory methods,
	 * {@link #forStatus(HttpStatusCode)} or {@link #forStatus(int)}.
	 * @param rawStatusCode the response status to use
	 */
	protected ProblemDetail(int rawStatusCode) {
		this.status = rawStatusCode;
	}

	/**
	 * Copy constructor that a subclass can use to re-create and extend a
	 * {@code ProblemDetail} with additional properties.
	 */
	protected ProblemDetail(ProblemDetail other) {
		this.type = other.type;
		this.title = other.title;
		this.status = other.status;
		this.detail = other.detail;
		this.instance = other.instance;
		this.properties = (other.properties != null ? new LinkedHashMap<>(other.properties) : null);
	}

	/**
	 * No-arg constructor, for deserialization.
	 */
	protected ProblemDetail() {
	}


	/**
	 * Setter for the {@link #getType() problem type}.
	 * <p>By default, this is {@link #BLANK_TYPE}.
	 * @param type the problem type
	 */
	public void setType(URI type) {
		Assert.notNull(type, "'type' is required");
		this.type = type;
	}

	/**
	 * Setter for the {@link #getTitle() problem title}.
	 * <p>By default, if not explicitly set and the status is well-known, this
	 * is sourced from the {@link HttpStatus#getReasonPhrase()}.
	 * @param title the problem title
	 */
	public void setTitle(@Nullable String title) {
		this.title = title;
	}

	/**
	 * Setter for the {@link #getStatus() problem status}.
	 * @param httpStatus the problem status
	 */
	public void setStatus(HttpStatus httpStatus) {
		this.status = httpStatus.value();
	}

	/**
	 * Setter for the {@link #getStatus() problem status}.
	 * @param status the problem status
	 */
	public void setStatus(int status) {
		this.status = status;
	}

	/**
	 * Setter for the {@link #getDetail() problem detail}.
	 * <p>By default, this is not set.
	 * @param detail the problem detail
	 */
	public void setDetail(@Nullable String detail) {
		this.detail = detail;
	}

	/**
	 * Setter for the {@link #getInstance() problem instance}.
	 * <p>By default, when {@code ProblemDetail} is returned from an
	 * {@code @ExceptionHandler} method, this is initialized to the request path.
	 * @param instance the problem instance
	 */
	public void setInstance(@Nullable URI instance) {
		this.instance = instance;
	}

	/**
	 * Set a "dynamic" property to be added to a generic {@link #getProperties()
	 * properties map}.
	 * @param name the property name
	 * @param value the property value
	 */
	public void setProperty(String name, Object value) {
		this.properties = (this.properties != null ? this.properties : new LinkedHashMap<>());
		this.properties.put(name, value);
	}


	/**
	 * Return the configured {@link #setType(URI) problem type}.
	 */
	public URI getType() {
		return this.type;
	}

	/**
	 * Return the configured {@link #setTitle(String) problem title}.
	 */
	@Nullable
	public String getTitle() {
		if (this.title == null) {
			HttpStatus httpStatus = HttpStatus.resolve(this.status);
			if (httpStatus != null) {
				return httpStatus.getReasonPhrase();
			}
		}
		return this.title;
	}

	/**
	 * Return the status associated with the problem, provided either to the
	 * constructor or configured via {@link #setStatus(int)}.
	 */
	public int getStatus() {
		return this.status;
	}

	/**
	 * Return the configured {@link #setDetail(String) problem detail}.
	 */
	@Nullable
	public String getDetail() {
		return this.detail;
	}

	/**
	 * Return the configured {@link #setInstance(URI) problem instance}.
	 */
	@Nullable
	public URI getInstance() {
		return this.instance;
	}

	/**
	 * Return a generic map of properties that are not known ahead of time,
	 * possibly {@code null} if no properties have been added. To add a property,
	 * use {@link #setProperty(String, Object)}.
	 */
	@Nullable
	public Map<String, Object> getProperties() {
		return this.properties;
	}


	@Override
	public String toString() {
		return getClass().getSimpleName() + "[" + initToStringContent() + "]";
	}

	/**
	 * Return a String representation of the {@code ProblemDetail} fields.
	 * Subclasses can override this to append additional fields.
	 */
	protected String initToStringContent() {
		return "type='" + this.type + "'" +
				", title='" + getTitle() + "'" +
				", status=" + getStatus() +
				", detail='" + getDetail() + "'" +
				", instance='" + getInstance() + "'" +
				", properties='" + getProperties() + "'";
	}


	// Static factory methods

	/**
	 * Create a {@code ProblemDetail} instance with the given status code.
	 */
	public static ProblemDetail forStatus(HttpStatusCode status) {
		Assert.notNull(status, "HttpStatusCode is required");
		return forStatus(status.value());
	}

	/**
	 * Create a {@code ProblemDetail} instance with the given status value.
	 */
	public static ProblemDetail forStatus(int status) {
		return new ProblemDetail(status);
	}

	/**
	 * Create a {@code ProblemDetail} instance with the given status and detail.
	 */
	public static ProblemDetail forStatusAndDetail(HttpStatusCode status, String detail) {
		Assert.notNull(status, "HttpStatusCode is required");
		ProblemDetail problemDetail = forStatus(status.value());
		problemDetail.setDetail(detail);
		return problemDetail;
	}

}

相关信息

spring 源码目录

相关文章

spring CacheControl 源码

spring ContentDisposition 源码

spring DefaultHttpStatusCode 源码

spring HttpCookie 源码

spring HttpEntity 源码

spring HttpHeaders 源码

spring HttpInputMessage 源码

spring HttpLogging 源码

spring HttpMessage 源码

spring HttpMethod 源码

0  赞